Repository navigation
windowed, content/header-based message deduplication #4373
almostintuitive
started this conversation in
Ideas
Replies: 3 comments
|
Thanks for the detailed write-up. On the history question: the old message deduplicator was removed when server-ng became the Iggy server (#3856), with the last of it going in #3880. The client-table dedup you mention came in with #3934 and #3959. @hubcio @numinnex could you take a look at the questions here? |
0 replies
|
yes. it'll be implemented after multi-leader, and it'll work exactly like before. |
0 replies
|
Hi, we've dropped that functionality as the purpose of that mechanism was dedup of retired messages rather than content based deduplication. I am actually curious what is use case for such deduplication that |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Discussion: windowed, content-based message deduplication
Hi all 👋
We'd like to add windowed, content-based message deduplication to Iggy. A partition's primary would drop any message whose identity (its message ID, or the value of a configured user header) was already appended within a time window, before the message goes into the replication pipeline. We built this design before on a Raft-based broker. Before writing any Iggy code, we'd like to know whether you want this, and how it should fit with VSR and the partition plane.
Below: a short proposal, the lessons that carry over, and questions for you.
Proposal
User-facing semantics
dedup_windowdedup_identitymessage_id(default) orheader:<name>.Plus a server-wide
dedup_max_entries_per_partitionceiling.origin_timestampis never used (see the lessons below for why).id == 0(server-assigned UUID) are always admitted and never indexed.Core design idea: an advisory index derived from the log
The index (
identity → timestamp) is a deterministic function of the committed data in the partition log. It is not separately replicated state. Three facts make that safe:What follows from that:
now − windowand replay only about one window of committed batches. This also addresses a problem the client table slice has today: it comes back empty after a restart.Our first design replicated the index as its own metadata stream (key lists, mutations, checkpoints) so every replica could rebuild it byte for byte. That brought unbounded undo history, a checkpoint protocol, retention and compaction coordination, and a mutex that allowed only one write per partition per commit round-trip. We deleted all of it, about 2k lines, in favor of the log-derived design. We recommend skipping that step.
Mechanics
RandomState) because the digest is persisted and compared across replicas. Every entry has the same fixed size regardless of identity size, and collisions are negligible at 10⁷ entries. Collision resistance against attackers doesn't matter, since anyone can already suppress an identity by sending it first.project().commit ≥ current opis a simple, conservative way to do this.Lessons that carry over (?)
new − stored ≤ W) also turned any older timestamp into a "duplicate". Iggy's monotonic prepare timestamp avoids this whole class of bugs. Using it is most of why this proposal is short.Known gaps in the design
Questions for maintainers
SendMessagesbatch betweenconvert_request_messageandproject()? Is that step synchronous with op assignment, which would remove the ordering fence?SendMessagesconfirmations report anything beyondbase_offset(for example a dropped count or per-ID status)? What shouldbase_offsetbe when every message was dropped?We're happy to start with the self-contained index (map, FIFO eviction and digest, plus tests) as a first PR if the direction looks right.
All reactions