Skip to content

fix(signal): use FIFO eviction for sent-timestamp tracking - #3692

Closed
dieutx wants to merge 1 commit into
NousResearch:mainfrom
dieutx:fix/signal-timestamp-fifo-eviction
Closed

fix(signal): use FIFO eviction for sent-timestamp tracking#3692
dieutx wants to merge 1 commit into
NousResearch:mainfrom
dieutx:fix/signal-timestamp-fifo-eviction

Conversation

@dieutx

@dieutx dieutx commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Signal's echo-back filter (_recent_sent_timestamps) uses set.pop() to evict old entries when the set exceeds 50 items. Since Python sets are unordered, pop() removes an arbitrary element — it might evict the newest timestamp while keeping a stale one from hours ago. Over time this degrades the echo-back filter: legitimate sent timestamps get evicted early, causing the adapter to re-process its own outbound messages as inbound.

Same class of bug as #3490 (email _seen_uids growing unbounded).

Root Cause

set.pop() on line 626 of gateway/platforms/signal.py doesn't guarantee oldest-first eviction. Sets have no insertion order in their iteration/pop behavior, so the eviction is effectively random.

Fix

Replace the plain set with collections.OrderedDict (used as an ordered set with None values):

  • gateway/platforms/signal.py:184set()OrderedDict()
  • gateway/platforms/signal.py:382.discard(ts).pop(ts, None)
  • gateway/platforms/signal.py:624-626.add(ts)[ts] = None, .pop().popitem(last=False)

OrderedDict.popitem(last=False) always removes the oldest entry — O(1), deterministic FIFO.

Tests

6 new tests in tests/gateway/test_signal_timestamp_eviction.py:

  • Tracks timestamps, ignores missing/non-dict input
  • Verifies oldest-first eviction when exceeding max
  • Verifies only newest 50 survive after 10k messages
  • Asserts backing store is OrderedDict

38 pre-existing signal tests + 6 new = 44 passed.

Replace plain set with OrderedDict for _recent_sent_timestamps so that
eviction always removes the oldest entry instead of an arbitrary one.
The previous set.pop() could discard newer timestamps while keeping
stale ones indefinitely, degrading echo-back filtering over time.
@dieutx

dieutx commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

Closing — minor edge case that rarely triggers in practice.

@dieutx dieutx closed this Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant