Skip to content

Design — The Wrapper Anatomy

Foundations: what is inside, and why each piece earns its place.

At a glance

  • What — the five components of the function host and the design rulings behind them: faithful instances/private, an anycast event bus, fail-fast deadlines, and engine-identical operations.
  • For developers and reviewers who want the mental model before the API.

Anatomy

flowchart TB
    subgraph Host [Python function host]
      direction TB
      A["/api/event<br/>(Event API host)"] --> B[EventBus<br/>per-route FIFO mailboxes]
      C[PostOffice<br/>local mode] --> B
      B --> W1[worker 1..N] --> F["@preload function"]
      D[Actuator<br/>/info /health ...] -.probes via bus.-> B
    end
    E[Engine or peer] -- envelope bytes --> A
    F -- PostOffice remote mode --> X[another host or engine]

Five components, one dispatch pipeline:

Component Engine convention it mirrors
Envelope codec (EventEnvelope) the standard wire format, verified against golden vectors shared with both engines
Event API host (POST /api/event) the engines' event.api.service semantics: x-ttl bounds execution, x-async is drop-n-forget, handler errors ride HTTP 200 inside the envelope
Primitive event bus the engines' in-memory bus semantics: per-route FIFO, instances worker tasks, deliver (RPC) and publish (drop-n-forget) — nothing else
PostOffice the engines' po.request/po.send vocabulary — remote to any peer's /api/event, local through the same bus
Actuator + index page the engines' operational surface: /, /info, /info/routes, /env, /health, /livenessprobe, pretty JSON, the same error signature

The rulings, and why

instances and private are faithful, not decorative. Each route has one FIFO mailbox consumed by exactly instances worker tasks — the parameter really is the concurrency limit, as in the engines. private=True means what it means there too: callable in-app through PostOffice, while the wire answers 403. A developer who reads the engine documentation forms expectations this host meets.

The bus is an anycast work queue, deliberately hand-built. Each delivery goes to exactly one of N workers and waits its FIFO turn while all are busy. That contract is a work queue, not a broadcast — which is why the implementation is a small mailbox on asyncio primitives rather than a pub/sub construct. Two operations only:

  • deliver — RPC bounded by the caller's ttl; a queued call whose caller already timed out is skipped, never wastefully executed (the dead-work check).
  • publish — drop-n-forget, acknowledged with the engines' 202 shape.

No spill tier, no queue cap, fail fast by deadline. Back-pressure belongs to the tier that owns recovery — the engines' flows and graphs (Rationale). A breach produces the standard 408 envelope (Timeout for N ms), identical to an engine timeout, so flows handle both the same way.

Blocking code cannot hurt the host. Plain def handlers run in a thread-pool executor with trace context carried across; the event loop that serves every other route is never blocked. This is the Python analog of the Java engine's virtual threads: write sequential blocking-style code; the platform makes it safe.

Sync functions compose through a bridge, not a second API. PostOffice.request_sync()/send_sync() submit the same coroutines onto the host loop while blocking only the handler's own worker thread — trace chain unbroken, identical envelope shaping. Misuse teaches: calling the bridge on the event loop, or outside a hosted function, raises a descriptive error instead of deadlocking.

In-memory only. In-flight events die with the process, exactly like the engines' own in-memory bus; at-least-once behavior comes from flow-level retries, not from a leaf journal.

The scope fence

The package intentionally contains no flows, no graphs, no persistence and no pub/sub broadcast. What it carries is deliberately minimal: functions, the primitive bus, the thin client, and the engine-consistent utilities (configuration, logging, trace). Divergence from an engine convention is treated as a bug, not a style choice.

Where to go next

Function Writing Patterns turns this anatomy into day-to-day code.