notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit a613cc7e18c0f56beec4a131ecc9d67b5183390a
parent 5be8bc0774fad8872d0d66c68ad4fbbd0f3c7531
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 24 Feb 2026 17:27:18 -0800

negentropy: wait for fetched events to be ingested before next round

The next negentropy round was firing on the very next frame (~16ms)
after trigger_now(), but the REQ responses from the previous round
need a full network round-trip to arrive and be ingested into ndb.
This caused every round to see identical local state and re-identify
the same missing events.

Track fetched event IDs and gate the next round on confirming at
least one has appeared in ndb via get_note_by_id.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mcrates/enostr/src/negentropy.rs | 24++++++++++++++++++++++++
1 file changed, 24 insertions(+), 0 deletions(-)

diff --git a/crates/enostr/src/negentropy.rs b/crates/enostr/src/negentropy.rs @@ -93,6 +93,9 @@ pub struct NegentropySync { sync_requested: bool, /// IDs accumulated across multi-round reconciliation. need_ids: Vec<[u8; 32]>, + /// IDs sent via REQ that we're waiting to see ingested into ndb + /// before starting the next round. + pending_fetch_ids: Vec<[u8; 32]>, } impl NegentropySync { @@ -103,6 +106,7 @@ impl NegentropySync { neg: None, sync_requested: false, need_ids: Vec::new(), + pending_fetch_ids: Vec::new(), } } @@ -154,6 +158,24 @@ impl NegentropySync { } } + // Wait for previously-fetched events to be ingested into ndb + // before starting the next round. Without this, the next round + // starts before the REQ responses arrive, causing the same + // events to be identified as missing every round. + if self.sync_requested && !self.pending_fetch_ids.is_empty() { + if let Ok(txn) = Transaction::new(ndb) { + if ndb.get_note_by_id(&txn, &self.pending_fetch_ids[0]).is_ok() { + tracing::info!( + "negentropy: fetched events ingested, proceeding with next round" + ); + self.pending_fetch_ids.clear(); + } else { + // Events not yet ingested — wait for next frame + return fetched; + } + } + } + // Initiate sync if requested and idle if self.sync_requested && self.state == SyncState::Idle { self.sync_requested = false; @@ -261,6 +283,7 @@ impl NegentropySync { if count > 0 { tracing::info!("negentropy: fetching {} missing events", count); Self::fetch_missing(&missing, pool, relay_url); + self.pending_fetch_ids = missing; } count } @@ -278,6 +301,7 @@ impl NegentropySync { self.sub_id = None; self.neg = None; self.need_ids.clear(); + self.pending_fetch_ids.clear(); } fn fetch_missing(ids: &[[u8; 32]], pool: &mut RelayPool, relay_url: &str) {