Files
electric-cherry/src/lib/fleet-settings.ts
T

176 lines
10 KiB
TypeScript

export interface FleetSettings {
provisioningKeepDays: number;
actionKeepDays: number;
failureKeepDays: number;
maintenanceKeepDays: number;
recoveryKeepDays: number;
hostResourceKeepDays: number;
instanceResourceKeepDays: number;
metricsKeepDays: number;
telemetryKeepDays: number;
telemetryMaxPerServer: number;
telemetryMaxTotal: number;
matchKeepDays: number;
betaKeepDays: number;
fileContentKeepDays: number;
payloadKeepDays: number;
versionKeepDays: number;
versionKeepCount: number;
gameFileGraceDays: number;
gameFileKeepCount: number;
uploadKeepDays: number;
auditKeepDays: number;
historyKeepCount: number;
deployKeepDays: number;
observationKeepDays: number;
restartConcurrency: number;
createConcurrency: number;
destroyConcurrency: number;
restartConcurrencyPerHost: number;
createConcurrencyPerHost: number;
destroyConcurrencyPerHost: number;
lifecycleConcurrency: number;
rebuildConcurrency: number;
preparationConcurrency: number;
}
export const defaultFleetSettings: FleetSettings = {
provisioningKeepDays: 0,
actionKeepDays: 0,
failureKeepDays: 0,
maintenanceKeepDays: 30,
recoveryKeepDays: 0,
hostResourceKeepDays: 7,
instanceResourceKeepDays: 1,
metricsKeepDays: 31,
telemetryKeepDays: 7,
telemetryMaxPerServer: 200,
telemetryMaxTotal: 10000,
matchKeepDays: 0,
betaKeepDays: 0,
fileContentKeepDays: 30,
payloadKeepDays: 30,
versionKeepDays: 0,
versionKeepCount: 0,
gameFileGraceDays: 0,
gameFileKeepCount: 1,
uploadKeepDays: 0,
auditKeepDays: 0,
historyKeepCount: 0,
deployKeepDays: 7,
observationKeepDays: 1,
restartConcurrency: 1,
createConcurrency: 1,
destroyConcurrency: 1,
restartConcurrencyPerHost: 0,
createConcurrencyPerHost: 0,
destroyConcurrencyPerHost: 0,
lifecycleConcurrency: 0,
rebuildConcurrency: 0,
preparationConcurrency: 0,
};
export const fleetSettingLabels: Record<keyof FleetSettings, string> = {
provisioningKeepDays: 'Provisioning history retention',
actionKeepDays: 'Action operation history retention',
failureKeepDays: 'Resolved failure history retention',
maintenanceKeepDays: 'Maintenance history retention',
recoveryKeepDays: 'Resolved recovery incident retention',
hostResourceKeepDays: 'Host resource history retention',
instanceResourceKeepDays: 'Instance resource sample retention',
metricsKeepDays: 'Fleet metrics history retention',
telemetryKeepDays: 'Telemetry diagnostic history retention',
telemetryMaxPerServer: 'Maximum telemetry records per server',
telemetryMaxTotal: 'Maximum telemetry records overall',
matchKeepDays: 'Match history retention',
betaKeepDays: 'Completed beta history retention',
fileContentKeepDays: 'File diffs and rollback snapshot retention',
payloadKeepDays: 'Launch and command file retention',
versionKeepDays: 'Unused artifact version retention',
versionKeepCount: 'Minimum artifact versions to keep',
gameFileGraceDays: 'Unused game-file grace period',
gameFileKeepCount: 'Minimum game-file versions to keep',
uploadKeepDays: 'Abandoned upload retention',
auditKeepDays: 'Authentication audit history retention',
historyKeepCount: 'Minimum history records to keep',
deployKeepDays: 'Campaign history retention',
observationKeepDays: 'Routine operation history retention',
restartConcurrency: 'Concurrent restarts',
createConcurrency: 'Concurrent server startups',
destroyConcurrency: 'Concurrent server destructions',
restartConcurrencyPerHost: 'Concurrent restarts per host',
createConcurrencyPerHost: 'Concurrent startups per host',
destroyConcurrencyPerHost: 'Concurrent destructions per host',
lifecycleConcurrency: 'Combined lifecycle limit',
rebuildConcurrency: 'Concurrent rebuilds',
preparationConcurrency: 'Hosts preparing game files simultaneously',
};
export function validateFleetSettings(input: unknown): FleetSettings {
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('Provide fleet settings.');
const values = input as Record<string, unknown>;
for (const key of Object.keys(values)) {
if (!Object.hasOwn(defaultFleetSettings, key)) throw new Error(`Unknown setting: ${key}`);
}
for (const key of Object.keys(defaultFleetSettings) as (keyof FleetSettings)[]) {
const value = values[key];
const minimum = settingMinimum(key);
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum) {
throw new Error(`${fleetSettingLabels[key]} must be ${minimum === 0 ? 'zero or a positive whole number' : 'a positive whole number'}.`);
}
if (key.endsWith('Days') && value > 36500) {
throw new Error(`${fleetSettingLabels[key]} must be at most 36500 days.`);
}
}
return Object.fromEntries(Object.keys(defaultFleetSettings).map(key => [key, values[key]])) as unknown as FleetSettings;
}
export const additionalConcurrencyKeys: (keyof FleetSettings)[] = ["restartConcurrencyPerHost", "createConcurrencyPerHost", "destroyConcurrencyPerHost", "lifecycleConcurrency", "rebuildConcurrency", "preparationConcurrency"];
export const concurrencyKeys: (keyof FleetSettings)[] = ['restartConcurrency', 'createConcurrency', 'destroyConcurrency', ...additionalConcurrencyKeys];
export const retentionKeys: (keyof FleetSettings)[] = ['deployKeepDays', 'observationKeepDays', 'provisioningKeepDays', 'actionKeepDays', 'failureKeepDays', 'maintenanceKeepDays', 'recoveryKeepDays', 'hostResourceKeepDays', 'instanceResourceKeepDays', 'metricsKeepDays', 'telemetryKeepDays', 'telemetryMaxPerServer', 'telemetryMaxTotal', 'matchKeepDays', 'betaKeepDays', 'fileContentKeepDays', 'payloadKeepDays', 'versionKeepDays', 'versionKeepCount', 'gameFileGraceDays', 'gameFileKeepCount', 'uploadKeepDays', 'auditKeepDays', 'historyKeepCount'];
export function settingMinimum(key: keyof FleetSettings): number {
return ['restartConcurrency','createConcurrency','destroyConcurrency','gameFileKeepCount'].includes(key) ? 1 : 0;
}
export const settingHelp: Partial<Record<keyof FleetSettings, string>> = {
provisioningKeepDays: 'Completed lifecycle history. Recovery, source workflows and cancellation dependencies remain protected.',
actionKeepDays: 'Completed agent actions. Referenced commands and retry evidence remain protected. Expired actions keep compact receipts to prevent duplicate execution.',
failureKeepDays: 'Explicitly resolved failed operations and campaigns, measured from resolution. Unresolved failures remain protected.',
maintenanceKeepDays: 'Finished non-update maintenance. Update attempts remain durable to prevent duplicate automatic retries.',
recoveryKeepDays: 'Resolved watchdog incidents with no unfinished recovery. Dismissed incidents remain as suppression evidence.',
hostResourceKeepDays: 'Host CPU and memory history.',
instanceResourceKeepDays: 'Minute resource samples. Capacity learning continues to use its independent 24-hour window.',
metricsKeepDays: 'Historical fleet occupancy and utilization samples.',
telemetryKeepDays: 'Raw received telemetry diagnostics. Independent of saved matches. Count limits also apply.',
telemetryMaxPerServer: 'Count cap per server, in addition to the age limit. 0 disables this count cap.',
telemetryMaxTotal: 'Fleet-wide count cap for telemetry diagnostics. 0 disables this count cap.',
matchKeepDays: 'Completed and interrupted matches and player details. Expiry changes leaderboards and player totals; no lifetime aggregates are preserved.',
betaKeepDays: 'Closed betas, revisions and test evidence, after linked campaigns and recovery work no longer need them.',
fileContentKeepDays: 'Expired campaign file bodies are removed while summaries remain. Rollback becomes unavailable for those campaigns. Active beta snapshots remain protected.',
payloadKeepDays: 'Expire file bodies in completed launch recipes, commands and receipts while keeping their history. Unfinished work and protected records keep their files. 0 keeps files indefinitely.',
versionKeepDays: 'Unused script, plugin, configuration and baseline versions. Current and referenced versions are always protected.',
versionKeepCount: 'Keep at least this many recent versions per artifact. With no age expiry, a positive count removes older unused versions.',
gameFileGraceDays: 'Delay removal after a version becomes superseded. Latest files, referenced versions and offline host copies remain protected. 0 adds no delay.',
gameFileKeepCount: 'Keep this many recent published versions per game, in addition to every referenced or protected version.',
uploadKeepDays: 'Remove incomplete browser uploads after this many days without activity. Active transfers and publication are protected.',
auditKeepDays: 'Recorded authentication and account-administration events.',
historyKeepCount: 'Keep at least this many recent records per history category. Does not apply to metrics, resource samples, telemetry count caps or game-file storage.',
restartConcurrency: 'Fleet recovery slots stay occupied until container and game health are verified.',
createConcurrency: 'Fleet startup slots cover creation and rebuilds through health and selected-map verification.',
destroyConcurrency: 'Fleet removals hold capacity until teardown and cleanup finish.',
restartConcurrencyPerHost: 'Additional recovery limit on each host, including starts and rebuilds.',
createConcurrencyPerHost: 'Additional startup limit on each host, including rebuilds.',
destroyConcurrencyPerHost: 'Additional removal limit on each host, including migration source removal.',
lifecycleConcurrency: 'Combined fleet limit for creates, starts, stops, restarts, rebuilds and destructions. Each workflow counts once.',
rebuildConcurrency: 'Additional fleet limit for rebuilds, including host drain migrations.',
preparationConcurrency: 'Maximum hosts downloading or verifying game files at once. Interrupted transfers keep their slot until reconciled.',
deployKeepDays: 'Eligible campaign records and file diffs. Pending, unresolved failed, held and recovery-linked campaigns are protected.',
observationKeepDays: 'Successful monitoring and file-read records. Failures and actions are retained. 0 keeps records indefinitely.'
};