Offer rebuild retry for failed creation commands
This commit is contained in:
@@ -14,6 +14,7 @@ import { canCancelCreation } from './cancel-creation.js';
|
||||
import { canCancelProvisioningJob, provisioningCancelled, rebalanceCancellationGuard } from './cancellation.js';
|
||||
import { committedRebalanceStartup } from './concurrency.js';
|
||||
import { nativeDeployment } from '../backends/native.js';
|
||||
import { ensureHostSchema } from '../hosts/schema.js';
|
||||
import { safetyOverride, safetyOverrideAvailability } from './safety-override.js';
|
||||
import { serverTitle } from '../../ui/server-labels.js';
|
||||
import { provisioningQueueStatus } from '../../ui/provisioning-queue.js';
|
||||
@@ -149,7 +150,8 @@ export function getDeployment(id: string, options: { events?: 'all' | 'recent' }
|
||||
activity: { ...activitySummary(id), failureExcerpt: latestFailureEvent(id) }, sources,
|
||||
recipe: launch?.recipe ?? null, blueprintName: launch?.blueprintName ?? null,
|
||||
canResumeVerification: canResumeVerification(id), canResumeCleanup: canResumeCleanup(id),
|
||||
canResumeRebuild: canResumeRebuild(id), canCancelRemoval: canCancelRemoval(id), collection: collection ?? null,
|
||||
canResumeRebuild: canResumeRebuild(id), canRetryWithRebuild: canRetryWithRebuild(id),
|
||||
canCancelRemoval: canCancelRemoval(id), collection: collection ?? null,
|
||||
progress: readProgress(row), capacity: capacityFor(deployment, generatedAt),
|
||||
warningMessages: warningMessages(deployment.warnings), generatedAt };
|
||||
})();
|
||||
@@ -197,17 +199,43 @@ export function resumeCleanup(id: string): boolean {
|
||||
}
|
||||
|
||||
export function canResumeVerification(id: string): boolean {
|
||||
ensureHostSchema(db());
|
||||
return !!db().prepare(`SELECT d.id FROM server_deployments d
|
||||
JOIN server_creation_jobs j ON j.deployment_id = d.id
|
||||
JOIN servers s ON s.name = j.server AND s.game_port = j.port
|
||||
WHERE d.id = ? AND d.server_deleted_at IS NULL AND (d.action = 'create' OR (d.action = 'rebuild' AND (EXISTS (
|
||||
SELECT 1 FROM server_rebuild_jobs r WHERE r.deployment_id = d.id AND r.removed_at IS NOT NULL) OR EXISTS (
|
||||
SELECT 1 FROM ec_host_migrations m JOIN native_provisioning n ON n.deployment_id=m.deployment_id
|
||||
WHERE m.deployment_id=d.id AND m.removed_at IS NOT NULL AND n.operation_id IS NOT NULL)))) AND d.state IN ('failed', 'interrupted')
|
||||
JOIN servers s ON s.name = j.server AND s.game_port = j.port AND s.destroyed_at IS NULL
|
||||
LEFT JOIN native_provisioning n ON n.deployment_id=d.id
|
||||
LEFT JOIN native_instances i ON i.id=n.instance_id AND i.server=d.server AND i.retired_at IS NULL
|
||||
LEFT JOIN ec_host_operations o ON o.id=n.operation_id
|
||||
WHERE d.id = ? AND d.server_deleted_at IS NULL AND d.action IN ('create','rebuild')
|
||||
AND d.state IN ('failed', 'interrupted')
|
||||
AND ((n.deployment_id IS NOT NULL AND i.port=j.port AND o.state='completed'
|
||||
AND o.instance_id=i.id AND o.host_id=i.host_id AND o.generation=n.generation
|
||||
AND ((o.kind IN ('instance.create','instance.restart') AND i.generation=n.generation)
|
||||
OR (o.kind='instance.rebuild' AND i.generation IN (n.generation,n.generation+1))))
|
||||
OR (n.deployment_id IS NULL AND (d.action='create' OR EXISTS (
|
||||
SELECT 1 FROM server_rebuild_jobs r WHERE r.deployment_id=d.id AND r.removed_at IS NOT NULL))))
|
||||
AND NOT EXISTS (SELECT 1 FROM server_deployments newer WHERE newer.server = d.server AND newer.rowid > d.rowid)`)
|
||||
.get(id);
|
||||
}
|
||||
|
||||
/** A conclusively failed initial create can enter the normal rebuild flow. */
|
||||
export function canRetryWithRebuild(id: string): boolean {
|
||||
ensureHostSchema(db());
|
||||
return !!db().prepare(`SELECT d.id FROM server_deployments d
|
||||
JOIN server_creation_jobs j ON j.deployment_id=d.id
|
||||
JOIN servers s ON s.name=d.server AND s.game_port=j.port AND s.destroyed_at IS NULL
|
||||
JOIN native_provisioning n ON n.deployment_id=d.id AND n.recovery_step IS NULL
|
||||
JOIN native_instances i ON i.id=n.instance_id AND i.server=d.server AND i.retired_at IS NULL
|
||||
AND i.generation=n.generation AND i.port=j.port
|
||||
JOIN ec_host_operations o ON o.id=n.operation_id AND o.instance_id=i.id
|
||||
AND o.host_id=i.host_id AND o.generation=n.generation
|
||||
WHERE d.id=? AND d.server_deleted_at IS NULL AND d.action='create'
|
||||
AND d.state IN ('failed','interrupted') AND o.kind='instance.create' AND o.state='failed'
|
||||
AND NOT EXISTS (SELECT 1 FROM native_creation_cancellations c WHERE c.creation_id=d.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM server_deployments newer WHERE newer.server=d.server AND newer.rowid>d.rowid)`)
|
||||
.get(id);
|
||||
}
|
||||
|
||||
export function canResumeRebuild(id: string): boolean {
|
||||
return !!db().prepare(`SELECT d.id FROM server_deployments d JOIN server_rebuild_jobs r ON r.deployment_id = d.id
|
||||
WHERE d.id = ? AND d.server_deleted_at IS NULL AND d.state IN ('failed','interrupted') AND d.stage = ? AND r.removed_at IS NULL
|
||||
@@ -226,7 +254,7 @@ export function resumeRebuild(id: string): boolean {
|
||||
})();
|
||||
}
|
||||
|
||||
/** Resume observation only. Never repeat a container creation or restart. */
|
||||
/** Resume readiness after a completed command; unhealthy recovery still applies. */
|
||||
export function resumeVerification(id: string): boolean {
|
||||
return db().transaction(() => {
|
||||
if (!canResumeVerification(id)) return false;
|
||||
|
||||
Reference in New Issue
Block a user