@cocalc/server
Version:
CoCalc server functionality: functions used by either the hub and the next.js server
118 lines (110 loc) • 2.94 kB
text/typescript
/*
Handle a project query (or query cancel) message from a project.
*/
import { db } from "@cocalc/database";
import { callback2 } from "@cocalc/util/async-utils";
import { error } from "@cocalc/util/message";
import getLogger from "@cocalc/backend/logger";
const logger = getLogger("project-connection:handle-query");
interface Options {
project_id: string;
mesg;
sendResponse: (any) => void;
}
export default function handleQuery(opts: Options) {
switch (opts.mesg.event) {
case "query":
query(opts);
return;
case "query_cancel":
cancel(opts);
return;
default:
throw Error(`unknown event ${opts.mesg.event}`);
}
}
const changefeeds: { [project_id: string]: Set<string> } = {};
function query({ project_id, mesg, sendResponse }: Options) {
logger.debug("query", project_id);
const { id, changes, options, query } = mesg;
if (!query) {
throw Error("query must be defined");
}
let first = true; // relevant if changes is true
if (changes) {
if (changefeeds[project_id] === undefined) {
changefeeds[project_id] = new Set<string>([id]);
} else {
changefeeds[project_id].add(id);
}
}
const database = db();
database.user_query({
// use callback rather than async/await here, due to changefeed
project_id,
query,
options,
changes: changes ? id : undefined,
cb: (err, result) => {
if (result?.action == "close") {
err = "close";
}
if (err) {
if (err != "close") {
logger.debug("query: err=", err);
}
if (changefeeds[project_id]?.has(id)) {
changefeeds[project_id]?.delete(id);
}
sendResponse(error({ error: `${err}` }));
if (changes && !first) {
database.user_query_cancel_changefeed({ id });
}
} else {
let resp;
if (changes && !first) {
resp = result;
resp.id = id;
resp.multi_response = true;
} else {
first = false;
resp = { ...mesg };
resp.query = result;
}
sendResponse(resp);
}
},
});
}
async function cancel({
project_id,
mesg,
sendResponse,
}: Options): Promise<void> {
const c = changefeeds[project_id];
if (!c?.has(mesg.id)) {
// no such changefeed -- nothing to do
sendResponse(mesg);
return;
}
const database = db();
const resp = await callback2(database.user_query_cancel_changefeed, {
id: mesg.id,
});
mesg.resp = resp;
sendResponse(mesg);
c.delete(mesg.id);
}
export async function cancelAll(project_id: string): Promise<void> {
const database = db();
const c = changefeeds[project_id];
if (!c) return;
for (const id of c) {
try {
await callback2(database.user_query_cancel_changefeed, { id });
c.delete(id);
} catch (err) {
logger.debug("WARNING: error cancelling changefeed", id, err);
}
}
}