Dev.to · 2 min read

Zenoh's put is fire-and-forget, get isn't — a read-after-write race in Elixir

Zenoh's put is fire-and-forget, get isn't — a read-after-write race in Elixir

This English version is an AI translation of my original article on Qiita (in Japanese). Background I've been experimenting with Zenoh via its Elixir bindings, Zenohex, not for its usual pub/sub use case but for its put/get storage feature. It mostly worked, except every so the state I picked back up was one step behind. Digging into why turned into a fun rabbit hole, so here's the writeup. Reproducing it To keep things simple, strip out the GenServer part entirely and just loop put immediately followed by get on the same key: {:ok, session_id} = Zenohex.Session.open(config) Enum.each(1..2000, fn i -> payload = Integer.to_string(i) :ok = Zenohex.Session.put(session_id, key, payload) {:ok, replies} = Zenohex.Session.get(session_id, key, 3_000, consolidation: :latest) case Enum.find(replies, &match?(%Zenohex.Sample{}, &1)) do %Zenohex.Sample{payload: ^payload} -> :ok %Zenohex.Sample{payload: other} -> IO.puts("stale! put #{payload} but got #{other}") nil -> IO.puts("no reply at all") end end) Out of 2000 iterations, a small fraction print stale! — about 78 (3.9%) in one run. The interesting part: querying again immediately afterward almost always returns the correct value (the fastest I measured was a single extra get about 1ms later). So it's not that the value disappears — there's just a small window of lag before the write is actually visible. Why Zenohex.Session.put/4 is a thin Rustler wrapper around zenoh-rust's put. Looking at the NIF implementation: fn session_put(...) -> rustler::NifResult { ... publication_builder .apply_opts(opts)? .wait() //

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News