Skip to content

Prevent duplicate reconciled message events with a keyed cache - #835

Draft
qayshp wants to merge 2 commits into
BlueBubblesApp:developmentfrom
qayshp:agent/prevent-duplicate-old-message-events
Draft

Prevent duplicate reconciled message events with a keyed cache#835
qayshp wants to merge 2 commits into
BlueBubblesApp:developmentfrom
qayshp:agent/prevent-duplicate-old-message-events

Conversation

@qayshp

@qayshp qayshp commented Jul 23, 2026

Copy link
Copy Markdown

Summary

  • retain message GUIDs and message state for the seven-day reconciliation window plus one reconciliation interval
  • use a keyed, Map-backed cache for high-volume message-event deduplication
  • keep chat event/state retention at one hour and preserve the existing array-backed chat cache
  • centralize the message poller's reconciliation and retention constants
  • add focused regression coverage for retention, pruning, and cache separation

Root cause

The message poller periodically widens its query to seven days, but message event and state entries were pruned after one hour. A previously emitted row could therefore lose its GUID and state, then be classified as a new entry when the reconciliation lookback revisited it.

Simply extending the old array-backed event cache would also make high-volume message lookup increasingly expensive. The final implementation separates the two workloads:

  • message events use KeyedEventCache, backed by Map for keyed add/find/remove operations
  • chat events continue to use the existing EventCache array and one-hour retention
  • message event and state entries remain for seven days plus five minutes, covering one final reconciliation after they age out of the query window

Impact

Long-running servers no longer re-emit unchanged reconciled rows as new-entry events after the former one-hour deduplication window. Message lookup no longer performs a linear scan of the retained event set, while chat read-state deduplication keeps its existing storage and retention behavior.

Validation

Passed on the current head:

  • node --test test/messageCacheRetention.test.cjs — 4/4 tests
  • targeted ESLint
  • targeted Prettier check
  • git diff --check

The tests cover full-lookback deduplication, expiry pruning, one-hour chat retention, Map-backed message storage, unchanged array-backed chat storage, and duplicate-key behavior.

tsc -p packages/server/tsconfig.json --noEmit still reaches the existing unrelated ScheduledService.ts:39 NodeJS.Timer/clearInterval type error.

Fixes #765

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review complete. The cache-retention behavior is covered by the synthetic replay tests; I left one performance-oriented follow-up inline.

Comment thread packages/server/src/server/databases/imessage/pollers/constants.ts
@qayshp

qayshp commented Jul 23, 2026

Copy link
Copy Markdown
Author

Validation update

Validated on SIP-enabled arm64 macOS 26.5.2 with Node 20.11.1 and npm 10.2.4.

  • npm run test:message-cache --workspace=@bluebubbles/server — 3/3 tests passed.
  • A synthetic benchmark using this PR's actual EventCache retained about 8.97 MiB at 100,000 entries; 100 worst-case lookups took 68–79 ms, and inserting those entries through add() took 17.18 seconds.
  • At 500,000 retained entries, the cache used about 36.62 MiB and 100 worst-case lookups took about 400 ms.

The functional regression coverage passes, but the benchmark confirms that the linear cache can become material at unusually high volume. Validation against a representative busy server, or a decision to use a keyed cache, remains open before this draft is promoted.

No messages were sent or received during validation. No screenshot is needed because this change has no visual surface.

@qayshp qayshp changed the title Prevent duplicate old message events Prevent duplicate reconciled message events with a keyed cache Jul 27, 2026
@qayshp

qayshp commented Jul 27, 2026

Copy link
Copy Markdown
Author

Superseding the earlier performance note: its linear-cache benchmark describes the prior implementation, not the current head. Message-event deduplication now uses a keyed Map-backed cache, while the chat cache remains the existing array-backed EventCache. The focused retention/cache suite passes 4/4. The remaining validation limitation is the pre-existing unrelated ScheduledService.ts:39 NodeJS.Timer/clearInterval type-check failure.

@qayshp
qayshp marked this pull request as ready for review July 28, 2026 07:02
@qayshp
qayshp marked this pull request as draft July 28, 2026 07:30
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