Skip to content

aimdb-sync: collapse the bridge onto the block_on seam #200

Description

@lxsaah

Background

Most of the sync bridge is already unnecessary. The core API is synchronous on both ends — AimDb::produce() / subscribe() are sync (aimdb-core/src/builder.rs:946, :963), Producer::try_produce is non-blocking, and the buffer reader exposes sync try_recv() alongside async recv() (aimdb-core/src/buffer/traits.rs:179, :189).

Operation Status in core Bridge needed?
Produce / non-blocking produce db.produce / try_produce sync No — call directly
Non-blocking consume reader.try_recv() sync No — poll directly
Blocking consume only reader.recv().await Yes — the only runtime-specific piece

So this is simplification, not re-architecture: lean on the sync API, isolate one small seam.

The block_on seam

aimdb-executor::RuntimeOps has no block_on/spawn (aimdb-executor/src/ops.rs:51), so the seam is net-new — but it lives in aimdb-sync, not widened into RuntimeOps.

  • Only block_on is needed, never spawn: producer and non-blocking paths call the sync API directly; blocking consume blocks the calling thread.
  • The std / embassy features are mutually exclusive per build, so the waiter is a concrete #[cfg]-selected type, not a trait object — no object-safety or generic-dispatch problem.
  • #[cfg(feature = "std")] → a waiter holding the tokio::runtime::Handle:
    • get()handle.block_on(reader.recv())
    • get_with_timeout(d)handle.block_on(async { tokio::time::timeout(d, reader.recv()).await }). The timed variant must go through the runtime so the tokio timer is in context — this is why a naive futures::block_on will not do.

Handle::block_on is called from the user's sync thread, never from inside the runtime, so this is valid.

Task

  • Define the block_on seam as a concrete #[cfg(feature = "std")] waiter holding the Handle.
  • Rewrite SyncProducer to call db.produce / Producer::try_produce directly — drop the tokio::mpsc/oneshot round-trip, the forwarder task, and the stored runtime handle for produce.
  • Rewrite SyncConsumer to hold the Box<dyn BufferReader<T> + Send> directly and use the seam for get() / get_with_timeout(); try_get()reader.try_recv(). Delete the per-consumer forwarder task and the std::sync::mpsc::sync_channel.
  • Keep the runtime thread + handle + detach/Drop lifecycle — it still hosts the executor driving runner.run().
  • Re-audit unsafe impl Send/Sync for AimDbHandle (handle.rs:653) against the much smaller surface. If the justification no longer holds, or is no longer needed, say so explicitly in the PR.

What this does not remove

  • The DB's async stages (sources, transforms, taps, connectors) still need an executor driving runner.run(). On std that stays the existing thread.
  • Blocking consume still needs a real park/wake primitive per runtime. The seam hides it, it does not eliminate it.

Verification

cargo test -p aimdb-sync
make check

The blocking-API characterization tests must pass unchanged — they are written against the public API precisely so they can arbitrate this rewrite. examples/sync-api-demo behaves identically.

Files

  • aimdb-sync/src/{handle,producer,consumer}.rs
  • Reference: aimdb-core/src/builder.rs:946/:963, aimdb-core/src/buffer/traits.rs:189, aimdb-executor/src/ops.rs:51

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions