View Source ProcessHub.Service.Migration (ProcessHub v0.8.0)

Deferred-migration list and graceful node drain for the migration consent protocol. See ProcessHub.Strategy.Migration.MigrationConsent.

Summary

Functions

Parks children in the deferred list and ensures the retry tick runs.

Drains the local node before shutdown: removes it from the distribution cluster-wide, migrates every local child away through the consent gate, and force-migrates whatever is still deferred at the :timeout deadline (default 60000 ms). Leaves children untouched on error.

One retry pass: prunes entries whose child died or is assigned back to the local node, migrates the ready, re-consenting and expired ones, and returns the number of entries left.

Migrates one child to target_node, handing its state over per the hub's migration strategy — the same per-child move a drain performs, invocable for a single child with an explicit target. The child's declared-list entry is untouched by the move, so a crash mid-migration heals through the reconcile instead of losing the child.

Marks a deferred child ready; it migrates on the next retry tick.

Types

@type entry() :: %{
  child_id: ProcessHub.child_id(),
  deferred_at: integer(),
  ready: boolean()
}

Functions

Link to this function

defer_children(hub, child_ids)

View Source
@spec defer_children(ProcessHub.Hub.t(), [ProcessHub.child_id()]) :: :ok

Parks children in the deferred list and ensures the retry tick runs.

@spec deferred_child_ids(ProcessHub.Hub.t()) :: MapSet.t()
@spec deferred_list(ProcessHub.Hub.t()) :: [entry()]
Link to this function

drain(hub_or_id, opts \\ [])

View Source
@spec drain(
  ProcessHub.hub_id() | ProcessHub.Hub.t(),
  keyword()
) ::
  {:ok, %{migrated: non_neg_integer(), forced: non_neg_integer()}}
  | {:error, :no_target_nodes | :draining | :partitioned | :locked}

Drains the local node before shutdown: removes it from the distribution cluster-wide, migrates every local child away through the consent gate, and force-migrates whatever is still deferred at the :timeout deadline (default 60000 ms). Leaves children untouched on error.

A drain pass still queued at the deadline is waited for and whatever it defers is forced, so the drain then overruns :timeout by the work queued ahead of the pass, the pass's consent wait (at most :consent_timeout) and the forced moves.

Returns once every other node has confirmed it dropped this node. A node confirms behind the children it moved here before, during its own drain or redistribution, so those have been passed on by then. A node silent at the deadline is logged as a warning, and one whose hub or node goes away is not waited for.

A hub with work still queued, such as a membership change or a start, answers {:error, :locked} by default. Pass when_busy: :wait to drain it anyway: the drain's pass runs on the worker queue behind that work.

@spec draining?(ProcessHub.Hub.t()) :: boolean()
@spec handle_retry_tick(ProcessHub.Hub.t()) :: non_neg_integer()

One retry pass: prunes entries whose child died or is assigned back to the local node, migrates the ready, re-consenting and expired ones, and returns the number of entries left.

Link to this function

migrate_child(hub_id, child_id, target_node)

View Source
@spec migrate_child(ProcessHub.hub_id(), ProcessHub.child_id(), node()) ::
  :ok | {:error, :not_found | :not_a_member | :same_node | term()}

Migrates one child to target_node, handing its state over per the hub's migration strategy — the same per-child move a drain performs, invocable for a single child with an explicit target. The child's declared-list entry is untouched by the move, so a crash mid-migration heals through the reconcile instead of losing the child.

Runs on the node currently hosting the child and routes itself there when called elsewhere. The consent protocol is NOT queried — the caller decides the child may move; a drain remains the consent-gated path.

The distribution strategy still considers the child assigned by its own arithmetic, so a later topology event may migrate it back — callers healing toward an off-assignment placement must be prepared to re-apply.

Link to this function

migration_ready(hub_id, child_id)

View Source
@spec migration_ready(ProcessHub.hub_id() | ProcessHub.Hub.t(), ProcessHub.child_id()) ::
  :ok | {:error, :not_deferred}

Marks a deferred child ready; it migrates on the next retry tick.

@spec retry_interval(ProcessHub.Hub.t()) :: pos_integer()