Remove latest snapshot#119
Merged
Merged
Conversation
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.
…ds without buffer
…imdb-tokio-adapter, and aimdb-wasm-adapter with new features, fixes, and breaking changes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch lands two milestones plus follow-up test coverage:
latest_snapshotmutex and serves point-in-time reads (latest()/ AimXrecord.get) directly from the buffer via a newDynBuffer::peek().json-serializefeature backed by a newcodecmodule, soRecordValue::as_json()works onno_std + alloc, not juststd.peek()for Embassy buffers (fixing alatest()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_snapshotwas a redundantArc<Mutex<Option<T>>>updated on everyproduce()— 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 acrosstyped_record.rs. M15 deletes it in favor of reading the buffer's native storage. M16 then removes the last reason JSON wasstd-only (the closure-based serializer pair) by moving to a type-erased codec that runs onalloc.What changed
M15 — buffer-native point-in-time reads
DynBuffer::peek(&self) -> Option<T>(defaultNone); implemented by the Tokio and Embassy adapters per buffer type.latest()and AimXrecord.get(latest_json) readbuffer.peek()instead of the snapshot mutex.TypedRecord::produceremoved; all writes go throughWriteHandle::pushviawriter_handle(). As a side effect AimXset_from_jsonnow marks metadata updated (previously skipped).build()rejects a.with_remote_access()record that has no buffer (previously a silent runtime no-op).SingleLatestpush switched fromwatch::Sender::sendtosend_replace— fixes a latent bug where a value produced before any subscriber attached was dropped.producer_service→producer(set_producer,has_producer).M16 — JSON codec behind
json-serializeaimdb-core::codecmodule:RemoteSerialize(blanket-impl'd for everyserdeSerialize + DeserializeOwned), object-safeJsonCodec<T>, zero-sizedSerdeJsonCodec.with_remote_access()is gated onjson-serialize(transitively enabled bystd) and stores a singleArc<dyn JsonCodec<T>>instead of serializer/deserializer closures.with_read_only_serialization()removed.Embassy adapter & CI (Closes #85)
EmbassyBuffer::peek():SingleLatestviaWatch::try_get(),MailboxviaChannel::try_peek(),SpmcRing→None— matching Tokio semantics and restoringlatest()on embedded.join_queuemodule gate relaxed fromembassy-runtimetoembassy-sync(the runtime-specificJoinFanInRuntimeimpl keeps its own gate), so its channel-only tests run on the host without pullingembassy-executor's cortex-m assembly.make testnow runscargo 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_handlerand a trivialembassy-time-driversatisfy the host link targets.EmbassyBufferdoc example (referenced the removedBufferBackendtrait).Supporting changes
aimdb-wasm-adapter:record.setpath routes throughdb.producer::<T>(key)?.produce(val)(no JS API change).buffer()/buffer_sized()now record theBufferCfg, sobuffer_info()reports real type/capacity in the dependency graph onno_stdtoo.examples/remote-access-demo: client updated to reflectrecord.get→not_foundon theSpmcRingrecord.aimdb-core/aimdb-tokio-adapter/aimdb-embassy-adapter/aimdb-wasm-adapter.Breaking changes & migration
.with_remote_access()with no buffer now failsbuild(). Add a buffer:reg.buffer(BufferCfg::SingleLatest).with_remote_access().record.get/latest()on anSpmcRingrecord returnsnot_found/None. Rings keep per-consumer history with no canonical latest — userecord.drain(history) orrecord.subscribe(live).SingleLatest/Mailboxunaffected.with_read_only_serialization()removed. Usewith_remote_access()(also requiresDeserializeOwned).no_std + alloccallers must enable thejson-serializefeature to usewith_remote_access()/as_json().stdenables it automatically.TypedRecord::produce/set_producer_service/has_producer_servicemust move towriter_handle().push()/set_producer/has_producer. The public.source()/.produce()(onAimDb/Producer) API is unchanged.Testing
make check(CI's gate) — fmt, clippy-D warnings, full test suite, embeddedcargo check, wasm, deny.aimdb-tokio-adapterpeek()unit tests +tests/remote_access_validation.rs.aimdb-embassy-adapterruns on host — 15 unit tests (buffer/error/join-queue) + 8 doctests.cargo check --target thumbv7em-none-eabihf --features embassy-runtime.Closes