Skip to content

Remove latest snapshot#119

Merged
lxsaah merged 7 commits into
mainfrom
remove-latest-snapshot
May 28, 2026
Merged

Remove latest snapshot#119
lxsaah merged 7 commits into
mainfrom
remove-latest-snapshot

Conversation

@lxsaah
Copy link
Copy Markdown
Contributor

@lxsaah lxsaah commented May 28, 2026

Summary

This branch lands two milestones plus follow-up test coverage:

  • M15 (Design 031) — removes the per-record latest_snapshot mutex and serves point-in-time reads (latest() / AimX record.get) directly from the buffer via a new DynBuffer::peek().
  • M16 (Design 032) — extracts JSON serialization into a json-serialize feature backed by a new codec module, so RecordValue::as_json() works on no_std + alloc, not just std.
  • Embassy adapter + CI — implements peek() for Embassy buffers (fixing a latest() regression introduced by M15) and makes the Embassy adapter's unit tests + doctests run on the host in CI. Closes Embassy join-queue tests not exercised by CI #85.

Diff vs main: ~24 files, roughly +1800 / −540 (core/adapters), plus the design docs and changelog updates.

Motivation

latest_snapshot was a redundant Arc<Mutex<Option<T>>> updated on every produce() — a second copy of state the buffer already holds. It cost a lock + clone on the hot path and forced ~10 #[cfg(std/no_std)] splits across typed_record.rs. M15 deletes it in favor of reading the buffer's native storage. M16 then removes the last reason JSON was std-only (the closure-based serializer pair) by moving to a type-erased codec that runs on alloc.

What changed

M15 — buffer-native point-in-time reads

  • New DynBuffer::peek(&self) -> Option<T> (default None); implemented by the Tokio and Embassy adapters per buffer type.
  • latest() and AimX record.get (latest_json) read buffer.peek() instead of the snapshot mutex.
  • TypedRecord::produce removed; all writes go through WriteHandle::push via writer_handle(). As a side effect AimX set_from_json now marks metadata updated (previously skipped).
  • build() rejects a .with_remote_access() record that has no buffer (previously a silent runtime no-op).
  • Tokio SingleLatest push switched from watch::Sender::send to send_replace — fixes a latent bug where a value produced before any subscriber attached was dropped.
  • Rename producer_serviceproducer (set_producer, has_producer).

