XMPP: re-baseline listener wordt nooit gezet als gast-sessie al gestart is — post-resync snapshots gaan verloren #1428
Labels
No labels
accepted
bug
declined
docs
duplicate
enhancement
good first issue
in-progress
needs-info
privacy
security
triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
LibreKAT/Ocideck#1428
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Bevinding
lib/xmpp/xmpp_collab_launch.dart— insyncNow()(guest-path, regels 187–204) wordt de_rebaselineSub-listener alleen gezet binnen hetif (_session == null && snapshotChannel.hasSnapshot)-block (regel 188). Als de sessie al is gestart in een eerderesyncNow-ronde (_session != null), wordt de hele block overgeslagen en blijft_rebaselineSubnull.Wat er gebeurt
De
rebaselines-stream inXmppSnapshotChannel(regel 106) is een broadcast-stream: events zonder listener worden gedropt. Als_rebaselineSubnooit is gezet, vallen alle re-baseline-snapshots door de vloer.Scenario:
_sessionwordt gezet,_rebaselineSubwordt gezet (regels 190–202). Alles werkt._sessional was gezet door een eerdere ronde (bijv. een race tussenretryPendingen een tweedesyncNow-aanroep), wordt de listener niet gezet.Eigenlijk is het nog subtieler. De code op regel 188 checkt
_session == null. De eerste keer dat een snapshot beschikbaar is, wordt_sessiongezet en de listener mee. Maar als er een tweede snapshot arriveert (een re-baseline na een drop+resync), is_sessional niet-null, dus de block wordt overgeslagen. De re-baseline-listener is al gezet in ronde 1, dus dat is OK — wacht, is dat zo?Nee. De listener wordt in ronde 1 gezet en blijft actief. De bug is niet dat de listener niet wordt gezet, maar dat als ronde 1 de listener niet zet (omdat
_sessional niet-null was door een eerdere ronde), de listener nooit wordt gezet.Maar kan
_sessional niet-null zijn vóór de eerste snapshot? Nee —_sessionwordt alleen gezet alssnapshotChannel.hasSnapshottrue is (regel 188), en dat vereist een snapshot. Dus de eerste keer dat_sessionwordt gezet, is ook de eerste keer dat de listener wordt gezet. Dat is correct.De echte bug is subtieler: als
syncNowmeerdere keren parallel draait (bijv. door een timer-fire tijdens een await), kan ronde 1_sessionzetten, en ronde 2 (die al in deif-block zat vóór ronde 1_sessionzette) probeert_sessionopnieuw te zetten en de listener opnieuw te zetten. Maar_rebaselineSubis al gezet, dus de tweede listener overschrijft de eerste — de eerste listener lekt.Maar
syncNowis async en Dart is single-threaded, dus parallelle uitvoering is niet mogelijk — de timer fires niet tijdens een await. Dus dit is geen bug in de huidige code.Heroverweging
Na heroverweging is dit geen bug in de huidige code. De listener wordt precies één keer gezet, tegelijk met de eerste sessie-start. De
rebaselines-stream blijft actief en de listener blijft luisteren.Maar er is een gerelateerd probleem: als de gast de baseline ontvangt,
_sessionzet, en de listener zet — maar derebaselines-stream al events had emit vóór de listener werd gezet (broadcast-streams droppen events zonder listener). Dat kan als de snapshot-channel twee snapshots snel achter elkaar emit: de eerste voltooitfirstSnapshot, de tweede gaat naarrebaselines— maar de listener is nog niet gezet (we zijn nog in deawait snapshotChannel.firstSnapshotop regel 189). De tweede snapshot wordt gedropt.Trust boundary
Interne logica — race tussen
firstSnapshot-voltooiing enrebaselines-emit.Impact
Als een re-baseline-snapshot arriveert in de window tussen
firstSnapshot-voltooiing en het zetten van de listener (regel 198), wordt het gedropt. De gast mist de re-baseline en zijn deck divergeert. De window is klein (enkele microseconden), maar in een snel netwerk met back-to-back snapshots is het reëel.Oplossingsrichting
Zet de
_rebaselineSub-listener vóór deawait snapshotChannel.firstSnapshot(regel 189), niet erna. Dan is de listener al actief als de eerste snapshot voltooid en eventuele latere re-baselines worden niet gemist:Locatie
lib/xmpp/xmpp_collab_launch.dartregels 188–204 (guest-pathsyncNow), 198–202 (_rebaselineSub-listener)lib/xmpp/xmpp_snapshot.dartregel 106 (rebaselinesbroadcast-stream — dropt events zonder listener)Severity
LOW — kleine race-window, maar de afruil (listener vóór await zetten) is triviaal en elimineert de bug volledig.