Skip to content

Log duplicate escalated write requests#2681

Open
rlinoz wants to merge 4 commits into
mainfrom
rodrigo-dedup-escalated-writes
Open

Log duplicate escalated write requests#2681
rlinoz wants to merge 4 commits into
mainfrom
rodrigo-dedup-escalated-writes

Conversation

@rlinoz

@rlinoz rlinoz commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Details

Duplicate escalated write requests may reach the leader and be executed more than once, causing write amplification (identified during fire analysis).

This is a protective, observability-first step: on the leader, we now track the ids of escalated requests currently in flight and detect when the same request arrives again while a previous copy is still being processed. For now duplicates are only logged (via SWARN) — they are not dropped — so we can first confirm whether/how often this actually happens in production.

Fixed Issues

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

Tests

Added EscalateTest::duplicateEscalatedRequestDetected, which escalates two identical write commands sharing one id from a follower (kept overlapping in flight on the leader via a slow process step) and verifies the leader's duplicateEscalatedRequestCount increased.


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

Track the ids of escalated requests in flight on the leader and log when
a duplicate arrives while an identical request is still being processed.
Expose a cumulative counter via Status and add a cluster test covering it.
rlinoz added 2 commits July 16, 2026 11:13
Match the convention used across the cluster tests: node 0 is the leader,
node 1 a follower, guarded with waitForState, instead of scanning nodes.
@rlinoz
rlinoz marked this pull request as ready for review July 16, 2026 14:47
@rlinoz rlinoz changed the title De-duplicate escalated write requests on the leader (log-only) Log duplicate escalated write requests\ Jul 16, 2026
@rlinoz rlinoz changed the title Log duplicate escalated write requests\ Log duplicate escalated write requests Jul 16, 2026
@rlinoz

rlinoz commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

@danieldoglas is this what you had in mind?

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac42ad6461

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread BedrockServer.cpp Outdated
// The escalated request is done, stop tracking it.
if (fromPrivateCommandPort) {
lock_guard<mutex> lock(_inFlightEscalatedRequestsMutex);
_inFlightEscalatedRequests.erase(escalatedID);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Only erase IDs inserted by this request

When a duplicate request hits the insert failure path, it still executes and then reaches this unconditional erase; if that duplicate finishes before the original, it removes the original request's marker even though the original is still in flight. A third copy of the same escalated request arriving during that window will insert successfully and won't increment/log, so the new status counter under-reports the repeated in-flight duplicates it is meant to measure. Track whether this handler actually inserted the id, or use a refcount, before erasing.

Useful? React with 👍 / 👎.

@danieldoglas

Copy link
Copy Markdown
Contributor

@rlinoz do you know where the ID is coming from? Does it come from PHP?

@rlinoz

rlinoz commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

From what I understand It comes from here

const string commandID = baseCommand->request.isSet("ID") ? baseCommand->request["ID"] : generateCommandID();

We generate the ID in Bedrock to build the command.

@danieldoglas

Copy link
Copy Markdown
Contributor

Yeah, so that's kind of a problem. The issue is that PHP is resending the command to followers, which are escalated to leader. So leader would always receive a different ID on those cases

@rlinoz

rlinoz commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

aaah I got it wrong, from the issue I thought followers were duplicating the commands, that makes more sense. I will rethink this, thanks!

…rites

PHP re-sends a timed-out write to a different follower, and each follower
mints its own command id, so command->id can't identify the duplicate copy
that lands on the leader. Fingerprint the request content instead (excluding
the transport headers a follower stamps on while escalating), so two copies
of one logical write - same requestID and payload - match.

Also track duplicates for writes that reach the leader directly on the public
command port (client connected to the leader), not just escalations on the
private port, and extract the tracking into trackInFlightWrite/
untrackInFlightWrite.
Comment thread BedrockServer.cpp
canonical += '\n';
}
canonical += request.content;
return hash<string>{}(canonical);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think hashing the request is better than sending another unique ID from PHP, mostly because it is less moving parts.

I say another because there is the requestID but we forward that to other commands, so not unique enough.

Do you have any thoughts @danieldoglas ?

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.

2 participants