smc-hub
Version:
CoCalc: Backend webserver component
153 lines • 9.8 kB
JavaScript
;
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.projects_that_used_license = exports.number_of_hours_projects_used_license = exports.number_of_projects_that_used_license = exports.update_site_license_usage_log = void 0;
var query_1 = require("../query");
var const_1 = require("./const");
function update_site_license_usage_log(db) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// don't run this in parallel – timeout_s triggers a transaction and as of now, we have only one client<->db connection
return [4 /*yield*/, update_site_license_usage_log_running_projects(db)];
case 1:
// don't run this in parallel – timeout_s triggers a transaction and as of now, we have only one client<->db connection
_a.sent();
return [4 /*yield*/, update_site_license_usage_log_not_running_projects(db)];
case 2:
_a.sent();
return [2 /*return*/];
}
});
});
}
exports.update_site_license_usage_log = update_site_license_usage_log;
/*
This function ensures that for every running project P using a site license L,
there is exactly one entry (P,L,time,null) in the table site_license_usage_log.
*/
function update_site_license_usage_log_running_projects(db) {
return __awaiter(this, void 0, void 0, function () {
var dbg, q;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
dbg = db._dbg("update_site_license_usage_log_running_projects");
dbg();
q = "\nWITH missing AS\n(\n WITH running_license_info AS\n (\n SELECT\n project_id,\n (\n jsonb_each_text(site_license)\n )\n .*\n FROM\n projects\n WHERE\n state #>> '{state}' = 'running'\n )\n SELECT\n running_license_info.project_id AS project_id,\n running_license_info.key::UUID AS license_id\n FROM\n running_license_info\n WHERE\n running_license_info.value != '{}'\n AND NOT EXISTS\n (\n SELECT\n FROM\n site_license_usage_log\n WHERE\n site_license_usage_log.stop IS NULL\n AND site_license_usage_log.project_id = running_license_info.project_id\n AND site_license_usage_log.license_id = running_license_info.key::UUID\n )\n)\nINSERT INTO\n site_license_usage_log(project_id, license_id, start)\n SELECT\n project_id,\n license_id,\n NOW()\n FROM\n missing;\n\n";
return [4 /*yield*/, query_1.query({ db: db, query: q, timeout_s: const_1.TIMEOUT_S })];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
/*
This function ensures that there are no entries of the form
(P,L,time,null) in the site_license_usage_log table with
the project P NOT running. It does this by replacing the null
value in all such cases by NOW().
*/
function update_site_license_usage_log_not_running_projects(db) {
return __awaiter(this, void 0, void 0, function () {
var dbg, q;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
dbg = db._dbg("update_site_license_usage_log_not_running_projects");
dbg();
q = "\nWITH stopped AS\n(\n WITH running_license_info AS\n (\n SELECT\n project_id,\n (\n jsonb_each_text(site_license)\n )\n .*\n FROM\n projects\n WHERE\n state #>> '{state}' = 'running'\n )\n SELECT\n site_license_usage_log.license_id AS license_id,\n site_license_usage_log.project_id AS project_id,\n site_license_usage_log.start AS start\n FROM\n site_license_usage_log\n WHERE\n stop IS NULL\n AND NOT EXISTS\n (\n SELECT\n FROM\n running_license_info\n WHERE\n running_license_info.value != '{}'\n AND running_license_info.project_id = site_license_usage_log.project_id\n AND site_license_usage_log.license_id = running_license_info.key::UUID\n )\n)\nUPDATE\n site_license_usage_log\nSET\n stop = NOW()\nFROM\n stopped\nWHERE\n site_license_usage_log.license_id = stopped.license_id\n AND site_license_usage_log.project_id = stopped.project_id\n AND site_license_usage_log.start = stopped.start;\n";
return [4 /*yield*/, query_1.query({ db: db, query: q, timeout_s: const_1.TIMEOUT_S })];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
}
// Return the number of distinct projects that used the license during the given
// interval of time.
function number_of_projects_that_used_license(db, license_id, interval) {
return __awaiter(this, void 0, void 0, function () {
var dbg;
return __generator(this, function (_a) {
dbg = db._dbg("number_of_projects_that_used_license(\"" + license_id + "\"," + interval.begin + "," + interval.end + ")");
dbg();
return [2 /*return*/, -1];
});
});
}
exports.number_of_projects_that_used_license = number_of_projects_that_used_license;
// Return the total number of hours of usage of the given license by projects during
// the given interval of time.
function number_of_hours_projects_used_license(db, license_id, interval) {
return __awaiter(this, void 0, void 0, function () {
var dbg;
return __generator(this, function (_a) {
dbg = db._dbg("number_of_hours_projects_used_license(\"" + license_id + "\"," + interval.begin + "," + interval.end + ")");
dbg();
return [2 /*return*/, -1];
});
});
}
exports.number_of_hours_projects_used_license = number_of_hours_projects_used_license;
// Given a license_id and an interval of time [begin, end], returns
// all projects that used the license during an interval that overlaps with [begin, end].
// Projects are returned as a list of objects:
// {project_id, [any other fields from the projects table (e.g., title)]}
function projects_that_used_license(db, license_id, interval, fields, limit // at most this many results; results are ordered by project_id.
) {
if (fields === void 0) { fields = ["project_id"]; }
if (limit === void 0) { limit = 500; }
return __awaiter(this, void 0, void 0, function () {
var dbg;
return __generator(this, function (_a) {
dbg = db._dbg("projects_that_used_license(\"" + license_id + "\"," + interval.begin + "," + interval.end + ")");
dbg([fields, limit]);
return [2 /*return*/, []];
});
});
}
exports.projects_that_used_license = projects_that_used_license;
//# sourceMappingURL=usage-log.js.map