fix(events): close review gaps found in the follow-up sweep
- deletion flow emitted job/instance envelopes before the transaction that closed the job rows; subscribers refetching on the notification saw status 'running' with no further event. Emits now happen after commit, including the failure path - interrupted-deletion sweep now emits job envelopes with the job's real request_id (it previously stayed silent while the secure-execution sweep already emitted) - verified during the sweep: backup restore validation re-derives the expected schema via migrateDatabase, so migration 18 is automatically covered; no tables reference audit_events or operation_preparations, so the retention deletes cannot trip FK constraints
This commit is contained in:
@@ -328,14 +328,6 @@ export class DeleteInstanceOperation {
|
||||
}
|
||||
try {
|
||||
await this.instances.delete(input.instanceId, input.revision, { allowJobHistory: true });
|
||||
this.#emit({
|
||||
kind: 'instance',
|
||||
id: this.id(),
|
||||
occurredAt: this.clock().toISOString(),
|
||||
requestId: input.requestId,
|
||||
instanceId: input.instanceId,
|
||||
});
|
||||
this.#emitJob(ids.job, input.requestId);
|
||||
const finished = this.clock().toISOString();
|
||||
this.db.transaction(() => {
|
||||
this.db
|
||||
@@ -356,6 +348,16 @@ export class DeleteInstanceOperation {
|
||||
)
|
||||
.run(finished, finished, ids.job);
|
||||
})();
|
||||
// Events only after the terminal rows are committed, so a subscriber that
|
||||
// refetches on the notification sees the finished job, not 'running'.
|
||||
this.#emit({
|
||||
kind: 'instance',
|
||||
id: this.id(),
|
||||
occurredAt: this.clock().toISOString(),
|
||||
requestId: input.requestId,
|
||||
instanceId: input.instanceId,
|
||||
});
|
||||
this.#emitJob(ids.job, input.requestId);
|
||||
} catch (error) {
|
||||
const finished = this.clock().toISOString();
|
||||
const state = error instanceof InstanceServiceError ? 'failed' : 'unknown-result';
|
||||
@@ -375,6 +377,7 @@ export class DeleteInstanceOperation {
|
||||
.prepare('UPDATE jobs SET status=?,finished_at=?,updated_at=? WHERE id=?')
|
||||
.run(state, finished, finished, ids.job);
|
||||
})();
|
||||
this.#emitJob(ids.job, input.requestId);
|
||||
}
|
||||
return this.job(ids.job);
|
||||
}
|
||||
@@ -449,9 +452,10 @@ export class DeleteInstanceOperation {
|
||||
const finished = this.clock().toISOString();
|
||||
return this.db.transaction(() => {
|
||||
const jobs = this.db
|
||||
.prepare("SELECT id FROM jobs WHERE operation_id=? AND status='running'")
|
||||
.all(OPERATION_ID) as Array<{ id: string }>;
|
||||
for (const { id } of jobs) {
|
||||
.prepare("SELECT id, request_id FROM jobs WHERE operation_id=? AND status='running'")
|
||||
.all(OPERATION_ID) as Array<{ id: string; request_id: string }>;
|
||||
for (const { id, request_id: requestId } of jobs) {
|
||||
this.#emitJob(id, requestId);
|
||||
this.db
|
||||
.prepare(
|
||||
"UPDATE job_items SET status='unknown-result',result_code='INTERRUPTED',finished_at=?,updated_at=? WHERE job_id=? AND status='running'",
|
||||
|
||||
Reference in New Issue
Block a user