UNPKG

@cocalc/project

Version:
171 lines 5.81 kB
"use strict"; /* * This file is part of CoCalc: Copyright © 2020 Sagemath, Inc. * License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.get_usage_info_table = exports.register_usage_info_table = void 0; // usage info for a specific file path, derived from the more general project info, // which includes all processes and other stats const debug_1 = __importDefault(require("debug")); const L = (0, debug_1.default)("project:sync:usage-info"); const async_utils_1 = require("@cocalc/util/async-utils"); const misc_1 = require("@cocalc/util/misc"); const usage_info_1 = require("../usage-info"); class UsageInfoTable { constructor(table, project_id) { this.servers = {}; this.project_id = project_id; this.log = L.extend("table"); this.table = table; this.setup_watchers(); } close() { this.log("close"); for (const path in this.servers) { this.stop_server(path); } (0, misc_1.close)(this); } // Start watching any paths that have recent interest (so this is not // in response to a *change* after starting). async setup_watchers() { if (this.table == null) return; // closed if (this.table.get_state() == "init") { await (0, async_utils_1.once)(this.table, "state"); } if (this.table.get_state() != "connected") { return; // game over } this.table.get()?.forEach((val) => { const path = val.get("path"); if (path == null) return; if (this.servers[path] == null) return; // already watching }); this.log("setting up 'on.change'"); this.table.on("change", this.handle_change_event.bind(this)); } async remove_stale_servers() { if (this.table == null) return; // closed if (this.table.get_state() != "connected") return; const paths = []; this.table.get()?.forEach((val) => { const path = val.get("path"); if (path == null) return; paths.push(path); }); for (const path of Object.keys(this.servers)) { if (!paths.includes(path)) { this.stop_server(path); } } } is_ready() { return !!this.table?.is_ready(); } get_table() { if (!this.is_ready() || this.table == null) { throw Error("table not ready"); } return this.table; } async set(obj) { this.get_table().set((0, misc_1.merge)({ project_id: this.project_id }, obj), "shallow"); await this.get_table().save(); } get(path) { const x = this.get_table().get(JSON.stringify([this.project_id, path])); if (x == null) return x; return x; // NOTE: That we have to use JSON.stringify above is an ugly shortcoming // of the get method in @cocalc/sync/table/synctable.ts // that could probably be relatively easily fixed. } handle_change_event(keys) { // this.log("handle_change_event", JSON.stringify(keys)); for (const key of keys) { this.handle_change(JSON.parse(key)[1]); } this.remove_stale_servers(); } handle_change(path) { this.log("handle_change", path); const cur = this.get(path); if (cur == null) return; // Make sure we watch this path for updates, since there is genuine current interest. this.ensure_watching(path); this.set({ path }); } ensure_watching(path) { if (this.servers[path] != null) { // We are already watching this path, so nothing more to do. return; } try { this.start_watching(path); } catch (err) { this.log("failed to start watching", err); } } start_watching(path) { this.log(`start_watching ${path}`); if (this.servers[path] != null) return; const server = new usage_info_1.UsageInfoServer(path); server.on("usage", (usage) => { // this.log(`watching/usage:`, usage); try { if (!this.is_ready()) return; this.set({ path, usage }); } catch (err) { this.log(`compute_listing("${path}") error: "${err}"`); } }); server.start(); this.servers[path] = server; } stop_server(path) { const s = this.servers[path]; if (s == null) return; delete this.servers[path]; s.stop(); this.remove_path(path); } async remove_path(path) { if (!this.is_ready()) return; this.log("remove_path", path); await this.get_table().delete({ project_id: this.project_id, path }); } } let usage_info_table = undefined; function register_usage_info_table(table, project_id) { L("register_usage_info_table"); if (usage_info_table != null) { // There was one sitting around wasting space so clean it up // before making a new one. usage_info_table.close(); } usage_info_table = new UsageInfoTable(table, project_id); } exports.register_usage_info_table = register_usage_info_table; function get_usage_info_table() { return usage_info_table; } exports.get_usage_info_table = get_usage_info_table; //# sourceMappingURL=usage-info.js.map