pub trait OpDispenser: Send + Sync {
// Required method
fn execute<'a>(
&'a self,
cycle: u64,
ctx: &'a ExecCtx<'a>,
) -> Pin<Box<dyn Future<Output = Result<OpResult, ExecutionError>> + Send + 'a>>;
// Provided methods
fn describe(&self) -> Option<String> { ... }
fn describe_resolved(&self, wires: &dyn WireSource) -> Option<String> { ... }
fn canonical_kernel(&self) -> Option<&Arc<dyn Kernel>> { ... }
fn adapter_metrics(&self) -> Vec<(String, Labels, MetricValue)> { ... }
fn status_counters(&self) -> Vec<(&str, u64)> { ... }
fn rows_per_op(&self) -> usize { ... }
fn inner_dispenser(&self) -> Option<&dyn OpDispenser> { ... }
}Expand description
A per-template op factory. Created at init time by the adapter’s
map_op(), called per-cycle to bind values and execute operations.
The dispenser captures template-specific state (prepared statement, field names, bind-point indices, metrics) so the per-cycle path is minimal: bind resolved values and execute.
Dispensers are shared across fibers and must be thread-safe.
Required Methods§
Sourcefn execute<'a>(
&'a self,
cycle: u64,
ctx: &'a ExecCtx<'a>,
) -> Pin<Box<dyn Future<Output = Result<OpResult, ExecutionError>> + Send + 'a>>
fn execute<'a>( &'a self, cycle: u64, ctx: &'a ExecCtx<'a>, ) -> Pin<Box<dyn Future<Output = Result<OpResult, ExecutionError>> + Send + 'a>>
Execute an operation for the given cycle.
The ctx bundle carries:
ctx.fields— op-field substitution view for the inner adapter (positional / by-name access matching the prepared statement).ctx.pulls— wrapper-facing handle-indexed view of Polydat values (used by validation / conditional / throttle wrappers; adapters ignore this).
See SRD 32 §“ExecCtx — cycle-time bundle” for the design.
Provided Methods§
Sourcefn describe(&self) -> Option<String>
fn describe(&self) -> Option<String>
One-line description of the op this dispenser
represents — typically the statement text /
request shape with bind-point placeholders left
unresolved ({table}, {key}, ?, …).
Used by the runtime’s error-capture path to attach
the actual op identity to a phase-stop diagnostic.
Without this, an error like [validation_failed] op 'indexes_present_cass' … only names the op
template; the operator can’t see what statement
the dispenser is actually firing without grepping
the workload yaml. With this hooked, the captured
reason carries the rendered statement so the
operator can immediately tell whether (a) it’s the
wrong dialect’s branch firing, (b) the bindpoints
resolved to something unexpected, (c) the
statement is malformed in the workload, etc.
Returns None for dispensers whose op shape isn’t
usefully one-lineable (composed wrappers that
delegate, dispensers whose body is multi-paragraph
HTTP, etc.). The runtime falls through to the
inner-dispenser chain when this returns None.
Default: delegate to inner dispenser if any, else
None.
Sourcefn describe_resolved(&self, wires: &dyn WireSource) -> Option<String>
fn describe_resolved(&self, wires: &dyn WireSource) -> Option<String>
Render the actual op the dispenser would fire for the given cycle — the dryrun-equivalent view of the statement after every bind point is interpolated.
Pairs with Self::describe: describe() returns the
op-template (placeholders intact) so the operator can match
the failure to the workload yaml; describe_resolved(wires)
returns what was actually sent for this cycle, so the
operator can immediately tell whether bindpoints resolved to
expected values, whether the wrong dialect’s branch fired,
whether quoting / escaping is broken, etc.
SRD-68 Push 5: takes the dispenser’s bound WireSource
(same surface adapters use at cycle time) so the rendered
view comes from the canonical resolution path — no
synthesis-layer ResolvedFields detour.
Returns None when the dispenser can’t usefully render the
resolved form (e.g. opaque request bodies, dispensers without
per-cycle interpolation). The default delegates to the inner
dispenser; leaf dispensers should override when they have a
useful per-cycle rendering.
Sourcefn canonical_kernel(&self) -> Option<&Arc<dyn Kernel>>
fn canonical_kernel(&self) -> Option<&Arc<dyn Kernel>>
SRD-68 invariant I-3 — the dispenser’s canonical Polydat Kernel,
established at construction by map_op from its parent — the
parent itself, or a kernel bound under it. Any engine: the
executor finds the program to bind per fiber by program_id.
Returns None for dispensers that don’t own a kernel
(adapters with no Polydat needs, or wrappers that delegate to
an inner dispenser).
The executor walks Some returns at fiber spawn to
materialise per-fiber subscope kernels — one per dispenser,
indexed parallel to the dispenser registry. At cycle time
the firing fiber’s slot for this dispenser is handed in
via ExecCtx::wires so cycle-time reads stay on the SRD-68
I-1 single resolution surface.
Default: delegate to inner dispenser if any, else None.
Sourcefn adapter_metrics(&self) -> Vec<(String, Labels, MetricValue)>
fn adapter_metrics(&self) -> Vec<(String, Labels, MetricValue)>
Snapshot adapter-specific metrics for inclusion in the capture snapshot.
Called by the metrics scheduler alongside the standard activity
metrics. Adapters return additional (family_name, labels, MetricValue) triples that represent adapter-internal state
(e.g., rows/s for batched CQL). These appear in the summary
report.
The OpenMetrics-shaped runtime model lives under nmbrs_metrics::snapshot
— adapters typically build MetricValue::Counter /
MetricValue::Histogram / MetricValue::Gauge directly.
Default: no additional metrics.
Sourcefn status_counters(&self) -> Vec<(&str, u64)>
fn status_counters(&self) -> Vec<(&str, u64)>
Adapter-specific status line entries (cumulative, non-destructive read).
Unlike adapter_metrics() which snapshots delta timers, this method
returns cumulative counters safe to read from the progress thread
without interfering with the metrics pipeline.
Returns (display_name, cumulative_count) pairs.
A name with a leading underscore (_batch_writes) is an INTERNAL
counter: published only to back a derived display metric (the
rows/batch average divides rows_inserted by _batch_writes), it is
looked up by name for that computation but never rendered as its own
<name>/s throughput chip. See
crate::readout_context::is_internal_counter — the single predicate
every chip-rendering surface filters on.
Default: delegates to inner dispenser (for wrapper chains).
Sourcefn rows_per_op(&self) -> usize
fn rows_per_op(&self) -> usize
The op’s uniform per-invocation cursor consumption — how many
consecutive wire ordinals one execute call reads and covers.
The executor drives the phase cursor with Σ rows_per_op over
the stanza’s ops (instead of the raw stanza length) and hands
each op a contiguous sub-run of exactly this size, so a batch
op that reads N rows per call also advances the cursor by
N — consecutive stanzas then cover disjoint ordinal runs
(SRD-22 “phase extent = cursor exhaustion”, cover-once). A
batch dispenser overrides this to return its fixed stride N;
every ordinary op keeps the default 1 (identical to the
pre-batching model).
The default delegates to the inner dispenser so a wrapped
batch op (retry / result / metrics layers) still reports its
leaf stride — the executor sees the outermost wrapper, and the
stride must reach it through the chain (mirrors describe /
adapter_metrics). Leaves with no inner fall through to 1.
This is the NOMINAL stride (what sets reserve(N)); at the
cursor tail the reservation may be shorter, and the executor
passes the actual (possibly-short) run length via
crate::fixture::ExecCtx::run_len so the op inserts exactly
the ordinals reserved — never over-reading, never dropping the
remainder.
Sourcefn inner_dispenser(&self) -> Option<&dyn OpDispenser>
fn inner_dispenser(&self) -> Option<&dyn OpDispenser>
Returns the wrapped dispenser when this is a
wrapper, None when this is a leaf (the adapter’s
base dispenser — e.g. CQL raw / prepared / batch,
HTTP, stdout, plotter, …).
Default returns None, which is correct for every
leaf. Adapter-base dispensers should rely on the
default — they have no inner to expose, and asking
them to write fn inner_dispenser(&self) -> None
is pure boilerplate.
Wrappers MUST override to return
Some(self.inner.as_ref()). Several pieces of
cross-cutting machinery walk this chain:
adapter_metricsandstatus_countersdelegate through wrapper layers via the default implementations on this trait, which callinner_dispenser()to find the wrapped layer.describe()walks inward to surface the runtime op shape (CQL statement text) for error-context dumps; missing this on a wrapper silently breaks the walk and the error loses its op-shape line.
The WrappingDispenser marker trait below is
the type-system signal that flags “this is a
wrapper”; wrappers should implement both. Future
composition machinery (SRD-32a) will use the
WrappingDispenser bound to require the override
at the registration boundary.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".