Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 63 additions & 25 deletions build/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -109555,48 +109555,86 @@ async function migrate(args) {
await backendTo.init();
} catch (error) {
exit(error);
throw error;
}
const numKeys2 = await esm_default("chelonia.db/keyCount");
let numMigratedKeys = 0;
let numVisitedKeys = 0;
let shouldExit = 0;
const handleSignal2 = (signal, code2) => {
process3.on(signal, () => {
console.error(`Received signal ${signal} (${code2}). Finishing current operation.`);
shouldExit = 128 + code2;
});
const reportStatus = () => {
console.log(`${green("Migrated:")} ${numMigratedKeys} entries`);
};
[
["SIGHUP", 1],
["SIGINT", 2],
["SIGQUIT", 3],
["SIGTERM", 15],
["SIGUSR1", 10],
["SIGUSR2", 11]
].forEach(([signal, code2]) => handleSignal2(signal, code2));
const checkAndExit = (() => {
let interruptCount = 0;
let shouldExit = 0;
const handleSignal2 = (signal, code2) => {
process3.on(signal, () => {
shouldExit = 128 + code2;
if (++interruptCount < 3) {
console.error(`Received signal ${signal} (${code2}). Finishing current operation.`);
} else {
console.error(`Received signal ${signal} (${code2}). Force quitting.`);
reportStatus();
exit(shouldExit);
}
});
};
const checkAndExit2 = async () => {
if (shouldExit) {
await backendTo.close();
reportStatus();
exit(shouldExit);
}
};
[
["SIGHUP", 1],
["SIGINT", 2],
["SIGQUIT", 3],
["SIGTERM", 15],
["SIGUSR1", 10],
["SIGUSR2", 11]
].forEach(([signal, code2]) => handleSignal2(signal, code2));
return checkAndExit2;
})();
let lastReportedPercentage = 0;
for await (const key of esm_default("chelonia.db/iterKeys")) {
if (!isValidKey(key)) continue;
const value = await esm_default("chelonia.db/get", key);
if (shouldExit) {
exit(shouldExit);
numVisitedKeys++;
if (!isValidKey(key)) {
console.debug("Skipping invalid key", key);
continue;
}
let value;
try {
value = await esm_default("chelonia.db/get", `any:${key}`);
} catch (e2) {
reportStatus();
console.error(`Error reading from source database key '${key}'`, e2);
await backendTo.close();
exit(1);
throw e2;
}
await checkAndExit();
if (value === void 0) {
console.debug("[chel] Skipping empty key", key);
console.debug("Skipping empty key", key);
continue;
}
await backendTo.writeData(key, value);
if (shouldExit) {
try {
await backendTo.writeData(key, value);
} catch (e2) {
reportStatus();
console.error(`Error writing to target database key '${key}'`, e2);
await backendTo.close();
exit(shouldExit);
exit(1);
throw e2;
}
++numVisitedKeys;
await checkAndExit();
++numMigratedKeys;
const percentage = Math.floor(numVisitedKeys / numKeys2 * 100);
if (percentage - lastReportedPercentage >= 10) {
lastReportedPercentage = percentage;
console.log(`[chel] Migrating... ${percentage}% done`);
console.log(`Migrating... ${percentage}% done`);
}
}
numVisitedKeys && console.log(`[chel] ${green("Migrated:")} ${numVisitedKeys} entries`);
reportStatus();
await backendTo.close();
}
var module9 = {
Expand Down
105 changes: 75 additions & 30 deletions src/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// chel migrate --to <backend>

import * as colors from 'jsr:@std/fmt/colors'
import type { Buffer } from 'node:buffer'
import { readFile } from 'node:fs/promises'
import process from 'node:process'
import sbp from 'npm:@sbp/sbp'
Expand Down Expand Up @@ -39,58 +40,102 @@ export async function migrate (args: ArgumentsCamelCase<Params>): Promise<void>
await backendTo.init()
} catch (error) {
exit(error)
throw error
}

const numKeys = await sbp('chelonia.db/keyCount')
let numMigratedKeys = 0
let numVisitedKeys = 0
let shouldExit = 0

const handleSignal = (signal: string, code: number) => {
process.on(signal, () => {
console.error(`Received signal ${signal} (${code}). Finishing current operation.`)
// Exit codes follow the 128 + signal code convention.
// See <https://tldp.org/LDP/abs/html/exitcodes.html>
shouldExit = 128 + code
})

const reportStatus = () => {
console.log(`${colors.green('Migrated:')} ${numMigratedKeys} entries`)
}

// Codes from <signal.h>
;([
['SIGHUP', 1],
['SIGINT', 2],
['SIGQUIT', 3],
['SIGTERM', 15],
['SIGUSR1', 10],
['SIGUSR2', 11]
] as [string, number][]).forEach(([signal, code]) => handleSignal(signal, code))
const checkAndExit = (() => {
let interruptCount = 0
let shouldExit = 0

const handleSignal = (signal: string, code: number) => {
process.on(signal, () => {
// Exit codes follow the 128 + signal code convention.
// See <https://tldp.org/LDP/abs/html/exitcodes.html>
shouldExit = 128 + code

if (++interruptCount < 3) {
console.error(`Received signal ${signal} (${code}). Finishing current operation.`)
} else {
console.error(`Received signal ${signal} (${code}). Force quitting.`)
reportStatus()
exit(shouldExit)
}
})
}

const checkAndExit = async () => {
if (shouldExit) {
await backendTo.close()
reportStatus()
exit(shouldExit)
}
}

// Codes from <signal.h>
;([
['SIGHUP', 1],
['SIGINT', 2],
['SIGQUIT', 3],
['SIGTERM', 15],
['SIGUSR1', 10],
['SIGUSR2', 11]
] as [string, number][]).forEach(([signal, code]) => handleSignal(signal, code))

return checkAndExit
})()

let lastReportedPercentage = 0
for await (const key of sbp('chelonia.db/iterKeys')) {
if (!isValidKey(key)) continue
const value = await sbp('chelonia.db/get', key)
if (shouldExit) {
exit(shouldExit)
numVisitedKeys++
if (!isValidKey(key)) {
console.debug('Skipping invalid key', key)
continue
}
// `any:` prefix needed to get the raw value, else the default is getting
// a string, which will be encoded as UTF-8. This can cause data loss.
let value: Buffer | string | undefined
try {
value = await sbp('chelonia.db/get', `any:${key}`)
} catch (e) {
reportStatus()
console.error(`Error reading from source database key '${key}'`, e)
await backendTo.close()
exit(1)
throw e
}
await checkAndExit()
// Make `deno check` happy.
if (value === undefined) {
console.debug('[chel] Skipping empty key', key)
// ++numVisitedKeys
console.debug('Skipping empty key', key)
continue
}
await backendTo.writeData(key, value)
if (shouldExit) {
try {
await backendTo.writeData(key, value)
} catch (e) {
reportStatus()
console.error(`Error writing to target database key '${key}'`, e)
await backendTo.close()
exit(shouldExit)
exit(1)
throw e
}
++numVisitedKeys
await checkAndExit()
++numMigratedKeys
// Prints a message roughly every 10% of progress.
const percentage = Math.floor((numVisitedKeys / numKeys) * 100)
if (percentage - lastReportedPercentage >= 10) {
lastReportedPercentage = percentage
console.log(`[chel] Migrating... ${percentage}% done`)
console.log(`Migrating... ${percentage}% done`)
}
}
numVisitedKeys && console.log(`[chel] ${colors.green('Migrated:')} ${numVisitedKeys} entries`)
reportStatus()
await backendTo.close()
}

Expand Down