portunus_delayed_restart (portunus v0.8.0)

View Source

Rate-limited restarts for plain Erlang/OTP supervisors, accepting the extended supervisor2 restart type without a dependency on supervisor2.

mirrored_supervisor is built on rabbit's supervisor2, which accepts an extended restart type {permanent, Delay} or {transient, Delay} (Delay in seconds). A child spec ported from mirrored_supervisor can carry that form; portunus runs elected children under a plain Erlang/OTP supervisor, which rejects it.

child_spec/1 rewrites such a spec into a standard one whose start is rate-limited to one attempt per Delay: an isolated crash restarts at once, and only a restart within Delay of the previous attempt waits out the remainder. This is not exactly supervisor2, which restarts immediately until MaxR/MaxT is exceeded and only then delays on a timer; the practical property migrated specs rely on (a one-off crash recovers immediately while a crash loop is dampened) is the same. The wait runs in the local supervisor's own process (a supervisor runs a child's start synchronously), so a crash-looping child holds up the others under that supervisor for up to Delay; the running pid is always the real child, so introspection and links are unchanged.

Last-attempt timestamps live in one node-global ETS set owned by portunus_sup, keyed by {LocalSup, ChildId} so entries are namespaced per local supervisor; forget/2 clears one when the child is stopped, so a later first start (for example after ownership moves back to this node) is immediate.

Summary

Types

Accepted input: a standard child spec (passed through untouched), or one carrying the extended restart, as a map or a supervisor2 tuple.

The extended supervisor2 restart: a standard type plus a delay in seconds before a restart.

Functions

Clear the last-attempt marker so the next start of Id is immediate.

Clear every marker of a local supervisor that is going away.

Types

child_spec_in()

-type child_spec_in() ::
          supervisor:child_spec() |
          #{id := term(),
            start := mfargs(),
            restart := delayed_restart(),
            shutdown => timeout() | brutal_kill,
            type => worker | supervisor,
            modules => [module()] | dynamic} |
          {term(),
           mfargs(),
           delayed_restart(),
           timeout() | brutal_kill,
           worker | supervisor,
           [module()] | dynamic}.

Accepted input: a standard child spec (passed through untouched), or one carrying the extended restart, as a map or a supervisor2 tuple.

delayed_restart()

-type delayed_restart() :: {permanent | transient, number()}.

The extended supervisor2 restart: a standard type plus a delay in seconds before a restart.

mfargs()

-type mfargs() :: {module(), atom(), [term()]}.

Functions

child_spec/1

-spec child_spec(child_spec_in()) -> supervisor:child_spec().

ensure_table()

-spec ensure_table() -> ok.

forget(LocalSup, Id)

-spec forget(pid(), term()) -> ok.

Clear the last-attempt marker so the next start of Id is immediate.

forget_all(LocalSup)

-spec forget_all(pid()) -> ok.

Clear every marker of a local supervisor that is going away.

start_link/3

-spec start_link(term(), number(), {module(), atom(), [term()]}) -> dynamic().