Use live host usage and running estimates for collection admission
This commit is contained in:
@@ -730,6 +730,37 @@ describe('automatic capacity admission and placement', () => {
|
||||
expect(capacityCalibration(hostId).startup.source).toBe('host');
|
||||
expect(queueNativeCreation(input)).toMatchObject({ ok: true });
|
||||
});
|
||||
it('queues a collection at running demand and releases startup reservations as live usage arrives', async () => {
|
||||
await prepareLibrary();
|
||||
const { heartbeat } = await import('../../src/lib/server/hosts/index.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { fleetSettings, saveFleetSettings } = await import('../../src/lib/server/settings.js');
|
||||
const { saveBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||
const { previewCollection, queueCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const GiB = 1024 ** 3;
|
||||
const measured = { ...inventory(), cpuCount: 4, cpuUsagePercent: 5, memoryTotal: 4 * GiB, memoryAvailable: 3.75 * GiB };
|
||||
heartbeat(hostId, measured);
|
||||
saveFleetSettings({ ...fleetSettings(), createConcurrency: 3 });
|
||||
const insert = db().prepare(`INSERT INTO ec_resource_allowances
|
||||
(scope,phase,cpu_cores,memory_bytes,samples,window_start,observed_at,computed_at) VALUES(?,?,?,?,30,?,?,?)`);
|
||||
const now = new Date().toISOString();
|
||||
const start = new Date(Date.now() - 30 * 60_000).toISOString();
|
||||
insert.run(hostId, 'startup', 2, 2 * GiB, start, now, now);
|
||||
insert.run(hostId, 'running', 0.2, GiB / 4, start, now, now);
|
||||
const blueprint = saveBlueprint({ name: 'Staggered lobbies', entries: [{ game: 't5', mapSlug: 'theater', count: 3 }] });
|
||||
const preview = previewCollection(blueprint.id);
|
||||
expect(preview.ready).toBe(true);
|
||||
expect(queueCollection({ previewId: preview.id, keys: Object.fromEntries(preview.servers.map(server => [server.name!, 'key'])) }).ok).toBe(true);
|
||||
await advance();
|
||||
const creates = () => db().prepare("SELECT instance_id FROM ec_host_operations WHERE kind='instance.create'").all() as { instance_id: string }[];
|
||||
expect(creates()).toHaveLength(1);
|
||||
await advance();
|
||||
expect(creates()).toHaveLength(1); // No heartbeat yet: the first launch still reserves startup resources.
|
||||
heartbeat(hostId, { ...measured, cpuUsagePercent: 10, memoryAvailable: 3.5 * GiB,
|
||||
instanceResources: [{ instanceId: creates()[0].instance_id, generation: 1, uptimeSeconds: 60, cpuCores: 0.2, memoryBytes: GiB / 4 }] });
|
||||
await advance();
|
||||
expect(creates()).toHaveLength(2); // Its actual usage replaces the peak even before health verification.
|
||||
});
|
||||
it('charges queued reservations and releases them on cancellation', async () => {
|
||||
await prepareLibrary();
|
||||
const { heartbeat } = await import('../../src/lib/server/hosts/index.js');
|
||||
@@ -801,8 +832,8 @@ describe('automatic capacity admission and placement', () => {
|
||||
expect(first.ok).toBe(true); if (!first.ok) return;
|
||||
await advance();
|
||||
const instance = nativeInstance(first.name)!;
|
||||
// 0.2 GiB OS + 0.8 GiB container. The remaining 0.2 GiB of its 1 GiB
|
||||
// reservation plus a new 1 GiB server fit below 2.53 GiB usable memory.
|
||||
// 0.2 GiB OS + 0.8 GiB container are already in host usage.
|
||||
// Adding a new 1 GiB server fits below 2.53 GiB usable memory.
|
||||
heartbeat(hostId, { ...host, memoryAvailable: 2 * GiB, instanceResources: [{ instanceId: instance.id,
|
||||
generation: instance.generation, uptimeSeconds: 60, cpuCores: 0.1, memoryBytes: Math.floor(0.8 * GiB) }] });
|
||||
const second = queueNativeCreation(input);
|
||||
|
||||
Reference in New Issue
Block a user