Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/hub/migrate-bookmarks.ts
1709 views
1
/*
2
* This file is part of CoCalc: Copyright © 2024 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { delay } from "awaiting";
7
8
import { conat } from "@cocalc/backend/conat/conat";
9
import { dkv } from "@cocalc/backend/conat/sync";
10
import {
11
CONAT_BOOKMARKS_KEY,
12
STARRED_FILES,
13
} from "@cocalc/util/consts/bookmarks";
14
import { getLogger } from "./logger";
15
import getPool from "@cocalc/database/pool";
16
17
const L = getLogger("hub:migrate-bookmarks");
18
19
const BATCH_SIZE = 100;
20
const MIGRATION_DELAY = 500; // ms between batches to avoid database saturation
21
22
export async function migrateBookmarksToConat(): Promise<void> {
23
L.info("Starting migration of bookmarks to conat...");
24
25
const pool = getPool();
26
let totalMigrated = 0;
27
let batchCount = 0;
28
29
while (true) {
30
try {
31
// Query for a batch of bookmark entries
32
const { rows } = await pool.query(
33
`
34
SELECT id, account_id, project_id, stars
35
FROM bookmarks
36
WHERE type = $1
37
ORDER BY id
38
LIMIT $2
39
`,
40
[STARRED_FILES, BATCH_SIZE],
41
);
42
43
if (rows.length === 0) {
44
L.info(
45
`Migration completed. Total migrated: ${totalMigrated} bookmarks`,
46
);
47
break;
48
}
49
50
batchCount++;
51
L.info(`Processing batch ${batchCount} with ${rows.length} bookmarks...`);
52
53
// Process each bookmark in the batch
54
const processedIds: string[] = [];
55
56
for (const row of rows) {
57
try {
58
const { id, account_id, project_id, stars } = row;
59
60
if (!account_id || !project_id || !Array.isArray(stars)) {
61
L.warn(
62
`Skipping invalid bookmark ${id}: account_id=${account_id}, project_id=${project_id}, stars=${stars}`,
63
);
64
processedIds.push(id);
65
continue;
66
}
67
68
// Get or create conat DKV for this account
69
const bookmarks = await dkv<string[]>({
70
name: CONAT_BOOKMARKS_KEY,
71
account_id,
72
client: conat(),
73
});
74
75
// Set the starred files for this project
76
bookmarks.set(project_id, stars);
77
L.debug(
78
`Migrated bookmark ${id} for account ${account_id}, project ${project_id} with ${stars.length} stars`,
79
);
80
81
processedIds.push(id);
82
totalMigrated++;
83
} catch (err) {
84
L.error(`Failed to migrate bookmark ${row.id}: ${err}`);
85
// Still add to processedIds so we don't get stuck on this one
86
processedIds.push(row.id);
87
}
88
}
89
90
// Delete the processed bookmarks from the database
91
if (processedIds.length > 0) {
92
await pool.query(`DELETE FROM bookmarks WHERE id = ANY($1)`, [
93
processedIds,
94
]);
95
L.debug(`Deleted ${processedIds.length} bookmarks from database`);
96
}
97
98
// Wait between batches to avoid database saturation
99
if (rows.length === BATCH_SIZE) {
100
L.debug(`Waiting ${MIGRATION_DELAY}ms before next batch...`);
101
await delay(MIGRATION_DELAY);
102
}
103
} catch (err) {
104
L.error(`Error in migration batch: ${err}`);
105
// Wait longer on error before retrying
106
await delay(MIGRATION_DELAY * 3);
107
}
108
}
109
110
L.info("Bookmark migration to conat completed successfully");
111
}
112
113