Queue native collection launches before async placement
This commit is contained in:
@@ -15,7 +15,19 @@ export function nativeInstance(server: string): NativeInstance | null {
|
||||
}
|
||||
|
||||
export function nativeDeployment(id: string): boolean {
|
||||
return !!db().prepare('SELECT 1 FROM native_provisioning WHERE deployment_id = ?').get(id);
|
||||
return !!db().prepare(`SELECT 1 FROM native_provisioning WHERE deployment_id = ?
|
||||
UNION ALL SELECT 1 FROM native_creation_intents WHERE deployment_id = ?
|
||||
UNION ALL SELECT 1 FROM native_creation_cancellations c
|
||||
JOIN native_creation_intents i ON i.deployment_id=c.creation_id WHERE c.cleanup_id = ? LIMIT 1`).get(id, id, id);
|
||||
}
|
||||
|
||||
export function unplacedNativeCreation(server: string): string | null {
|
||||
const row = db().prepare(`SELECT d.id FROM server_deployments d
|
||||
JOIN native_creation_intents i ON i.deployment_id=d.id
|
||||
LEFT JOIN native_provisioning n ON n.deployment_id=d.id
|
||||
WHERE d.server=? AND d.server_deleted_at IS NULL AND d.action='create' AND d.state='queued'
|
||||
AND n.deployment_id IS NULL ORDER BY d.rowid DESC LIMIT 1`).get(server) as { id: string } | undefined;
|
||||
return row?.id ?? null;
|
||||
}
|
||||
|
||||
const observations = new Map<string, Promise<unknown>>();
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import type Database from 'better-sqlite3';
|
||||
|
||||
export function migrateNativeInstances(handle: Database.Database): void {
|
||||
migrateUnplacedCreationPort(handle);
|
||||
handle.exec(`
|
||||
CREATE TABLE IF NOT EXISTS native_creation_intents (
|
||||
deployment_id TEXT PRIMARY KEY REFERENCES server_deployments(id) ON DELETE CASCADE,
|
||||
selection TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS native_instances (
|
||||
id TEXT PRIMARY KEY, server TEXT NOT NULL, host_id TEXT NOT NULL,
|
||||
generation INTEGER NOT NULL DEFAULT 1, library_id TEXT NOT NULL,
|
||||
@@ -24,3 +29,27 @@ export function migrateNativeInstances(handle: Database.Database): void {
|
||||
);
|
||||
`);
|
||||
}
|
||||
|
||||
/** Accepted native creations receive their host port only when provisioning places them. */
|
||||
function migrateUnplacedCreationPort(handle: Database.Database): void {
|
||||
const columns = handle.prepare('PRAGMA table_info(server_creation_jobs)').all() as { name: string; notnull: number }[];
|
||||
if (!columns.find(column => column.name === 'port')?.notnull) return;
|
||||
const { sql } = handle.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='server_creation_jobs'").get() as { sql: string };
|
||||
const replacement = sql.replace(/\bport\s+INTEGER\s+NOT\s+NULL\b/i, 'port INTEGER');
|
||||
if (replacement === sql) throw new Error('Could not migrate unplaced creation ports.');
|
||||
const dependent = handle.prepare("SELECT sql FROM sqlite_master WHERE tbl_name='server_creation_jobs' AND type IN ('index','trigger') AND sql IS NOT NULL")
|
||||
.all() as { sql: string }[];
|
||||
const foreignKeys = handle.pragma('foreign_keys', { simple: true });
|
||||
handle.pragma('foreign_keys = OFF');
|
||||
try {
|
||||
handle.transaction(() => {
|
||||
handle.exec(replacement.replace('server_creation_jobs', 'server_creation_jobs_unplaced'));
|
||||
const names = columns.map(column => `"${column.name.replaceAll('"', '""')}"`).join(', ');
|
||||
handle.exec(`INSERT INTO server_creation_jobs_unplaced(rowid,${names}) SELECT rowid,${names} FROM server_creation_jobs;
|
||||
DROP TABLE server_creation_jobs;
|
||||
ALTER TABLE server_creation_jobs_unplaced RENAME TO server_creation_jobs;`);
|
||||
for (const object of dependent) handle.exec(object.sql);
|
||||
if ((handle.pragma('foreign_key_check') as unknown[]).length) throw new Error('Unplaced creation migration found broken references.');
|
||||
})();
|
||||
} finally { handle.pragma(`foreign_keys = ${foreignKeys ? 'ON' : 'OFF'}`); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user