Pool Plutonium keys by game for launches and autoscaling

This commit is contained in:
2026-10-07 11:24:53 -04:00
parent 56e5a2d06d
commit ec45371478
47 changed files with 956 additions and 305 deletions
+8 -3
View File
@@ -13,6 +13,7 @@ import { parseServerName, zombieMapBySlug, zombieMaps } from './map-aliases.js';
import { nativeLaunchSelection, queueNativeCreationIntent } from '../provisioning/native.js';
import { nativeDeployment, nativeInstance } from '../backends/native.js';
import { launchAvailability } from '../hosts/availability.js';
import { availableServerKeyCount } from './server-keys.js';
function coverage(entries: BlueprintEntry[], includeHealth = true) {
type Server = { name: string; game: string | null; map_slug: string | null; map: string | null };
@@ -72,6 +73,8 @@ function collectionPlan(entries: BlueprintEntry[], mode: CollectionMode, include
function allocate(entries: BlueprintEntry[], counts = coverage(entries)): CollectionPreview['servers'] {
const occupied = db().prepare(`SELECT name FROM servers UNION
SELECT server AS name FROM native_instances WHERE retired_at IS NULL UNION
SELECT server_id AS name FROM server_key_leases UNION
SELECT j.server AS name FROM server_creation_jobs j JOIN server_deployments d ON d.id=j.deployment_id
WHERE d.server_deleted_at IS NULL AND (d.state IN ('queued','running','awaiting-health')
OR (d.action='rebuild' AND (d.state='waiting' OR (d.state IN ('failed','interrupted') AND d.stage='Removing server files and registry'))))`)
@@ -83,7 +86,7 @@ function allocate(entries: BlueprintEntry[], counts = coverage(entries)): Collec
const taken = lobbies.get(match[1]) ?? new Set<number>();
taken.add(Number(match[2])); lobbies.set(match[1], taken);
}
const savedKeys = new Set((db().prepare("SELECT server_id FROM server_keys WHERE length(trim(pluto_key))>0").all() as { server_id: string }[]).map(row => row.server_id));
const availableKeys = { t5: availableServerKeyCount('t5'), t6: availableServerKeyCount('t6') };
return entries.flatMap((entry, entryIndex) => Array.from({ length: counts[entryIndex].missing }, () => {
const base = `${entry.recipe.game}_zombie_${entry.recipe.mapSlug}`;
const taken = lobbies.get(base) ?? new Set<number>();
@@ -92,7 +95,9 @@ function allocate(entries: BlueprintEntry[], counts = coverage(entries)): Collec
const native = entry.recipe.native;
const port = null;
if (lobby !== null) { taken.add(lobby); lobbies.set(base, taken); }
return { entryIndex, name, port, ...(native ? { native } : {}), hasSavedKey: !!name && savedKeys.has(name) };
const hasSavedKey = !!name && availableKeys[entry.recipe.game] > 0;
if (hasSavedKey) availableKeys[entry.recipe.game]--;
return { entryIndex, name, port, ...(native ? { native } : {}), hasSavedKey };
}));
}
@@ -185,7 +190,7 @@ export function queueCollection(input: { previewId?: unknown; keys?: unknown }):
const blockers = [...blockersFor(servers, entries), ...removalBlockersFor(removals)];
if (blockers.length) throw new Error(blockers.join(' '));
if (entries.some((_entry, index) => counts[index].missing !== reviewed.filter(server => server.entryIndex === index).length)) {
throw new Error('Fleet counts changed. Nothing was queued. Refresh the preview and review each server key.');
throw new Error('Fleet counts changed. Nothing was queued. Refresh the preview and review the launches.');
}
const expected = new Set(servers.map(server => server.name));
for (const [name, value] of Object.entries(keys)) {