CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/database/postgres/retention/index.ts
Views: 687
1
import getPool from "../../pool";
2
import getLogger from "@cocalc/backend/logger";
3
import type { RetentionModel } from "@cocalc/util/db-schema";
4
import activeUsers from "./active-users";
5
import retainedUsers from "./retained-users";
6
7
const log = getLogger("database:retention");
8
9
type Period =
10
| { seconds: number }
11
| { hours: number }
12
| { days: number }
13
| { months: number }
14
| { years: number };
15
16
interface Options {
17
start: Date;
18
stop: Date;
19
model: RetentionModel;
20
period: Period;
21
}
22
23
export async function updateRetentionData({
24
start,
25
stop,
26
model,
27
period,
28
}: Options) {
29
if (start == null || stop == null || model == null || period == null) {
30
log.debug("some input is null so nothing to do");
31
// nothing to do
32
return;
33
}
34
if (typeof start == "object" && start["="]) {
35
start = start["="];
36
}
37
if (typeof stop == "object" && stop["="]) {
38
stop = stop["="];
39
}
40
if (typeof model == "object" && model["="]) {
41
model = model["="];
42
}
43
if (typeof period == "object" && period["="]) {
44
period = period["="];
45
}
46
const pool = getPool();
47
const current = await pool.query(
48
"SELECT last_start_time, NOW() - $4::interval - $4::interval AS required_last_start_time FROM crm_retention WHERE start=$1 AND stop=$2 AND model=$3 AND period=$4",
49
[start, stop, model, period]
50
);
51
log.debug(current);
52
53
if (
54
current.rows.length > 0 &&
55
current.rows[0].last_start_time >= current.rows[0].required_last_start_time
56
) {
57
log.debug("have the data, so nothing to do");
58
// nothing to do.
59
return;
60
}
61
log.debug("need to compute data", JSON.stringify(current.rows?.[0]));
62
63
// We do a check to make sure the interval is not too short to avoid a massive
64
// computation. This could easily happen, e.g., when playing around in the crm.
65
const { rows } = await pool.query(
66
"SELECT extract(epoch FROM $1::interval) AS seconds",
67
[period]
68
);
69
if (rows[0].seconds < 3600) {
70
throw Error("period must be at least one hour long");
71
// TODO: stronger constraint involving start?
72
}
73
const last_start_time = current.rows[0]?.last_start_time;
74
75
const [table, isActiveUsers] = model.split(":");
76
77
if (isActiveUsers) {
78
await activeUsers({
79
table,
80
model,
81
last_start_time,
82
pool,
83
start,
84
stop,
85
period,
86
});
87
} else {
88
await retainedUsers({
89
table,
90
model,
91
last_start_time,
92
pool,
93
start,
94
stop,
95
period,
96
});
97
}
98
}
99
100