fix(cli): harden election edge cases
This commit is contained in:
parent
68c62774ac
commit
c61e93a1f3
2 changed files with 12 additions and 7 deletions
|
|
@ -29,7 +29,7 @@ type ManagedServiceOptions = Service.Options & { readonly file: string }
|
||||||
|
|
||||||
export const run = Effect.fn("cli.server-process.run")((options: Options) =>
|
export const run = Effect.fn("cli.server-process.run")((options: Options) =>
|
||||||
processEffect(options).pipe(
|
processEffect(options).pipe(
|
||||||
Effect.catchTag("ServiceAlreadyOwned", () => Effect.void),
|
Effect.catchTag("ServiceAlreadyOwned", () => Effect.logInfo("another process owns the managed service, exiting")),
|
||||||
Effect.provide(Updater.layer),
|
Effect.provide(Updater.layer),
|
||||||
Effect.provide(AppNodeBuilder.build(LayerNode.group([Global.node, AppProcess.node, EffectFlock.node]))),
|
Effect.provide(AppNodeBuilder.build(LayerNode.group([Global.node, AppProcess.node, EffectFlock.node]))),
|
||||||
Effect.provide(NodeServices.layer),
|
Effect.provide(NodeServices.layer),
|
||||||
|
|
@ -55,7 +55,11 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
|
||||||
(acquired) => acquired,
|
(acquired) => acquired,
|
||||||
() => ({ _tag: "ServiceAlreadyOwned" }) as const,
|
() => ({ _tag: "ServiceAlreadyOwned" }) as const,
|
||||||
),
|
),
|
||||||
Effect.retry(Schedule.spaced("100 millis").pipe(Schedule.both(Schedule.recurs(20)))),
|
// Retry only lost elections: a displaced owner may take a moment to release.
|
||||||
|
Effect.retry({
|
||||||
|
while: (error) => error._tag === "ServiceAlreadyOwned",
|
||||||
|
schedule: Schedule.spaced("100 millis").pipe(Schedule.both(Schedule.recurs(20))),
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const password =
|
const password =
|
||||||
|
|
|
||||||
|
|
@ -164,11 +164,12 @@ export namespace EffectFlock {
|
||||||
process.kill(owner.pid, 0)
|
process.kill(owner.pid, 0)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
catch: (cause) => {
|
catch: (cause) => (cause && typeof cause === "object" && "code" in cause ? cause.code : undefined),
|
||||||
const code = cause && typeof cause === "object" && "code" in cause ? cause.code : undefined
|
}).pipe(
|
||||||
return code !== "ESRCH"
|
// Only ESRCH proves the pid is gone; other errors (e.g. EPERM) mean a
|
||||||
},
|
// process exists, so never break a possibly live owner's lock.
|
||||||
}).pipe(Effect.orElseSucceed(() => false))
|
Effect.catch((code) => Effect.succeed(code !== "ESRCH")),
|
||||||
|
)
|
||||||
if (!alive) return true
|
if (!alive) return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue