Skip to main content

compute

The compute: parent of the pod and the patient-data API.

Compute is a library object hosted by whichever process is at the top of the tree -- the orchestrator on the desktop, bitfount run_compute in Docker. It owns the compute's identity, its registration with the hub, its TLS certificate, and the lifecycles of its two children. The pod and the API are siblings under it; neither knows about the other (ADR 0011).

This module must not import from bitfount.federated: that package's top-level import pulls in the pod, which imports the patient API. Anything that needs the pod belongs in the host, handed in as a ChildSpec.

First implementation: a child that exits is reported in status() and not restarted. Restart policy is a later change.

Module​

Global variables​

  • POD_INSTANCE_ID_ENV_VAR : Final[str] - The environment variable carrying a pod child's identity to it, which the pod reads as config.settings.private.pod_instance_id and reports on its control server's /health. _BITFOUNT_-prefixed so the settings read it as private. Minted fresh for every pod child the compute starts, so a host can tell its own live pod from one a previous generation left behind.
  • REGISTRATION_INTERVAL_SECONDS : Final[float] - How often the compute re-registers with the hub and checks its certificate. Fast enough for an IP change to reach DNS within a minute; deliberately not the 10-second "dataset online" signal, which stays with the pod.

Classes​

ChildStatus​

class ChildStatus(    started: bool,    alive: bool,    exit_code: int | None,    pid: int | None = None,    instance_id: str | None = None,):

Whether a child is running, and how it ended if not.

Arguments

  • started: Whether the child has been started since the last stop.
  • alive: Whether it is running now.
  • exit_code: How it ended, if it has.
  • pid: Its process id, if it was started.
  • instance_id: The identity it was handed, for the pod child only; see POD_INSTANCE_ID_ENV_VAR.

Variables​

  • static alive : bool
  • static exit_code : int | None
  • static instance_id : str | None
  • static pid : int | None
  • static started : bool

Compute​

class Compute(    *,    identity: ComputeIdentity,    hub: BitfountHub,    pod_child: ChildSpec,    api_child: ChildSpec | None = None,    registration_interval_seconds: float = 60.0,):

Owns the compute's identity, registration, certificate and children.

The key pair is loaded, and created on a first run, here: before either child starts, so neither child ever creates it. Creation is first-writer-wins in any case.

Arguments

  • identity: Who this compute is and where its files live.
  • hub: The compute's authenticated hub session.
  • pod_child: What to run as the pod. The only child a host describes: the desktop's pod takes the app's request and a token queue, Docker's a config path.
  • api_child: What to run as the patient-data API. Defaults to serving it for identity, which is all any host has needed.
  • registration_interval_seconds: How often to re-register and check the certificate.

Methods​


pod_status​

def pod_status(self) ‑> ChildStatus:

The pod child alone, cheaply.

status also reports whether a servable certificate is on disk, which reads and parses it; a host polling the pod every few seconds wants neither. This reads only the process handle.

start​

def start(self, *, pod_spec: ChildSpec | None = None) ‑> None:

Bring the compute up: registration, certificate, API, then pod.

The API is started before the pod so nothing about the pod's startup -- however long its datasets take -- delays it. Neither child's failure to start stops the other.

Arguments

  • pod_spec: A replacement for what runs as the pod; see start_pod.

start_pod​

def start_pod(self, spec: ChildSpec | None = None) ‑> None:

Start the pod child if it is not running.

Every pod child started here is handed a freshly minted identity (see POD_INSTANCE_ID_ENV_VAR), reported as status().pod.instance_id. A host that needs to know whether a call actually restarted the pod compares that before and after; a running pod keeps its id.

Arguments

  • spec: A replacement for what runs as the pod, used for this start and every later one. A host whose pod configuration arrives with each start request (the desktop app posts it) passes the fresh one here; a running pod is left alone either way.

status​

def status(self) ‑> ComputeStatus:

A snapshot of the compute and its children.

stop​

def stop(self) ‑> None:

Take the compute down: both children, then registration.

stop_pod​

def stop_pod(self) ‑> None:

Stop the pod child if it is running. The API is untouched.

Discards the handle once stopped, so status() reports the pod as never started rather than as crashed with its SIGTERM exit code -- a poll after a deliberate stop must read the same as one that never found a pod, not as one that found a dead one.

tick​

def tick(self) ‑> None:

One registration + certificate pass. Public for hosts and tests.

ComputeStatus​

class ComputeStatus(    instance_id: str,    compute_id: str | None,    domain: str | None,    registered: bool,    certificate_present: bool,    pod: ChildStatus,    patient_api: ChildStatus,):

A snapshot of the compute and its children.

Variables​

  • static certificate_present : bool
  • static compute_id : str | None
  • static domain : str | None
  • static instance_id : str
  • static registered : bool