Skip to content

Latest commit

 

History

History
81 lines (72 loc) · 4.03 KB

File metadata and controls

81 lines (72 loc) · 4.03 KB

Architecture

The SDK is split into three layers so AWS integration and JSON parsing do not leak into the replay hot path.

  1. Core — protocol values, deterministic identity, replay context, checkpoint state, operations, serializers, and handler result mapping.
  2. Service adapter — implements service_client using the AWS SDK for C++ Lambda durable APIs. One virtual dispatch occurs per network request, not per local model or serializer operation.
  3. Runtime adapter — translates Lambda invocation JSON/context into invocation_input, attaches function/tenant metadata, calls run, and writes the invocation response.
  4. Local runner — implements the same service_client boundary in memory, advances a virtual clock, and repeatedly invokes the unchanged handler.

The service and runtime adapters are optional build targets. Applications that use a custom transport or embed the core do not link either AWS dependency.

Replay flow

Lambda invocation
  -> runtime adapter
  -> execution_state loads all history pages
  -> durable_context reserves deterministic operation IDs
  -> operation finds checkpoint by ID
       succeeded: deserialize and skip user code
       pending: suspend invocation
       absent/ready: execute and checkpoint
       incompatible: fail closed
  -> child contexts repeat the same flow in an ID-prefixed namespace
  -> parallel/map reserve every branch ID before bounded workers start
  -> contending branch checkpoints are flat-combined into atomic API batches
  -> flow validates its DAG, prunes unreachable nodes, then pre-reserves every
     reachable node context before scheduling ready waves
  -> handler returns SUCCEEDED, PENDING, FAILED, or RETRY

Performance rules

  • No dependency is required by the core library.
  • Invocation JSON is parsed directly into protocol models. Unknown values are skipped recursively without allocating a general-purpose DOM.
  • Recursive-level inspection and replacement use a targeted top-level JSON object scanner that preserves all unrelated value slices.
  • History lookup accepts std::string_view, performs no temporary string allocation, and returns an immutable shared snapshot safe across concurrent state refreshes.
  • Known wire enums store only an enum value; storage is allocated only for an unknown future value.
  • Serializers and durable callables are templates, allowing inlining.
  • Declarative flow uses type erasure only at graph boundaries. Ordinary step, callback, map, and invoke hot paths remain statically dispatched.
  • The local runner uses no scheduler thread and performs no sleeps. Timer transitions advance directly to the next virtual event.
  • Exceptions represent invocation control flow and failures; ordinary transport results use std::expected.
  • Callback and chained-invoke results use std::optional to represent a successful backend operation without a payload.
  • Sequential checkpoints use a direct call path. Parallel/map scopes enable a flat-combining queue only while workers are active, avoiding queue and background-thread overhead for ordinary workflows.
  • Benchmarks must retain their checksum and run in optimized builds to prevent dead-code elimination.

Safety invariants

  • User code inside a durable step cannot create nested durable operations.
  • At-most-once interrupted steps never rerun the same attempt.
  • A replay identity mismatch fails before user code executes.
  • Unknown future statuses fail closed when their semantics are unsafe.
  • A missing checkpoint token is accepted only after terminal execution updates.
  • Callback failures are deferred from creation to callback_handle::result() so replay always executes code between those two boundaries.
  • A suspended operation inside a child context never writes a false child failure checkpoint.
  • Checkpoint-token mutation is serialized, and history entries are immutable snapshots replaced atomically under a write lock.
  • Parallel workers operate on forked durable contexts whose operation counters were pre-reserved on the caller thread.