diff --git a/src/lib/server/hosts/schema.ts b/src/lib/server/hosts/schema.ts index 0075062..cb12041 100644 --- a/src/lib/server/hosts/schema.ts +++ b/src/lib/server/hosts/schema.ts @@ -29,6 +29,10 @@ export function ensureHostSchema(handle: Database.Database): void { UNIQUE(host_id, idempotency_key) ); CREATE INDEX IF NOT EXISTS ec_host_operations_queue ON ec_host_operations(host_id, state, created_at); + -- Polling checks cancellations and preparation by kind across retained + -- history. The queue index cannot narrow those lookups beyond host_id, + -- causing a history scan for every queued command on Node's main thread. + CREATE INDEX IF NOT EXISTS ec_host_operations_kind ON ec_host_operations(host_id, kind, created_at); CREATE TABLE IF NOT EXISTS ec_host_operation_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, operation_id TEXT NOT NULL REFERENCES ec_host_operations(id), state TEXT NOT NULL, created_at TEXT NOT NULL, error TEXT diff --git a/tests/unit/native-hosts.test.ts b/tests/unit/native-hosts.test.ts index bf07be2..e164b98 100644 --- a/tests/unit/native-hosts.test.ts +++ b/tests/unit/native-hosts.test.ts @@ -15,6 +15,28 @@ async function enrolled() { } describe('native host enrollment and scoped authentication', () => { + it('narrows cancellation lookups by kind instead of scanning completed health history', async () => { + const api = await enrolled(); + api.heartbeat(api.host.id, { ...inventory, capabilities: ['instance.cancel-create', 'operations.health-lane'] }); + const { db } = await import('../../src/lib/server/db/index.js'); + const handle = db(); + const insert = handle.prepare(`INSERT INTO ec_host_operations + (id,host_id,kind,payload,state,created_at,updated_at) VALUES(?,?,'instance.inspect','{}','completed',?,?)`); + const at = new Date().toISOString(); + handle.transaction(() => { + for (let i = 0; i < 10_000; i++) insert.run(`history-${i}`, api.host.id, at, at); + })(); + const inspect = api.queueHostOperation(api.host.id, 'instance.inspect', {}); + const prepare = vi.spyOn(handle, 'prepare'); + expect(api.pendingHostOperations(api.host.id, 'health').map(op => op.id)).toEqual([inspect.id]); + const query = prepare.mock.calls.map(([sql]) => sql).find(sql => sql.includes('SELECT * FROM ec_host_operations o'))!; + prepare.mockRestore(); + // Assert the actual poll's access path, avoiding hardware-dependent timing thresholds. + const plan = handle.prepare(`EXPLAIN QUERY PLAN ${query}`).all(api.host.id, 1, 1) as { detail: string }[]; + expect(plan.find(row => row.detail.includes('SEARCH cancellation'))?.detail) + .toContain('(host_id=? AND kind=?)'); + }); + it('reserves health delivery while a file backlog is pending and bounds background batches', async () => { const api = await enrolled(); api.heartbeat(api.host.id, { ...inventory, capabilities: ['operations.health-lane'] });