@cocalc/project
Version:
CoCalc: project daemon
171 lines • 5.81 kB
JavaScript
"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