rollbridge 0.1.44 → 0.1.45

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.44",
3
+ "version": "0.1.45",
4
4
  "description": "Zero-downtime process supervisor and local traffic switcher for deploy-managed apps.",
5
5
  "keywords": [
6
6
  "deploy",
@@ -531,7 +531,7 @@ export default class ManagedProcess extends EventEmitter {
531
531
  const child = this.child
532
532
  const pid = this.pid
533
533
 
534
- if (!child?.pid || child.pid !== pid || (this.state !== "quiesced" && this.state !== "running")) {
534
+ if (!child?.pid || child.pid !== pid || (this.state !== "failed" && this.state !== "quiesced" && this.state !== "running")) {
535
535
  throw new Error(`Process ${this.id} is not retained for reactivation`)
536
536
  }
537
537
  await this.runReactivationHook(pid)
@@ -294,11 +294,8 @@ export default class ReleaseGroup extends EventEmitter {
294
294
  const reactivate = async (processInstance) => {
295
295
  const {pid, state} = processInstance.status()
296
296
 
297
- if (state === "failed") {
298
- await processInstance.stop()
299
- await processInstance.start("crash", "active")
300
- } else if (pid !== undefined) await processInstance.reactivateStrict()
301
- else if (state === "stopped") await processInstance.start("crash", "active")
297
+ if (pid !== undefined) await processInstance.reactivateStrict()
298
+ else if (state === "stopped" || state === "failed") await processInstance.start("crash", "active")
302
299
  else throw new Error(`Generation process is ${state}; refusing active-role restoration`)
303
300
  }
304
301
 
@@ -532,6 +532,31 @@ test("reactivateStrict restores a retained quiesced process only after activatio
532
532
  }
533
533
  })
534
534
 
535
+ test("reactivateStrict retries a failed active-role startup against the retained child", async () => {
536
+ const managed = buildLongLived(() => false)
537
+ let attempts = 0
538
+
539
+ managed.lifecycle = {activateCommand: "jobs activate", drainTimeoutMs: 0}
540
+ managed.runHook = async () => {
541
+ attempts += 1
542
+ return attempts === 1 ? new Error("startup activation raced readiness") : undefined
543
+ }
544
+
545
+ try {
546
+ await assert.rejects(() => managed.start("deploy", "active"), /startup activation raced readiness/)
547
+ const failed = managed.status()
548
+
549
+ assert.equal(failed.state, "failed")
550
+ assert.ok(failed.pid)
551
+ await managed.reactivateStrict()
552
+ assert.equal(managed.status().state, "running")
553
+ assert.equal(managed.status().lifecycleRole, "active")
554
+ assert.equal(managed.status().pid, failed.pid)
555
+ } finally {
556
+ await managed.stop()
557
+ }
558
+ })
559
+
535
560
  test("quiesce waits for active-role restoration before retiring a restarted process", async () => {
536
561
  const managed = buildLongLived(() => false)
537
562
  const hooks = /** @type {string[]} */ ([])