[
https://issues.apache.org/jira/browse/HDDS-17266?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-17266:
------------------------------
Affects Version/s: 2.2.1
2.1.2
2.1.0
2.1.1
2.2.0
> Reconcile container command is not resent when it is lost before the datanode
> runs it (lost heartbeat reply, SCM leader change, datanode restart)
> -------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-17266
> URL: https://issues.apache.org/jira/browse/HDDS-17266
> Project: Apache Ozone
> Issue Type: Bug
> Affects Versions: 2.1.0, 2.2.0, 2.1.1, 2.1.2, 2.2.1
> Reporter: Siyao Meng
> Priority: Minor
> Attachments: TestBugMC3LostHeartbeatReplyDropsCommand.java
>
>
> h3. Mechanism
> {{CommandQueue.getCommand}} removes the commands queued for a datanode when
> {{SCMNodeManager.processHeartbeat}} builds the heartbeat response, and
> {{SCMDatanodeProtocolServer.sendHeartbeat}} returns them in the reply.
> Nothing acknowledges the reply and SCM keeps no copy. If the reply is lost
> after SCM processed the heartbeat, the datanode gets an {{IOException}}, does
> not resend that heartbeat, and the commands exist nowhere. Hadoop RPC does
> not resend the call either.
> Delivery is therefore at most once, and the other command types are built for
> that: their issuers repair a loss on their own. From reading the code, not
> tested:
> * DeleteBlocks: the SENT timeout in
> {{SCMDeletedBlockTransactionStatusManager}}.
> * Replicate, delete container, EC reconstruct: ReplicationManager pending op
> expiry and re-evaluation.
> * Close container: sent again while the container is CLOSING.
> * Pipelines: {{BackgroundPipelineScrubber}} and {{PipelineReportHandler}}.
> * Operational state, finalize and reregister: regenerated from every
> heartbeat. Refresh volume usage: sent on every balancer iteration.
> {{ReconcileContainerCommand}} is the exception.
> {{SCMClientProtocolServer.reconcileContainer}} fires the event,
> {{ReconcileContainerEventHandler}} queues one command per replica, and
> nothing tracks or resends them. This is unchanged on master at e0de543cbe9.
> h3. Trigger
> An admin runs {{ozone admin container reconcile}}, and the command for one
> replica is lost before that datanode runs it:
> * the heartbeat reply carrying it is lost after SCM built it: the connection
> is reset, or SCM answers after the heartbeat RPC timeout
> ({{ozone.scm.heartbeat.rpc-timeout}}, default 5s).
> * the SCM leader changes first. The per datanode {{CommandQueue}} is in the
> old leader's memory only, so the new leader has no copy; if the old leader
> hands the command out on a later heartbeat, it carries the old term and the
> datanode drops it once it knows the new term. A reconcile that already
> reached the datanode runs through {{ReplicationSupervisor}}
> ({{ReconcileContainerTask}} takes the command's term), which skips queued
> tasks whose term is lower than the current leader term ("Ignoring ... since
> SCM leader has new term"). With a replication backlog on the datanode, a
> failover long after the admin command still discards it.
> * the datanode restarts before it runs the command, or its command queue is
> full.
> For every other command type the new leader's ReplicationManager or report
> handlers send the command again after a failover.
> h3. Impact
> Reconciliation runs per replica: each replica repairs itself from its peers.
> If the replica that lost its command is one that needed repair, it stays
> unrepaired. The admin command reports that reconciliation was triggered and
> points to {{ozone admin container reconcile --status}}, which then keeps
> showing that replica's checksum differing from its peers; re-running the
> command repairs it. If the replica that lost its command was a healthy one,
> the others still reconcile against it and the result converges. No data is
> lost or changed. Not exercised: the full admin command on a cluster, the
> failover routes (code reading only), and the datanode not reconciling
> afterwards (it learns commands only from heartbeat replies).
> h3. Reproduction
> [^TestBugMC3LostHeartbeatReplyDropsCommand.java] ({{hadoop-hdds/server-scm}},
> target path and Maven command in the class comment) passes on unmodified
> source when the bug is present. It starts a real SCM with
> {{HddsTestUtils.getScm}} and a datanode client built by the real
> {{SCMConnectionManager}}, connected through a TCP relay in the test. A
> reconcile command is queued with the {{DATANODE_COMMAND}} event that
> {{ReconcileContainerEventHandler}} fires, and the relay loses the reply to
> the next heartbeat, by resetting the connection or by swallowing it until the
> client times out. In both cases SCM holds no copy afterwards and five later
> heartbeats carry no command. The datanode RPC server is started explicitly
> because the HA stub of the test has no Ratis log whose catch up starts it.
> The failover routes are not reproduced.
> h3. Suggested fix
> The fix belongs with the issuer, as for the other command types. The
> container reconciliation design doc already plans this for Phase II
> (HDDS-12987): "Automate container reconciliation requests as part of SCM's
> replication manager". If ReplicationManager triggers reconciliation on a
> checksum mismatch, a lost command gets the same repair as every other command
> type. Any issuer side tracking has to survive an SCM failover, so it must be
> re-derivable on the new leader from replica state (as ReplicationManager
> state is) or persisted; tracking kept only in the leader's memory would not
> cover the failover routes. There is no completion signal to stop on today
> (checksums that still differ can also mean that reconciliation ran and could
> not repair), so this needs a decision on what counts as done and how often to
> retry. Acknowledging heartbeat replies and redelivering in SCM would be a
> protocol change for every command type and would make non-idempotent commands
> at least once. Documenting that a reconcile may need to be re-run is the
> minimal alternative.
> Found by TLA+ model checking and code review of the datanode registration and
> heartbeat paths under HDDS-15926, on commit
> b10a7d427bdf83807a7e48a1be507d3b02d5a503. Checked against HDDS issues and
> apache/ozone pull requests for duplicates before filing. Related but not
> duplicates: HDDS-10372 (added the reconcile command), HDDS-13086 (blocks
> duplicate reconcile requests on the datanode), HDDS-10714 (restrict
> reconciliation requests by datanode status), HDDS-8882 (status tracking for
> DeleteBlocks) and HDDS-17064 (a full container report lost on a failed
> heartbeat, the other direction). Only the reproduction is attached, there is
> no fix patch. Generated with Specula (Claude Opus 5.5).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]