Skip to content

fix: one TOPIC per channel on netsplit heal (issue #1)#13

Open
vjt wants to merge 1 commit intoazzurra:masterfrom
vjt:fix/topic-twice-burst
Open

fix: one TOPIC per channel on netsplit heal (issue #1)#13
vjt wants to merge 1 commit intoazzurra:masterfrom
vjt:fix/topic-twice-burst

Conversation

@vjt
Copy link
Copy Markdown
Contributor

@vjt vjt commented Apr 21, 2026

Summary

Closes #1 (open since 2011).

When a remote server finished bursting after a netsplit heal and the affected channels had CI_KEEPTOPIC + CI_TOPICLOCK set, ChanServ emitted two back-to-back TOPIC commands per channel:

  1. chan_handle_SJOIN called restore_topic() the moment the channel was re-created (newChannel == TRUE && synched == TRUE).
  2. The immediately-following TOPIC propagated from the same bursting server fell into chan_handle_TOPIC's server-source branch. That branch was gated only on synched == TRUE, so it fired, overrode chan->topic with ci->last_topic, and emitted a second ChanServ TOPIC with identical content.

Services already track per-server burst state via SERVER_FLAG_BURSTING (set on SERVER entry, cleared in m_gnotice at "has synched to network data."). The flag just wasn't consulted by the TOPIC path.

Fix

  • channels.c / chan_handle_SJOIN — skip restore_topic() while the originating server is still BURSTING.
  • channels.c / chan_handle_TOPIC — in the server-source branch, skip both the TOPICLOCK override (and its send_cmd TOPIC) and the trailing record_topic() while the originating server is BURSTING. chan->topic is still updated via the normal store path so in-memory state stays consistent; ci->last_topic is preserved so the reconciliation pass has something to compare against.
  • messages.c / m_gnotice — after clearing SERVER_FLAG_BURSTING, call synch_topics() to resolve any ci->last_topic vs chan->topic mismatch accumulated during the burst with a single ChanServ TOPIC per channel. This is the same logic already used for services' own initial sync (called in m_ping / m_burst).

Result: one TOPIC per channel per netsplit heal.

Addresses all three points rfc1459 raised on the original issue:

  1. ✅ Services no longer issue any TOPIC until burst is processed (per server).
  2. ✅ Topic is reset only if CI_TOPICLOCK is set and the server's topic differs from ChanServ's DB (via synch_topics's str_equals check).
  3. ⚠️ Still relies on the existing GNOTICE pattern match for per-server burst-end; a dedicated CAPAB on bahamut would be cleaner but is out of scope for this fix.

Test plan

  • Compiles clean (-Wall -Wshadow -Wcast-align -Wsign-compare).
  • Deploy on testnet hub, trigger a netsplit between two leaves, verify only one TOPIC emitted per channel on rejoin with KEEPTOPIC+TOPICLOCK set.
  • Regression: services-initial-sync topic restore still works (same synch_topics() is invoked from m_burst/m_ping, unchanged).
  • Regression: user-sourced TOPIC during stable operation unaffected (user-source branch untouched; src_bursting is only written in the server-source branch).

…rra#1)

When a remote server finished bursting after a netsplit heal and the
affected channels had CI_KEEPTOPIC + CI_TOPICLOCK set, ChanServ emitted
two back-to-back TOPIC commands for each such channel:

  1) chan_handle_SJOIN called restore_topic() the moment the channel was
     re-created (newChannel == TRUE + synched == TRUE).
  2) The immediately-following TOPIC propagated from the same bursting
     server fell into chan_handle_TOPIC's server-source branch, which —
     gated only on synched == TRUE — overrode chan->topic with
     ci->last_topic and emitted a second ChanServ TOPIC with identical
     content.

Services already track per-server burst state via SERVER_FLAG_BURSTING
(set in servers_SERVER_create / server_handle_SERVER, cleared at
m_gnotice's "has synched to network data." match). The flag just wasn't
consulted by the TOPIC path.

Fix:

  * channels.c / chan_handle_SJOIN: skip restore_topic() while the
    originating server is still BURSTING.

  * channels.c / chan_handle_TOPIC: in the server-source branch, skip
    both the TOPICLOCK override (and its send_cmd TOPIC) and the
    trailing record_topic() while the originating server is BURSTING.
    chan->topic is still updated via the normal store path so the
    in-memory state stays consistent; ci->last_topic is preserved so the
    reconciliation pass has something to compare against.

  * messages.c / m_gnotice: after clearing SERVER_FLAG_BURSTING, call
    synch_topics() to resolve any ci->last_topic vs chan->topic mismatch
    accumulated during the burst with a single ChanServ TOPIC per
    channel — the same logic already used for services' own initial
    sync.

Result: one TOPIC per channel per netsplit heal, matching
rfc1459's original three-point critique on the issue.

Fixes azzurra#1
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.

ChanServ sets TOPIC twice after a netsplit if TOPICLOCK is enabled

1 participant