Reduce host capacity headroom and remove learned usage padding

This commit is contained in:
2026-10-04 23:33:21 -04:00
parent 725d41c112
commit b0f592b41c
8 changed files with 62 additions and 23 deletions
+23 -10
View File
@@ -108,17 +108,17 @@ describe('measured instance capacity', () => {
const { capacityCalibration } = await import('../../src/lib/server/hosts/calibration.js');
expect(capacityCalibration(hostId).startup).toMatchObject({ source: 'bootstrap', cpuCores: 0.5, memoryBytes: GiB });
const model = await learned();
expect(model.running).toMatchObject({ source: 'host', cpuCores: 0.125, memoryBytes: GiB * 0.3125, samples: 20 });
expect(model.startup).toMatchObject({ source: 'host', cpuCores: 0.25, memoryBytes: GiB * 0.46875, samples: 4 });
expect(model.running).toMatchObject({ source: 'host', cpuCores: 0.1, memoryBytes: GiB / 4, samples: 20 });
expect(model.startup).toMatchObject({ source: 'host', cpuCores: 0.2, memoryBytes: 3 * GiB / 8, samples: 4 });
});
it('raises allowances immediately on a measured spike, including during cold start', async () => {
await report(0, { cpuCores: 2, memoryBytes: 2 * GiB });
const { capacityCalibration } = await import('../../src/lib/server/hosts/calibration.js');
expect(capacityCalibration(hostId).running).toMatchObject({ source: 'bootstrap', cpuCores: 2.5, memoryBytes: 2.5 * GiB });
expect(capacityCalibration(hostId).running).toMatchObject({ source: 'bootstrap', cpuCores: 2, memoryBytes: 2 * GiB });
await learned();
await report(25, { cpuCores: 3, memoryBytes: 3 * GiB });
expect(capacityCalibration(hostId).running).toMatchObject({ source: 'host', cpuCores: 3.75, memoryBytes: 3.75 * GiB });
expect(capacityCalibration(hostId).startup.cpuCores).toBe(3.75);
expect(capacityCalibration(hostId).running).toMatchObject({ source: 'host', cpuCores: 3, memoryBytes: 3 * GiB });
expect(capacityCalibration(hostId).startup.cpuCores).toBe(3);
});
it('uses recent fleet evidence on new hosts and survives a database restart', async () => {
const model = await learned();
@@ -129,6 +129,19 @@ describe('measured instance capacity', () => {
expect(capacityCalibration(other.id).running).toMatchObject({ ...model.running, source: 'fleet' });
expect(capacityCalibration(hostId)).toEqual(model);
});
it('removes legacy cached padding once while preserving host and fleet evidence', async () => {
const model = await learned();
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
// Recreate the previous schema and its padded allowance values.
db().exec('ALTER TABLE ec_resource_allowances DROP COLUMN usage_margin');
db().exec('UPDATE ec_resource_allowances SET cpu_cores=cpu_cores*1.25, memory_bytes=memory_bytes*1.25');
closeDb();
const { capacityCalibration } = await import('../../src/lib/server/hosts/calibration.js');
expect(capacityCalibration(hostId)).toEqual(model);
expect(capacityCalibration('new-host').startup).toEqual({ ...model.startup, source: 'fleet' });
closeDb();
expect(capacityCalibration(hostId)).toEqual(model);
});
it('prefers mature local measurements when hosts have different CPU footprints', async () => {
await learned();
const hosts = await import('../../src/lib/server/hosts/index.js');
@@ -137,9 +150,9 @@ describe('measured instance capacity', () => {
await instance(other.id, 'instance-2');
for (let minute = 25; minute < 45; minute++) await report(minute, { instanceId: 'instance-2', cpuCores: 2 }, other.id);
const { capacityCalibration } = await import('../../src/lib/server/hosts/calibration.js');
expect(capacityCalibration(hostId).running).toMatchObject({ source: 'host', cpuCores: 0.125 });
expect(capacityCalibration(other.id).running).toMatchObject({ source: 'host', cpuCores: 2.5 });
expect(capacityCalibration('new-host').running).toMatchObject({ source: 'fleet', cpuCores: 2.5 });
expect(capacityCalibration(hostId).running).toMatchObject({ source: 'host', cpuCores: 0.1 });
expect(capacityCalibration(other.id).running).toMatchObject({ source: 'host', cpuCores: 2 });
expect(capacityCalibration('new-host').running).toMatchObject({ source: 'fleet', cpuCores: 2 });
});
it('retains minute peaks instead of treating repeated heartbeats as independent evidence', async () => {
const { heartbeat } = await import('../../src/lib/server/hosts/index.js');
@@ -183,12 +196,12 @@ describe('measured instance capacity', () => {
expect(db().prepare('SELECT * FROM ec_instance_resource_samples').all()).toHaveLength(1);
expect(capacityCalibration(hostId).running.source).toBe('bootstrap');
});
it('uses a running percentile and startup peaks with margin, keeping admission reads cached', async () => {
it('uses a running percentile and startup peaks without padding, keeping admission reads cached', async () => {
for (let minute = 0; minute < 100; minute++) await report(minute, { cpuCores: minute === 0 ? 2 : 0.1 });
const { db } = await import('../../src/lib/server/db/index.js');
const { capacityCalibration } = await import('../../src/lib/server/hosts/calibration.js');
const prepare = vi.spyOn(db(), 'prepare');
expect(capacityCalibration(hostId).running.cpuCores).toBe(0.125);
expect(capacityCalibration(hostId).running.cpuCores).toBe(0.1);
expect(prepare.mock.calls.some(([sql]) => sql.includes('ec_instance_resource_samples'))).toBe(false);
prepare.mockRestore();
});