Skip to content

updates: dispatch common difference updates directly - #1854

Open
igitur wants to merge 1 commit into
gotd:mainfrom
igitur:fix/common-state-gap-buffered
Open

igitur wants to merge 1 commit into
gotd:mainfrom
igitur:fix/common-state-gap-buffered

Conversation

@igitur

@igitur igitur commented Sep 11, 2026

Copy link
Copy Markdown

Problem

A common-state (private chat / basic group) pts gap can strand buffered updates.

A live update that arrives behind a gap is buffered in the sequence box's pending. A gap-triggered getDifference then replays the same update in OtherUpdates, and getDifference calls setState(diff.State) afterwards. Because the replayed update is routed through the sequence box while the box state is still behind, it is buffered again; the following setState advances the box past it, and applyPending drops it as Outdated update, skipping.

Reported in #1853 with a concrete reproduction: a bot streaming a long reply via repeated edits, where only a subset of the edits reach the handler (30 wire edits, 17 dispatched in one capture).

This is the common-state counterpart of #1852 (which is the channel channelDifference.timeout path). Possibly the same family as the closed #1623.

Fix

Dispatch common pts updates from a difference directly instead of routing them through the sequence box. The difference is the authoritative, ordered replay for the gap, and setState records the position it reaches. Channel updates in OtherUpdates keep their existing routing to the channel state.

Concretely, handleUpdates/handleSeq/applyCombined gain a fromDifference flag; when set, applyCombined appends a common IsPtsUpdate to the batch's others (dispatched with the batch) rather than calling handlePts.

Test

telegram/updates/common_getdiff_test.go reproduces the drop deterministically: state Pts=10, a live edit at Pts=12 is buffered behind the gap at 11, getDifference replays it in OtherUpdates and returns State.Pts=12. Before the fix the edit is dropped; after, it is dispatched.

The existing TestE2E covers the channel-routing boundary: channel updates in OtherUpdates must still reach the channel state (the first version of this fix dispatched them directly and duplicated them).

go test ./telegram/updates/... passes (also with -count=5 for the randomized e2e).

Fixes #1853.

A common-state (private chat / basic group) pts gap can strand buffered
updates. A live update that arrives behind a gap is buffered in the
sequence box's pending. A gap-triggered getDifference replays the same
update in OtherUpdates and then calls setState(diff.State). Because the
replayed update is routed through the sequence box while the state is
still behind, it is buffered again; the following setState advances the
box past it, and applyPending drops it as outdated.

Dispatch common pts updates from a difference directly instead of
routing them through the sequence box. The difference is the
authoritative, ordered replay for the gap, and setState records the
position it reaches. Channel updates in OtherUpdates keep their existing
routing to the channel state.

Fixes gotd#1853.
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.

updates: common-state pts gap strands buffered updates (sequence-box state advance) Some UpdateNewMessage updates are not received in update handler

1 participant