v0.3.0 (2026-06-17)
Enhancements
Transactional callback variants —
handle_cast_tx/3,handle_call_tx/4, andhandle_info_tx/3are optional callback variants that receive atx_ctx()map as their first argument (#{td := tenant(), tuid := tuid()}). When exported, the_txvariant is preferred over the plain variant. Thetdvalue is the open backend transaction+directory pair for the current consume cycle, allowing callbacks to read or write arbitrary keys atomically alongside the module-state commit. The newtx_ctx/0type is exported fromdgen_server.lock_timeoutoption fordgen_server— Sets the maximum milliseconds a distributed lock may be held before other consumers treat it as stale and clear it. Previously a consumer killed (SIGKILL / VM abort) while holding the lock would block all other consumers permanently if no new messages arrived to trigger a re-check. Withlock_timeoutset, a backstop timer is scheduled whenever a live lock is observed: after the remaining timeout the consumer re-evaluates staleness and clears the lock if the holder has not done so.infinity(the default) preserves the previous behaviour.dgen_registryuseslock_timeout: 6_000.dgen_registry— Experimental. An OTP-compatible process registry backed by the configured storage backend. Implements the four-function{via, dgen_registry, {Name, LogicalName}}contract so standard OTP processes (gen_server,gen_statem, etc.) can be started and addressed by name across an Erlang cluster. Writes and consistent reads go through an elected leader;whereis_name/1(used by OTP via-tuple routing) is a snapshot read from the local member's in-memory map with no backend round-trip. The leader monitors registered pids and propagates{name_unregistered}to followers on process exit. Start withdgen_registry:start_link(Name, Tenant)and supply a supervisor name as the first argument tostart_link/3to embed it in an existing supervision tree.Partition recovery is reliable: each join carries a unique token so stale
member_downmessages from before a reconnect are discarded rather than undoing the rejoin. Leader transitions during a partition no longer trigger automatic distribution reconnect.
Breaking changes
DGenServerrenamed toDGen.Server—use DGenServerbecomesuse DGen.Server; allDGenServer.*call sites becomeDGen.Server.*. The module now lives atlib/dgen/server.ex.handle_locked/3→handle_locked/4— adb_ctx()map is now prepended as the first argument, matching the convention ofhandle_call_tx/4and friends.db_ctx()carries#{db := tenant(), tuid := tuid()}wheredbis the DB-level tenant (not a transaction); usedgen_backend:transactional/2inside the callback to open explicit transactions. Update allhandle_lockedimplementations to accept the new first argument.
v0.2.0 (2026-04-05)
Enhancements
Dead-letter queue — opt-in poison-message handling via the new
dead_letter_thresholdstart option (defaultinfinity, i.e. disabled). When set to a positive integer, messages that crash the consumer that many times are moved to a dead-letter queue (DLQ) stored in FoundationDB instead of being retried indefinitely. Forcallmessages the blocked caller raises{dead_letter, N}. The optionalhandle_dead_letter/2callback is invoked when a message is dead-lettered.dgen_server:outbox_cast/1,2— returns aCast = fun((Tx, Message) -> ok)closure for enqueuing a cast message atomically within the caller's own FDB transaction. Call it before opening the transaction as a preparatory step; the closure captures the queue directory and identifier internally. Intended for callers already operating directly with a backend transaction who need to compose the enqueue with other writes atomically.
v0.1.0 (2026-02-22)
Initial release.