|
| 1 | +// see packages/database/pool/pool.ts for where this name is also hard coded: |
| 2 | +process.env.PGDATABASE = "smc_ephemeral_testing_database"; |
| 3 | + |
1 | 4 | import { escapeIdentifier } from "pg";
|
2 | 5 |
|
3 | 6 | import getPool from "@cocalc/database/pool";
|
4 | 7 | import { SCHEMA } from "@cocalc/util/schema";
|
5 | 8 |
|
6 | 9 | interface Opts {
|
7 |
| - table: string; |
| 10 | + table: string; // e.g. project_log, etc. |
8 | 11 | field: "project_id" | "account_id"; // for now, we only support a few
|
| 12 | + id?: string; // default "id", the ID field in the table, which identifies each row uniquely |
9 | 13 | value: string; // a UUID
|
10 |
| - limit?: number; |
| 14 | + limit?: number; // default 1024 |
| 15 | + maxUtilPct?: number; // 0-100, percent |
11 | 16 | }
|
12 | 17 |
|
13 | 18 | type Ret = Promise<{
|
14 | 19 | rowsDeleted: number;
|
15 | 20 | durationS: number;
|
| 21 | + totalWaitS: number; |
| 22 | + totalPgTimeS: number; |
16 | 23 | }>;
|
17 | 24 |
|
18 |
| -function deleteQuery(table: string, field: string) { |
| 25 | +function deleteQuery(table: string, field: string, id: string) { |
19 | 26 | const T = escapeIdentifier(table);
|
20 | 27 | const F = escapeIdentifier(field);
|
| 28 | + const ID = escapeIdentifier(id); |
21 | 29 |
|
22 | 30 | return `
|
23 | 31 | DELETE FROM ${T}
|
24 |
| -WHERE ${F} IN ( |
25 |
| - SELECT ${F} FROM ${T} WHERE ${F} = $1 LIMIT $2 |
26 |
| -) |
27 |
| -RETURNING 1 |
28 |
| -`; |
| 32 | +WHERE ${ID} IN ( |
| 33 | + SELECT ${ID} FROM ${T} WHERE ${F} = $1 LIMIT $2 |
| 34 | +)`; |
29 | 35 | }
|
30 | 36 |
|
31 | 37 | export async function bulk_delete(opts: Opts): Ret {
|
32 |
| - const { table, field, value } = opts; |
33 |
| - let { limit = 1000 } = opts; |
| 38 | + const { table, field, value, id = "id", maxUtilPct = 10 } = opts; |
| 39 | + let { limit = 1024 } = opts; |
34 | 40 | // assert table name is a key in SCHEMA
|
35 | 41 | if (!(table in SCHEMA)) {
|
36 | 42 | throw new Error(`table ${table} does not exist`);
|
37 | 43 | }
|
38 | 44 |
|
39 |
| - const q = deleteQuery(table, field); |
40 |
| - console.log(q); |
41 |
| - console.log(opts); |
| 45 | + if (maxUtilPct < 1 || maxUtilPct > 99) { |
| 46 | + throw new Error(`maxUtilPct must be between 1 and 99`); |
| 47 | + } |
42 | 48 |
|
| 49 | + const q = deleteQuery(table, field, id); |
43 | 50 | const pool = getPool();
|
44 |
| - |
45 | 51 | const start_ts = Date.now();
|
46 |
| - let rowsDeleted = 0; |
47 | 52 |
|
| 53 | + let rowsDeleted = 0; |
| 54 | + let totalWaitS = 0; |
| 55 | + let totalPgTimeS = 0; |
48 | 56 | while (true) {
|
49 | 57 | const t0 = Date.now();
|
50 | 58 | const ret = await pool.query(q, [value, limit]);
|
51 |
| - const td = Date.now() - t0; |
| 59 | + const dt = (Date.now() - t0) / 1000; |
52 | 60 | rowsDeleted += ret.rowCount ?? 0;
|
| 61 | + totalPgTimeS += dt; |
53 | 62 |
|
54 |
| - // adjust the limit |
55 |
| - const next = Math.round( |
56 |
| - td > 0.1 ? limit / 2 : td < 0.05 ? limit * 2 : limit, |
57 |
| - ); |
58 |
| - limit = Math.max(1, Math.min(10000, next)); |
| 63 | + // adjust the limit: we aim to keep the operation between 0.1 and 0.2 secs |
| 64 | + const next = dt > 0.2 ? limit / 2 : dt < 0.1 ? limit * 2 : limit; |
| 65 | + limit = Math.max(1, Math.min(32768, Math.round(next))); |
59 | 66 |
|
60 | 67 | // wait for a bit, but not more than 1 second ~ this aims for a max utilization of 10%
|
61 |
| - const wait_ms = Math.min(1000, td * 10); |
62 |
| - await new Promise((done) => setTimeout(done, wait_ms)); |
| 68 | + const waitS = Math.min(1, dt * ((100 - maxUtilPct) / maxUtilPct)); |
| 69 | + await new Promise((done) => setTimeout(done, 1000 * waitS)); |
| 70 | + totalWaitS += waitS; |
63 | 71 |
|
64 |
| - console.log( |
65 |
| - `loop: deleted ${ret.rowCount} | wait=${wait_ms} | limit=${limit}`, |
66 |
| - ); |
| 72 | + // console.log( |
| 73 | + // `deleted ${ret.rowCount} | dt=${dt} | wait=${waitS} | limit=${limit}`, |
| 74 | + // ); |
67 | 75 |
|
68 | 76 | if (ret.rowCount === 0) break;
|
69 | 77 | }
|
70 | 78 |
|
71 | 79 | const durationS = (Date.now() - start_ts) / 1000;
|
72 |
| - return { durationS, rowsDeleted }; |
| 80 | + return { durationS, rowsDeleted, totalWaitS, totalPgTimeS }; |
73 | 81 | }
|
0 commit comments