|
| 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