rollbridge 0.1.46 → 0.1.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rollbridge",
3
- "version": "0.1.46",
3
+ "version": "0.1.47",
4
4
  "description": "Zero-downtime process supervisor and local traffic switcher for deploy-managed apps.",
5
5
  "keywords": [
6
6
  "deploy",
package/src/daemon.js CHANGED
@@ -25,7 +25,7 @@ const STATE_PERSIST_INTERVAL_MS = 5000
25
25
  * @typedef {{id: string, process: import("./managed-process.js").ManagedProcessStatus}} ProcessStatus
26
26
  * @typedef {{disruptive: true, mode: "legacy-first-upgrade", reason: string}} OwnerTransition
27
27
  * @typedef {"candidate_ready" | "retiring_previous" | "previous_retired" | "activating_candidate" | "restoring_previous" | "retiring_failed_candidate" | "degraded_active" | "committed_pending" | "committed" | "restoring_committed"} GenerationTransitionPhase
28
- * @typedef {{activationError?: string, activationLifecycle?: boolean, candidateReleaseId: string, candidateReleasePath: string, candidateRevision: string, compensationError?: string, configDigest: string, error?: string, journalRevision?: number, phase: GenerationTransitionPhase, previousReleaseId: string | null, startedAt: string, updatedAt: string}} GenerationTransition
28
+ * @typedef {{activationError?: string, activationLifecycle?: boolean, candidateReleaseId: string, candidateReleasePath: string, candidateRevision: string, compensationError?: string, configDigest: string, degradedIncumbent?: boolean, error?: string, journalRevision?: number, phase: GenerationTransitionPhase, previousReleaseId: string | null, startedAt: string, updatedAt: string}} GenerationTransition
29
29
  * @typedef {{activeReleaseId: string | null, application: string, bootstrap: BootstrapIdentity | undefined, control: import("./config.js").ControlConfig, daemonPid: number, daemonRuntime: import("./daemon-runtime.js").DaemonRuntimeIdentity | undefined, generationTransition?: GenerationTransition, ownerRecovery: {configDigest: string, ready: boolean} | undefined, ownerTransition?: OwnerTransition, orphans: {id: string, pid: number, releaseId: string | null}[], proxy: {host: string, port: number | undefined, upstreamHost: string}, releaseReferences: {releaseId: string, releasePath: string}[], releases: import("./release-group.js").ReleaseStatus[], services: ProcessStatus[], singletons: ProcessStatus[]}} DaemonStatus
30
30
  * @typedef {{configDigest: string, format: number, guardian: {pid?: number, socketPath: string, token: string}, reconnectGraceMs: number}} OwnerRecoveryMetadata
31
31
  * @typedef {DaemonStatus & {recovery: OwnerRecoveryMetadata, serviceReleaseIds?: Record<string, string>, singletonReleaseIds?: Record<string, string>}} OwnerRecoverySnapshot
@@ -1620,6 +1620,7 @@ export default class RollbridgeDaemon {
1620
1620
  candidateReleasePath: release.releasePath,
1621
1621
  candidateRevision: release.revision,
1622
1622
  configDigest: ownerConfigDigest(nextConfig),
1623
+ degradedIncumbent: transition?.phase === "degraded_active",
1623
1624
  journalRevision: 0,
1624
1625
  phase: activationLifecycle ? "candidate_ready" : "activating_candidate",
1625
1626
  previousReleaseId: previousRelease?.releaseId ?? null,
@@ -1651,7 +1652,7 @@ export default class RollbridgeDaemon {
1651
1652
  let retirementFailure = activationLifecycle ? undefined : previousRelease?.retirementError
1652
1653
 
1653
1654
  if (transition.phase === "candidate_ready") {
1654
- if (previousRelease) await this.updateGenerationTransition("retiring_previous")
1655
+ if (previousRelease && !transition.degradedIncumbent) await this.updateGenerationTransition("retiring_previous")
1655
1656
  else await this.updateGenerationTransition("previous_retired")
1656
1657
  }
1657
1658
 
@@ -834,7 +834,7 @@ test("explicit recovery stops the exact failed candidate and fences degraded inc
834
834
  assert.equal(daemon.status().activeReleaseId, "v3")
835
835
  assert.equal(daemon.status().generationTransition?.phase, "committed")
836
836
  assert.equal(await fetchText(daemon, "/release"), "v3")
837
- assert.deepEqual(await lifecycleEvents(fixture.lifecycleLogPath), ["activate:v1", "retire:v1", "retire:v2", "retire:bad-v3", "retire:v1", "activate:v3"])
837
+ assert.deepEqual(await lifecycleEvents(fixture.lifecycleLogPath), ["activate:v1", "retire:v1", "retire:v2", "retire:bad-v3", "activate:v3"], "fresh deployment must not re-retire a degraded incumbent generation")
838
838
  } finally {
839
839
  await daemon.shutdown()
840
840
  await fs.rm(fixture.root, {force: true, recursive: true})