Skip to content

Commit 3ba695a

Browse files
committed
database/bulk-delete: add env vars and improve test
1 parent d163c82 commit 3ba695a

File tree

2 files changed

+32
-19
lines changed

2 files changed

+32
-19
lines changed

src/packages/database/postgres/bulk-delete.test.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe("bulk delete", () => {
1919
test("deleting projects", async () => {
2020
const p = getPool();
2121
const project_id = uuid();
22-
const N = 2000;
22+
const N = 100000;
2323

2424
// extra entry, which has to remain
2525
const other = uuid();
@@ -28,12 +28,12 @@ describe("bulk delete", () => {
2828
[other, uuid(), new Date()],
2929
);
3030

31-
for (let i = 0; i < N; i++) {
32-
await p.query(
33-
"INSERT INTO project_log (id, project_id, time) VALUES($1::UUID, $2::UUID, $3::TIMESTAMP)",
34-
[uuid(), project_id, new Date()],
35-
);
36-
}
31+
await p.query(
32+
`INSERT INTO project_log (id, project_id, time)
33+
SELECT gen_random_uuid(), $1::UUID, NOW() - interval '1 second' * g.n
34+
FROM generate_series(1, $2) AS g(n)`,
35+
[project_id, N],
36+
);
3737

3838
const num1 = await p.query(
3939
"SELECT COUNT(*)::INT as num FROM project_log WHERE project_id = $1",
@@ -45,16 +45,15 @@ describe("bulk delete", () => {
4545
table: "project_log",
4646
field: "project_id",
4747
value: project_id,
48-
limit: 128,
4948
});
5049

5150
// if this ever fails, the "ret.rowCount" value is inaccurate.
5251
// This must be replaced by "RETURNING 1" in the the query and a "SELECT COUNT(*) ..." and so.
5352
// (and not only here, but everywhere in the code base)
5453
expect(res.rowsDeleted).toEqual(N);
55-
expect(res.durationS).toBeGreaterThan(0.01);
56-
expect(res.totalPgTimeS).toBeGreaterThan(0.001);
57-
expect(res.totalWaitS).toBeGreaterThan(0.001);
54+
expect(res.durationS).toBeGreaterThan(0.1);
55+
expect(res.totalPgTimeS).toBeGreaterThan(0.1);
56+
expect(res.totalWaitS).toBeGreaterThan(0.1);
5857
expect((res.totalPgTimeS * 10) / res.totalWaitS).toBeGreaterThan(0.5);
5958

6059
const num2 = await p.query(

src/packages/database/postgres/bulk-delete.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
import { escapeIdentifier } from "pg";
22

3+
import getLogger from "@cocalc/backend/logger";
4+
import { envToInt } from "@cocalc/backend/misc/env-to-number";
35
import getPool from "@cocalc/database/pool";
46
import { SCHEMA } from "@cocalc/util/schema";
57

8+
const log = getLogger("db:bulk-delete");
9+
const D = log.debug;
10+
611
type Field =
712
| "project_id"
813
| "account_id"
914
| "target_project_id"
1015
| "source_project_id";
1116

17+
const MAX_UTIL_PCT = envToInt("COCALC_DB_BULK_DELETE_MAX_UTIL_PCT", 10);
18+
// adjust the time limits: by default, we aim to keep the operation between 0.1 and 0.2 secs
19+
const MAX_TIME_TARGET_MS = envToInt(
20+
"COCALC_DB_BULK_DELETE_MAX_TIME_TARGET_MS",
21+
100,
22+
);
23+
const MAX_TARGET_S = MAX_TIME_TARGET_MS / 1000;
24+
const MIN_TARGET_S = MAX_TARGET_S / 2;
25+
const DEFAULT_LIMIT = envToInt("COCALC_DB_BULK_DELETE_DEFAULT_LIMIT", 16);
26+
const MAX_LIMIT = envToInt("COCALC_DB_BULK_DELETE_MAX_LIMIT", 32768);
27+
1228
interface Opts {
1329
table: string; // e.g. project_log, etc.
1430
field: Field; // for now, we only support a few
@@ -38,8 +54,8 @@ WHERE ${ID} IN (
3854
}
3955

4056
export async function bulkDelete(opts: Opts): Ret {
41-
const { table, field, value, id = "id", maxUtilPct = 10 } = opts;
42-
let { limit = 1024 } = opts;
57+
const { table, field, value, id = "id", maxUtilPct = MAX_UTIL_PCT } = opts;
58+
let { limit = DEFAULT_LIMIT } = opts;
4359
// assert table name is a key in SCHEMA
4460
if (!(table in SCHEMA)) {
4561
throw new Error(`table ${table} does not exist`);
@@ -63,18 +79,16 @@ export async function bulkDelete(opts: Opts): Ret {
6379
rowsDeleted += ret.rowCount ?? 0;
6480
totalPgTimeS += dt;
6581

66-
// adjust the limit: we aim to keep the operation between 0.1 and 0.2 secs
67-
const next = dt > 0.2 ? limit / 2 : dt < 0.1 ? limit * 2 : limit;
68-
limit = Math.max(1, Math.min(32768, Math.round(next)));
82+
const next =
83+
dt > MAX_TARGET_S ? limit / 2 : dt < MIN_TARGET_S ? limit * 2 : limit;
84+
limit = Math.max(1, Math.min(MAX_LIMIT, Math.round(next)));
6985

7086
// wait for a bit, but not more than 1 second ~ this aims for a max utilization of 10%
7187
const waitS = Math.min(1, dt * ((100 - maxUtilPct) / maxUtilPct));
7288
await new Promise((done) => setTimeout(done, 1000 * waitS));
7389
totalWaitS += waitS;
7490

75-
// console.log(
76-
// `deleted ${ret.rowCount} | dt=${dt} | wait=${waitS} | limit=${limit}`,
77-
// );
91+
D(`deleted ${ret.rowCount} | dt=${dt} | wait=${waitS} | limit=${limit}`);
7892

7993
if (ret.rowCount === 0) break;
8094
}

0 commit comments

Comments
 (0)