release-crate-docs: crate docs final pass — doc gate green with -D warnings posture recorded in AGENTS.md, warn(missing_docs) adopted on the four published crates (audit probe 0; Error enum+fields, sqlite substrate fuzz-surface fns filled), accuracy pass fixed core drifts (trim_to wake nuance, ADR-023 §2 domain rules + shared paragraph, QueueOpts gloss, MemStore::new exception) and engine postures (eleven-vs-ten tx-method count, spawn_blocking rationale via sync substrate, one-host cross-process honesty element), spec-side aligned (pg locks family + row-backed wording, mem clock-accessor home), two boundary-wording dispositions recorded for release-contract-window-close (try_recv arms, 8000-byte <= reading), queue round-trip doctest landed in alkstore-mem crate docs; task -> completed with Notes + Summary
This commit is contained in:
1 parent
5f5f6741d5
commit
8e27f4b64b
22 files changed
+289
-49
No files matched your search
+20
-4
@@ -1,3 +1,7 @@
|
||||
/// The crate's error taxonomy (ADR-008 §5): a small closed set of
|
||||
/// contract-meaningful variants plus the opaque [`Error::Database`]
|
||||
/// fallback. Nothing engine-specific ever rides the surface; driver
|
||||
/// detail lives in the source chain of the opaque variant.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
@@ -11,16 +15,25 @@ pub enum Error {
|
||||
/// outbox shape — the id in the notification, the payload in a
|
||||
/// table row.
|
||||
#[error("payload exceeds the engine's notification payload limit (limit: {limit} bytes)")]
|
||||
PayloadTooLarge { limit: usize },
|
||||
PayloadTooLarge {
|
||||
/// The engine's byte limit that was exceeded.
|
||||
limit: usize,
|
||||
},
|
||||
/// A name carrying the reserved `__alkstore_` prefix was supplied
|
||||
/// to a shared-namespace entry point.
|
||||
#[error(
|
||||
"reserved name `{name}`: names carrying the reserved `__alkstore_` prefix are engine-owned"
|
||||
)]
|
||||
ReservedName { name: String },
|
||||
ReservedName {
|
||||
/// The rejected name, as supplied.
|
||||
name: String,
|
||||
},
|
||||
/// A name failed entry-point validation.
|
||||
#[error("invalid name `{name}`")]
|
||||
InvalidName { name: String },
|
||||
InvalidName {
|
||||
/// The rejected name, as supplied.
|
||||
name: String,
|
||||
},
|
||||
/// A schedule spec that doesn't parse under the v1 grammar
|
||||
/// (`@every <n><unit>` with `<n>` a positive integer and `<unit>`
|
||||
/// in `s|m|h|d`).
|
||||
@@ -28,7 +41,10 @@ pub enum Error {
|
||||
/// Rejected by `schedule()` before storage, with no engine round
|
||||
/// trip; the caller fixes the spec string (ADR-009 §2, §6).
|
||||
#[error("invalid schedule spec `{spec}`")]
|
||||
InvalidSpec { spec: String },
|
||||
InvalidSpec {
|
||||
/// The spec string that failed the grammar.
|
||||
spec: String,
|
||||
},
|
||||
/// The `run_schedules` runner's leadership lock was lost mid-run.
|
||||
///
|
||||
/// A distinct, matchable outcome from the clean `Ok(())` a stop
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
//! This crate is the contract artifact: no driver dependencies, ever.
|
||||
//! Engine crates (`alkstore-sqlite`, `alkstore-postgres`) depend on
|
||||
//! this crate and implement its traits.
|
||||
//!
|
||||
//! This crate's docs are part of the contract artifact (ADR-001 §1) —
|
||||
//! every public item carries doc text, and the missing-docs lint is
|
||||
//! part of the standing gates.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod error;
|
||||
mod event_receiver;
|
||||
|
||||
@@ -13,7 +13,9 @@ pub trait Lock: Send {
|
||||
/// namespace — validated at the constructor).
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Renew: a new **full** TTL window from now. `false` = lost it
|
||||
/// Renew: a new **full** TTL window from now. `ttl` is a duration
|
||||
/// argument (ADR-023 §2): `ttl <= 0` is a rejection (opaque `Err`,
|
||||
/// never a silent immediately-expired lease). `false` = lost it
|
||||
/// (expired and re-acquired elsewhere — exclusivity lapses
|
||||
/// silently on expiry; no revocation event; ADR-008 §7). Holders
|
||||
/// of long work must renew within TTL.
|
||||
|
||||
@@ -51,9 +51,11 @@ pub struct EnqueueOpts {
|
||||
/// Opts changes apply to *future enqueues only*.
|
||||
///
|
||||
/// The numeric fields are trusted as given, engine-side — no domain
|
||||
/// validation (ADR-023 §2's domain table covers the trait-surface
|
||||
/// arguments, not these consumer-constructed constants; a
|
||||
/// consumer-obligation note, not a defect). A non-positive
|
||||
/// validation: ADR-023 §2's domain table enumerates its covered
|
||||
/// arguments by call-site kind (extents, `try_lock`/`renew` TTLs,
|
||||
/// `trim_to`'s horizon, enqueue `delay`/`run_at`/`expires`) and does
|
||||
/// not cover these queue-stamp fields (a consumer-obligation note,
|
||||
/// not a defect). A non-positive
|
||||
/// `visibility_timeout_s` stamps claims instantly reclaimable, and
|
||||
/// `max_attempts <= 0` stamps rows that are never claimed — see
|
||||
/// deployment.md's consumer-obligation notes for the per-field
|
||||
|
||||
@@ -41,7 +41,9 @@ pub trait Queue: Send + Sync {
|
||||
|
||||
/// Claim up to `n` rows, same ordering and exclusivity as
|
||||
/// [`Queue::claim_one`]. `worker_id` is consumer-local, non-empty
|
||||
/// only (`InvalidName`).
|
||||
/// only (`InvalidName`). `n` is an extent argument (ADR-023 §2):
|
||||
/// `n <= 0` claims nothing — the empty `Vec`, a value (never an
|
||||
/// error, never the whole queue).
|
||||
fn claim_batch<'a>(
|
||||
&'a self,
|
||||
worker_id: &str,
|
||||
|
||||
+22
-2
@@ -4,7 +4,25 @@
|
||||
//! Constructors and their option structs live in the engine crates
|
||||
//! (ADR-008 §6): the engine's `open` returns this trait, and a store
|
||||
//! handed out by an engine crate honors the full v1 surface. Consumer
|
||||
//! code never branches on engine type.
|
||||
//! code never branches on engine type. (The mem engine's engine-native
|
||||
//! constructor — `MemStore::new()`, no connection string — is the one
|
||||
//! documented exception to the open-prose, ADR-024 §4; the engine
|
||||
//! choice remains a dependency-graph fact made before any store
|
||||
//! exists.)
|
||||
//!
|
||||
//! # Numeric-argument domains (ADR-023 §2)
|
||||
//!
|
||||
//! Caller-supplied numeric arguments are classified by argument kind,
|
||||
//! uniform on every engine (validation at the trait-impl entry point):
|
||||
//! **extents** (`claim_batch`, the stream `read` forms, on the direct
|
||||
//! and tx paths alike) clamp to the empty value result on non-positive
|
||||
//! values — a value, not an error; **durations** (`try_lock`/
|
||||
//! `ttl`, `renew`/`ttl`) reject non-positive values (`Err`) — never a
|
||||
//! silent immediately-expired lease; **boundaries/offsets**
|
||||
//! (`trim_to`'s horizon; enqueue `delay`/`run_at`/`expires`) are
|
||||
//! total — any `i64` is well-defined. Queue/schedule opts-stamp fields
|
||||
//! are not in the domain table (trusted as given; deployment.md's
|
||||
//! consumer-obligation notes carry the per-field symptoms).
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -70,7 +88,9 @@ pub trait Store: Send + Sync {
|
||||
|
||||
/// Acquire a named TTL lock, or fail: `None` = someone holds it
|
||||
/// (no-work is a value, not an error). `owner` is consumer-local
|
||||
/// (non-empty only); `ttl` is seconds. Exclusion lapses silently
|
||||
/// (non-empty only); `ttl` is seconds — a duration argument
|
||||
/// (ADR-023 §2): `ttl <= 0` is a rejection (opaque `Err`, never a
|
||||
/// silent immediately-expired lease). Exclusion lapses silently
|
||||
/// at TTL expiry — holders renew within TTL (ADR-008 §7).
|
||||
fn try_lock<'a>(
|
||||
&'a self,
|
||||
|
||||
@@ -35,7 +35,9 @@ pub trait StreamHandle: Send + Sync {
|
||||
) -> BoxedFuture<'a, Result<i64>>;
|
||||
|
||||
/// Cursor read: events with `offset > ` cursor, `offset ASC`
|
||||
/// (global FIFO — ADR-015 §4), up to `limit`. A read from a
|
||||
/// (global FIFO — ADR-015 §4), up to `limit`. `limit` is an
|
||||
/// extent argument (ADR-023 §2): `limit <= 0` reads nothing —
|
||||
/// the empty `Vec`. A read from a
|
||||
/// trimmed-away region resumes at the trim horizon's first
|
||||
/// remaining row.
|
||||
fn read_since<'a>(
|
||||
@@ -46,7 +48,9 @@ pub trait StreamHandle: Send + Sync {
|
||||
|
||||
/// Cursor read from a consumer's stored checkpoint (absent
|
||||
/// consumer = 0). `consumer` is a consumer-local identifier
|
||||
/// (non-empty only — `InvalidName`).
|
||||
/// (non-empty only — `InvalidName`). `limit` is an extent
|
||||
/// argument (ADR-023 §2): `limit <= 0` reads nothing — the
|
||||
/// empty `Vec`.
|
||||
fn read_from_consumer<'a>(
|
||||
&'a self,
|
||||
consumer: &str,
|
||||
@@ -66,9 +70,14 @@ pub trait StreamHandle: Send + Sync {
|
||||
fn get_offset<'a>(&'a self, consumer: &str) -> BoxedFuture<'a, Result<i64>>;
|
||||
|
||||
/// Bounded growth, consumer-invoked: delete events with
|
||||
/// `offset <= horizon` (exact boundary; ADR-015 §5). Surviving
|
||||
/// `offset <= horizon` (exact boundary; ADR-015 §5). The horizon
|
||||
/// is a boundary argument (ADR-023 §2) — total: a negative
|
||||
/// horizon deletes nothing, idempotently. Surviving
|
||||
/// events keep their offsets (gaps legal, never renumbered); trim
|
||||
/// emits no wake. Returns the deleted-row count.
|
||||
/// emits no dedicated wake and no notify (on SQLite the
|
||||
/// `data_version` watcher may still fire on the trim commit — any
|
||||
/// committed write bumps it; a spurious hint, contract-legal).
|
||||
/// Returns the deleted-row count.
|
||||
fn trim_to<'a>(&'a self, horizon: i64) -> BoxedFuture<'a, Result<i64>>;
|
||||
|
||||
/// Durable consumption: attach a consumer that reads to the
|
||||
|
||||
+5
-2
@@ -107,7 +107,8 @@ pub trait TxHandle: Send {
|
||||
|
||||
/// Read stream events from `offset`, inside the caller's
|
||||
/// transaction (`offset ASC`, global FIFO — ADR-015 §4). Pure
|
||||
/// read. Validates `stream`.
|
||||
/// read. Validates `stream`. `limit` is an extent argument
|
||||
/// (ADR-023 §2): `limit <= 0` reads nothing — the empty `Vec`.
|
||||
fn read_since_tx<'a>(
|
||||
&'a mut self,
|
||||
stream: &str,
|
||||
@@ -117,7 +118,9 @@ pub trait TxHandle: Send {
|
||||
|
||||
/// Read stream events from a consumer's stored checkpoint, inside
|
||||
/// the caller's transaction. Pure read. Validates `stream`;
|
||||
/// `consumer` is consumer-local (non-empty only).
|
||||
/// `consumer` is consumer-local (non-empty only). `limit` is an
|
||||
/// extent argument (ADR-023 §2): `limit <= 0` reads nothing — the
|
||||
/// empty `Vec`.
|
||||
fn read_from_consumer_tx<'a>(
|
||||
&'a mut self,
|
||||
stream: &str,
|
||||
|
||||
@@ -36,11 +36,13 @@ pub enum NameKind {
|
||||
///
|
||||
/// - non-empty (after no trimming — whitespace-only counts as
|
||||
/// non-empty content but is nonetheless rejected below) →
|
||||
/// [`Error::InvalidName`] when empty
|
||||
/// [`Error::InvalidName`](crate::Error::InvalidName) when empty
|
||||
/// - `NameKind::Shared` additionally rejects names carrying
|
||||
/// [`RESERVED_PREFIX`] with [`Error::ReservedName`]
|
||||
/// [`RESERVED_PREFIX`] with
|
||||
/// [`Error::ReservedName`](crate::Error::ReservedName)
|
||||
///
|
||||
/// Whitespace-only names are rejected as [`Error::InvalidName`] for
|
||||
/// Whitespace-only names are rejected as
|
||||
/// [`Error::InvalidName`](crate::Error::InvalidName) for
|
||||
/// both kinds: they are never a meaningful name on any engine.
|
||||
pub fn validate_name(kind: NameKind, name: &str) -> crate::Result<()> {
|
||||
if name.is_empty() || name.trim().is_empty() {
|
||||
|
||||
Reference in new issue
Block a user