Index host operations by kind to avoid polling history scans
This commit is contained in:
@@ -29,6 +29,10 @@ export function ensureHostSchema(handle: Database.Database): void {
|
|||||||
UNIQUE(host_id, idempotency_key)
|
UNIQUE(host_id, idempotency_key)
|
||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS ec_host_operations_queue ON ec_host_operations(host_id, state, created_at);
|
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 (
|
CREATE TABLE IF NOT EXISTS ec_host_operation_events (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT, operation_id TEXT NOT NULL REFERENCES ec_host_operations(id),
|
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
|
state TEXT NOT NULL, created_at TEXT NOT NULL, error TEXT
|
||||||
|
|||||||
@@ -15,6 +15,28 @@ async function enrolled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('native host enrollment and scoped authentication', () => {
|
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 () => {
|
it('reserves health delivery while a file backlog is pending and bounds background batches', async () => {
|
||||||
const api = await enrolled();
|
const api = await enrolled();
|
||||||
api.heartbeat(api.host.id, { ...inventory, capabilities: ['operations.health-lane'] });
|
api.heartbeat(api.host.id, { ...inventory, capabilities: ['operations.health-lane'] });
|
||||||
|
|||||||
Reference in New Issue
Block a user