Skip to content

Conversation

@corhere
Copy link

@corhere corhere commented Apr 16, 2025

Under certain conditions, a broadcast in a TransmitLimitedQueue can be erroneously dropped (without invoking Finished()) when a new broadcast which does not invalidate it is added to the queue. The queued broadcasts are stored in a B-tree, sorted by priority. Broadcasts are sorted lexicographically by (transmits, -length, -id) such that longer messages are prioritized over shorter ones, and newer messages take priority over older ones of the same length.

The B-tree implementation requires a strict ordering of broadcasts. If two items have the same priority (!a.Less(b) && !b.Less(a)) they are considered equal. When a new item is inserted into the tree which is equal to an existing item, the existing item is replaced with it. TransmitLimitedQueue tries to ensure that no broadcast in the tree is equal to any other by giving each broadcast a distinct id value: the number of broadcasts enqueued since the queue was last emptied. But it sometimes assigns an id value to a broadcast which collides with the id of another broadcast already in the queue!

The trouble arises when adding a broadcast to the queue which invalidates all the existing broadcasts. The id of the new broadcast is generated first, before the invalidated broadcasts are removed from the queue. (The new broadcast is inserted into the tree last, after removing the invalidated ones.) When all broadcasts are invalidated, the id generator state is reset due to the queue being (transiently) empty. The end result is a queue containing a single broadcast with an id of N>1 and an id generator state such that the next id generated will be 1. The Nth broadcast added to the queue after this will be assigned id N -- id collision! If the message lengths happen to be equal, the Nth broadcast will clobber the previous broadcast in the queue with the colliding id when inserted into the tree.

Fix the erroneous loss of enqueued broadcasts by generating the id for the new broadcast after invalidating any existing ones, rather than before.

Under certain conditions, a broadcast in a TransmitLimitedQueue can be
erroneously dropped (without invoking Finished()) when a new broadcast
which does not invalidate it is added to the queue. The queued
broadcasts are stored in a B-tree, sorted by priority. Broadcasts are
sorted lexicographically by (transmits, -length, -id) such that longer
messages are prioritized over shorter ones, and newer messages take
priority over older ones of the same length.

The B-tree implementation requires a strict ordering of broadcasts. If
two items have the same priority (!a.Less(b) && !b.Less(a)) they are
considered equal. When a new item is inserted into the tree which is
equal to an existing item, the existing item is replaced with it.
TransmitLimitedQueue tries to ensure that no broadcast in the tree is
equal to any other by giving each broadcast a distinct id value: the
number of broadcasts enqueued since the queue was last emptied. But it
sometimes assigns an id value to a broadcast which collides with the id
of another broadcast already in the queue!

The trouble arises when adding a broadcast to the queue which
invalidates all the existing broadcasts. The id of the new broadcast is
generated first, before the invalidated broadcasts are removed from the
queue. (The new broadcast is inserted into the tree last, after removing
the invalidated ones.) When all broadcasts are invalidated, the id
generator state is reset due to the queue being (transiently) empty. The
end result is a queue containing a single broadcast with an id of N>1
and an id generator state such that the next id generated will be 1. The
Nth broadcast added to the queue after this will be assigned id N -- id
collision! If the message lengths happen to be equal, the Nth broadcast
will clobber the previous broadcast in the queue with the colliding id
when inserted into the tree.

Fix the erroneous loss of enqueued broadcasts by generating the id for the
new broadcast after invalidating any existing ones, rather than before.
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