Currently get_metadata() acquires the mutex and clones the full metadata struct on every call. The PyO3 layer then converts it to a Python dict, and the Python wrapper constructs a Pydantic model from it — three allocations per access.
The old pure-Python implementation cached self._metadata as a Pydantic model and only updated it on mutations. Internally the Rust lib already keeps metadata behind a Mutex and updates it on writes, so the data is effectively cached — but every read still pays for the clone + FFI conversion.
This is in the same spirit as #5 (porting JS optimizations) — the JS MemoryStorage also avoids redundant metadata re-reads. A lightweight approach: track a generation counter on the Rust side and expose it so the binding layer can skip re-fetching when nothing changed.
Currently
get_metadata()acquires the mutex and clones the full metadata struct on every call. The PyO3 layer then converts it to a Python dict, and the Python wrapper constructs a Pydantic model from it — three allocations per access.The old pure-Python implementation cached
self._metadataas a Pydantic model and only updated it on mutations. Internally the Rust lib already keeps metadata behind aMutexand updates it on writes, so the data is effectively cached — but every read still pays for the clone + FFI conversion.This is in the same spirit as #5 (porting JS optimizations) — the JS
MemoryStoragealso avoids redundant metadata re-reads. A lightweight approach: track a generation counter on the Rust side and expose it so the binding layer can skip re-fetching when nothing changed.