Make Code Queue read role database startup read-only
This commit is contained in:
@@ -283,6 +283,10 @@ function serviceRoleAllowsScheduler(role: CodeQueueServiceRole): boolean {
|
||||
return role === "combined" || role === "scheduler";
|
||||
}
|
||||
|
||||
function serviceRoleReadOnly(role: CodeQueueServiceRole): boolean {
|
||||
return role === "read";
|
||||
}
|
||||
|
||||
function envList(name: string, fallback: string[]): string[] {
|
||||
const raw = process.env[name];
|
||||
const source = raw === undefined || raw.length === 0 ? fallback.join(",") : raw;
|
||||
@@ -1158,6 +1162,7 @@ function runGarbageCollection(): void {
|
||||
}
|
||||
|
||||
function scheduleDatabaseFlush(delayMs = config.databaseFlushIntervalMs): void {
|
||||
if (serviceRoleReadOnly(config.serviceRole)) return;
|
||||
if (!databaseReady || (dirtyDatabaseTaskIds.size === 0 && dirtyDatabaseQueueIds.size === 0) || shutdownRequested) return;
|
||||
if (databaseFlushTimer !== null) return;
|
||||
databaseFlushTimer = setTimeout(() => {
|
||||
@@ -1924,6 +1929,11 @@ async function loadNextSeqFromDatabase(): Promise<number> {
|
||||
}
|
||||
|
||||
async function flushDirtyTasksToDatabase(force = false): Promise<void> {
|
||||
if (serviceRoleReadOnly(config.serviceRole)) {
|
||||
dirtyDatabaseTaskIds.clear();
|
||||
dirtyDatabaseQueueIds.clear();
|
||||
return;
|
||||
}
|
||||
if (!databaseReady) return;
|
||||
if (databaseFlushInFlight && !force) {
|
||||
scheduleDatabaseFlush();
|
||||
@@ -1964,89 +1974,91 @@ async function flushDirtyTasksToDatabase(force = false): Promise<void> {
|
||||
|
||||
async function initDatabasePersistence(): Promise<void> {
|
||||
logger("info", "database_persistence_init_start", { databaseUrl: redactDatabaseUrl(config.databaseUrl) });
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
queue_id TEXT NOT NULL DEFAULT 'default',
|
||||
status TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL DEFAULT 'main-server',
|
||||
execution_mode TEXT NOT NULL DEFAULT 'default',
|
||||
model TEXT NOT NULL,
|
||||
cwd TEXT NOT NULL,
|
||||
prompt TEXT NOT NULL,
|
||||
base_prompt TEXT NOT NULL DEFAULT '',
|
||||
reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
reference_injection JSONB,
|
||||
reasoning_effort TEXT,
|
||||
max_attempts INTEGER NOT NULL,
|
||||
current_attempt INTEGER NOT NULL DEFAULT 0,
|
||||
current_mode TEXT,
|
||||
codex_thread_id TEXT,
|
||||
active_turn_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
started_at TIMESTAMPTZ,
|
||||
finished_at TIMESTAMPTZ,
|
||||
read_at TIMESTAMPTZ,
|
||||
last_error TEXT,
|
||||
last_judge JSONB,
|
||||
output_count INTEGER NOT NULL DEFAULT 0,
|
||||
event_count INTEGER NOT NULL DEFAULT 0,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_output_seq BIGINT NOT NULL DEFAULT 0,
|
||||
task_json JSONB NOT NULL
|
||||
)
|
||||
`;
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_queues (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
)
|
||||
`;
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_workdirs (
|
||||
provider_id TEXT NOT NULL,
|
||||
execution_mode TEXT NOT NULL DEFAULT 'default',
|
||||
path TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (provider_id, execution_mode, path)
|
||||
)
|
||||
`;
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_notifications (
|
||||
id TEXT PRIMARY KEY,
|
||||
kind TEXT NOT NULL,
|
||||
dedup_key TEXT NOT NULL,
|
||||
target TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
next_attempt_at TIMESTAMPTZ NOT NULL,
|
||||
last_error TEXT,
|
||||
sent_at TIMESTAMPTZ
|
||||
)
|
||||
`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS queue_id TEXT NOT NULL DEFAULT 'default'`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS provider_id TEXT NOT NULL DEFAULT 'main-server'`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default'`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS base_prompt TEXT NOT NULL DEFAULT ''`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_injection JSONB`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS read_at TIMESTAMPTZ`;
|
||||
await sql`
|
||||
UPDATE unidesk_code_queue_tasks
|
||||
SET read_at = NULLIF(task_json->>'readAt', '')::timestamptz
|
||||
WHERE read_at IS NULL
|
||||
AND status IN ('succeeded', 'failed', 'canceled')
|
||||
AND COALESCE(task_json->>'readAt', '') <> ''
|
||||
AND (task_json->>'readAt') ~ '^\\d{4}-\\d{2}-\\d{2}T'
|
||||
`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_queues ADD COLUMN IF NOT EXISTS name TEXT NOT NULL DEFAULT ''`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_workdirs ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default'`;
|
||||
if (!serviceRoleReadOnly(config.serviceRole)) {
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
queue_id TEXT NOT NULL DEFAULT 'default',
|
||||
status TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL DEFAULT 'main-server',
|
||||
execution_mode TEXT NOT NULL DEFAULT 'default',
|
||||
model TEXT NOT NULL,
|
||||
cwd TEXT NOT NULL,
|
||||
prompt TEXT NOT NULL,
|
||||
base_prompt TEXT NOT NULL DEFAULT '',
|
||||
reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
reference_injection JSONB,
|
||||
reasoning_effort TEXT,
|
||||
max_attempts INTEGER NOT NULL,
|
||||
current_attempt INTEGER NOT NULL DEFAULT 0,
|
||||
current_mode TEXT,
|
||||
codex_thread_id TEXT,
|
||||
active_turn_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
started_at TIMESTAMPTZ,
|
||||
finished_at TIMESTAMPTZ,
|
||||
read_at TIMESTAMPTZ,
|
||||
last_error TEXT,
|
||||
last_judge JSONB,
|
||||
output_count INTEGER NOT NULL DEFAULT 0,
|
||||
event_count INTEGER NOT NULL DEFAULT 0,
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_output_seq BIGINT NOT NULL DEFAULT 0,
|
||||
task_json JSONB NOT NULL
|
||||
)
|
||||
`;
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_queues (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
)
|
||||
`;
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_workdirs (
|
||||
provider_id TEXT NOT NULL,
|
||||
execution_mode TEXT NOT NULL DEFAULT 'default',
|
||||
path TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (provider_id, execution_mode, path)
|
||||
)
|
||||
`;
|
||||
await sql`
|
||||
CREATE TABLE IF NOT EXISTS unidesk_code_queue_notifications (
|
||||
id TEXT PRIMARY KEY,
|
||||
kind TEXT NOT NULL,
|
||||
dedup_key TEXT NOT NULL,
|
||||
target TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL,
|
||||
attempts INTEGER NOT NULL DEFAULT 0,
|
||||
next_attempt_at TIMESTAMPTZ NOT NULL,
|
||||
last_error TEXT,
|
||||
sent_at TIMESTAMPTZ
|
||||
)
|
||||
`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS queue_id TEXT NOT NULL DEFAULT 'default'`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS provider_id TEXT NOT NULL DEFAULT 'main-server'`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default'`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS base_prompt TEXT NOT NULL DEFAULT ''`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_injection JSONB`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS read_at TIMESTAMPTZ`;
|
||||
await sql`
|
||||
UPDATE unidesk_code_queue_tasks
|
||||
SET read_at = NULLIF(task_json->>'readAt', '')::timestamptz
|
||||
WHERE read_at IS NULL
|
||||
AND status IN ('succeeded', 'failed', 'canceled')
|
||||
AND COALESCE(task_json->>'readAt', '') <> ''
|
||||
AND (task_json->>'readAt') ~ '^\\d{4}-\\d{2}-\\d{2}T'
|
||||
`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_queues ADD COLUMN IF NOT EXISTS name TEXT NOT NULL DEFAULT ''`;
|
||||
await sql`ALTER TABLE unidesk_code_queue_workdirs ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default'`;
|
||||
}
|
||||
|
||||
const countRows = await sql<Array<{ count: string | number }>>`SELECT COUNT(*) AS count FROM unidesk_code_queue_tasks`;
|
||||
const hotTasks = await loadTasksFromDatabase("hot");
|
||||
@@ -2091,7 +2103,7 @@ async function initDatabasePersistence(): Promise<void> {
|
||||
}
|
||||
}
|
||||
ensureDefaultWorkdirRecords();
|
||||
await upsertWorkdirsToDatabase(sortedWorkdirRecords());
|
||||
if (!serviceRoleReadOnly(config.serviceRole)) await upsertWorkdirsToDatabase(sortedWorkdirRecords());
|
||||
databaseReady = true;
|
||||
if (config.serviceRole === "combined" || config.serviceRole === "scheduler") scheduleStartupDatabaseMaintenance();
|
||||
runGarbageCollection();
|
||||
|
||||
Reference in New Issue
Block a user