Skip to content

Replication migration deploy 2: leaders send TRANSACTION, followers send FOLLOWER_STATUS - #2725

Open
tylerkaraszewski wants to merge 4 commits into
mainfrom
replication-migration-deploy-2
Open

Replication migration deploy 2: leaders send TRANSACTION, followers send FOLLOWER_STATUS#2725
tylerkaraszewski wants to merge 4 commits into
mainfrom
replication-migration-deploy-2

Conversation

@tylerkaraszewski

Copy link
Copy Markdown
Contributor

Details

Deploy 2 of 3 in the replication protocol migration. Remove QUORUM commits was deploy 1, and it's out everywhere, which is the only precondition for this one.

Deploy 1 added receivers for the new messages without sending any of them. This deploy starts sending them:

  • Leader sends a single TRANSACTION instead of the BEGIN_TRANSACTION + COMMIT_TRANSACTION pair. Every transaction leader broadcasts has already committed locally, so there was never a reason to hold the follower's copy open across two messages.
  • Followers send FOLLOWER_STATUS instead of APPROVE_TRANSACTION, every MIN_APPROVE_FREQUENCYth commit. Nothing acts on an approval any more, so the only thing a follower owes leader is a rough idea of how far along it is, and every message already carries that in its CommitCount header. That collapsed the whole approve/deny block down to four lines, since there's no longer an ASYNC-vs-not distinction to make on the response path.
  • Deleted the ROLLBACK_TRANSACTION receiver.

That last one is why this couldn't be merged into deploy 1. A leader running pre-deploy-1 code still sends ROLLBACK_TRANSACTION on a commit conflict, and a follower with no handler for it falls through to _replicate's unrecognized-command warning and keeps its prepared transaction open. The next BEGIN_TRANSACTION then throws already in a transaction out of a thread with no try/catch, which means set_terminate and a dead process.

BEGIN_TRANSACTION and COMMIT_TRANSACTION receivers both stay, because during this rolling deploy an un-upgraded leader is still sending them. They go away in deploy 3, along with the ASYNC_ prefix, the APPROVE_TRANSACTION/DENY_TRANSACTION pair, and the four rollback paths that only exist because a follower could hold a prepared transaction between two messages.

Worth noting for whoever reviews: nothing in the code enforces the ordering here. There's no replication protocol version on the wire to gate on — LOGIN["Version"] is an opaque build string that plugin versions feed into and -versionOverride replaces outright, so it can't be used to decide what a peer can receive. The method line is the discriminator, which is why TRANSACTION is a new name rather than a header on BEGIN_TRANSACTION.

Fixed Issues

For https://github.com/Expensify/Expensify/issues/669612

Tests

ClusterUpgradeTest is the interesting one — it's the only test that runs two different binaries against each other, and it picks the most recent release tag as the old side. That tag is 2026-08-14-373619, which contains deploy 1, so this run actually exercised the deploy-1-leader / deploy-2-follower combination that this PR depends on:

Cloning into 'Bedrock'...
HEAD is now at 2627a74b Merge pull request #2722 from Expensify/main
vagrant@expensidev-2404:/vagrant/Bedrock/test/clustertest$ ./clustertest -threads 16
[ TEST RESULTS ] Passed: 77, Failed: 0
vagrant@expensidev-2404:/vagrant/Bedrock/test$ ./test -threads 16
[ TEST RESULTS ] Passed: 222, Failed: 1

Failures:
SSLTest::proxyTest

proxyTest fails on my VM because squid isn't running, not because of anything here.

It still only covers the one upgrade order it performs, so treat it as a floor rather than a proof.


Internal Testing Reminder: when changing bedrock, please compile auth against your new changes

tylerkaraszewski and others added 4 commits August 14, 2026 11:54
…CTION

Every transaction a leader broadcasts has already committed locally, so
there is no reason to hold the follower's copy open across two messages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing acts on an approval any more, so the only thing a follower owes
leader is a periodic commit-count report. Every message already carries
CommitCount, so FOLLOWER_STATUS needs no body of its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
No leader in the cluster can send this any more: the previous release
stopped sending it, and this release only ships once every node is
running that one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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