M16 — JSON codec behind json-serialize

  • New aimdb-core::codec module: RemoteSerialize (blanket-impl'd for every serde Serialize + DeserializeOwned), object-safe JsonCodec<T>, zero-sized SerdeJsonCodec.
  • with_remote_access() is gated on json-serialize (transitively enabled by std) and stores a single Arc<dyn JsonCodec<T>> instead of serializer/deserializer closures.
  • with_read_only_serialization() removed.

Embassy adapter & CI (Closes #85)

  • EmbassyBuffer::peek(): SingleLatest via Watch::try_get(), Mailbox via Channel::try_peek(), SpmcRingNone — matching Tokio semantics and restoring latest() on embedded.
  • join_queue module gate relaxed from embassy-runtime to embassy-sync (the runtime-specific JoinFanInRuntime impl keeps its own gate), so its channel-only tests run on the host without pulling embassy-executor's cortex-m assembly.
  • make test now runs cargo test -p aimdb-embassy-adapter --no-default-features --features "alloc,embassy-sync,embassy-time" (15 unit tests + doctests). Test-only no-op #[defmt::global_logger]/panic_handler and a trivial embassy-time-driver satisfy the host link targets.
  • Fixed a stale EmbassyBuffer doc example (referenced the removed BufferBackend trait).

Supporting changes

  • aimdb-wasm-adapter: record.set path routes through db.producer::<T>(key)?.produce(val) (no JS API change).
  • Buffer config plumbing: registrar buffer()/buffer_sized() now record the BufferCfg, so buffer_info() reports real type/capacity in the dependency graph on no_std too.
  • examples/remote-access-demo: client updated to reflect record.getnot_found on the SpmcRing record.
  • Changelogs updated across root + aimdb-core / aimdb-tokio-adapter / aimdb-embassy-adapter / aimdb-wasm-adapter.

Breaking changes & migration

  • .with_remote_access() with no buffer now fails build(). Add a buffer: reg.buffer(BufferCfg::SingleLatest).with_remote_access().
  • record.get / latest() on an SpmcRing record returns not_found / None. Rings keep per-consumer history with no canonical latest — use record.drain (history) or record.subscribe (live). SingleLatest / Mailbox unaffected.
  • with_read_only_serialization() removed. Use with_remote_access() (also requires DeserializeOwned).
  • no_std + alloc callers must enable the json-serialize feature to use with_remote_access() / as_json(). std enables it automatically.
  • Direct callers of TypedRecord::produce / set_producer_service / has_producer_service must move to writer_handle().push() / set_producer / has_producer. The public .source() / .produce() (on AimDb/Producer) API is unchanged.

Testing

  • make check (CI's gate) — fmt, clippy -D warnings, full test suite, embedded cargo check, wasm, deny.
  • New: aimdb-tokio-adapter peek() unit tests + tests/remote_access_validation.rs.
  • New: aimdb-embassy-adapter runs on host — 15 unit tests (buffer/error/join-queue) + 8 doctests.
  • Embedded target verified: cargo check --target thumbv7em-none-eabihf --features embassy-runtime.

Closes

lxsaah and others added 5 commits May 27, 2026 20:38
Replaces the per-record latest_snapshot Mutex with a new DynBuffer::peek()
method that reads from the buffer's native storage (watch::Sender slot for
SingleLatest, Mutex<Option<T>> slot for Mailbox, None for SPMC Ring).

The snapshot was a redundant second copy of every produced value, updated
on two divergent code paths (TypedRecord::produce and RecordWriter::push),
and the only mutex on the SingleLatest hot path. After this change the
write path is unified through RecordWriter::push() and the SingleLatest
produce path is lock-free.

Also fixes a latent bug in TokioBuffer's Watch::push: it used tx.send()
which returns Err and silently drops the value when no receivers exist.
Switched to tx.send_replace() which always updates the slot — the snapshot
had been masking this for record.get callers.

Behaviour changes (documented in design 031 §Breaking Changes):
- record.get returns not_found on SPMC Ring and bufferless records
- record.get on Mailbox after record.drain returns not_found (slot was
  taken; previously the independent snapshot survived)
- metadata mark_updated now fires on AimX record.set and the WASM adapter
  produce paths, which previously bypassed it

Design: docs/design/031-M15-remove-latest-snapshot.md
Verified: make check (fmt, clippy, std/no_std/embedded/wasm builds, deny)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Added `RemoteSerialize` trait for JSON serialization/deserialization, blanket-implemented for all `serde` types.
- Implemented `JsonCodec<T>` trait for type-erased JSON encoding/decoding.
- Introduced `SerdeJsonCodec` as a zero-sized implementation of `JsonCodec`.
- Updated `TypedRecord` to use a single `remote_codec` field instead of separate serializer/deserializer closures.
- Modified `with_remote_access` to require `RemoteSerialize` and enable JSON codec installation.
- Updated `RecordValue` to utilize the new codec for JSON serialization.
- Removed the deprecated `with_read_only_serialization` method.
- Added documentation for the new codec functionality and its usage.
@lxsaah lxsaah linked an issue May 28, 2026 that may be closed by this pull request
lxsaah added 2 commits May 28, 2026 18:52
…imdb-tokio-adapter, and aimdb-wasm-adapter with new features, fixes, and breaking changes
@lxsaah lxsaah marked this pull request as ready for review May 28, 2026 19:14
@lxsaah lxsaah requested a review from Copilot May 28, 2026 19:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@lxsaah lxsaah requested a review from Copilot May 28, 2026 19:31
@lxsaah lxsaah self-assigned this May 28, 2026
@lxsaah lxsaah added the 🏗️ core Core engine work label May 28, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@lxsaah lxsaah merged commit 1ebf308 into main May 28, 2026
9 of 11 checks passed
@lxsaah lxsaah deleted the remove-latest-snapshot branch May 28, 2026 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🏗️ core Core engine work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Embassy join-queue tests not exercised by CI

2 participants