Siyao Meng created HDDS-17643:
---------------------------------

             Summary: Pipeline leader recreates a Ratis container replica that 
SCM force deleted, and SCM keeps the new copy over the real replicas
                 Key: HDDS-17643
                 URL: https://issues.apache.org/jira/browse/HDDS-17643
             Project: Apache Ozone
          Issue Type: Bug
          Components: SCM, Ozone Datanode
    Affects Versions: 2.2.2
            Reporter: Siyao Meng


h3. Mechanism
SCM can delete a CLOSED replica on a datanode that is still the Ratis leader of 
the pipeline the container was written on, for example the source of a balancer 
move ({{MoveManager}} sends a forced delete) or an excess replica. Pipelines 
outlive the containers on them, so writes for that container can still reach 
the leader after the delete.

{{KeyValueHandler}} deletes the replica through 
{{ContainerSet.removeContainer(containerId)}}, which also removes the ID from 
the container ID table (HDDS-11650) and does not add it to the missing 
container set. Nothing on the datanode remembers that the container existed.

When a WriteChunk for that container then reaches the leader:
# {{ContainerStateMachine.startTransaction}} calls 
{{HddsDispatcher.validateContainerCommand}}, which returns without any check 
when the container is not in the container set, so the leader accepts the write.
# In the WRITE_DATA stage {{HddsDispatcher.dispatchRequest}} auto creates a 
missing container for WriteChunk and PutBlock. HDDS-15791 added 
{{containerAutoCreate}} to turn this off, but only {{ECBlockOutputStream}} sets 
it to false; {{BlockOutputStream}} for Ratis always allows it. The leader 
creates an empty OPEN replica.
# The followers still have the container CLOSED and return CLOSED_CONTAINER_IO, 
which {{ContainerStateMachine}} ignores in writeStateMachineData and 
applyTransaction. The entry commits.

Two outcomes follow:
* If the block's earlier chunks were written to the deleted replica, PutBlock 
fails with UNABLE_TO_FIND_CHUNK, the new replica becomes UNHEALTHY and the 
leader closes the whole pipeline.
* If all chunks of a block are written after the recreate, PutBlock succeeds on 
the new replica only. SCM then closes the mismatched OPEN replica, and it ends 
CLOSED with a BCSID higher than the real replicas, but holding only the blocks 
written after the recreate.

SCM cannot tell the new replica from the real ones and prefers it:
* {{AbstractContainerReportHandler}} raises the CLOSED container's sequence ID 
to the new replica's BCSID (it only logs an error first, see HDDS-16448).
* {{RatisContainerReplicaCount}} counts any CLOSED replica as healthy 
regardless of its BCSID.
* {{RatisOverReplicationHandler}} picks excess CLOSED replicas by 
{{ContainerReplica.hashCode}}, so with four replicas it may delete a real one.
* {{RatisUnderReplicationHandler}} and the balancer copy from the replica with 
the highest sequence ID, which is the new one.

Repeated over moves and replica losses, the real replicas are replaced by 
copies of the new one. Reads of existing keys then fail with NO_SUCH_BLOCK. 
Once the blocks written after the recreate are deleted, every replica is empty, 
{{EmptyContainerHandler}} deletes the container, and reads fail with 
NO_REPLICA_FOUND while OM still has the keys.

HDDS-17435 is the same datanode gap reached through StreamInit replay after a 
restart. This one needs no restart and no data stream.

h3. Trigger
* Container balancer enabled (or any other forced or excess replica delete).
* A container closes and SCM deletes its replica on the pipeline leader while 
the pipeline stays open.
* A client keeps writing to a block in that container afterwards (a writer that 
allocated the block before the close).

h3. Observed
Seen on a production cluster running a release that contains HDDS-11650. One 
datanode logged 103 containers that were deleted and then OPEN again with BCSID 
0, over about 2.5 months. A typical sequence for container C on leader D1 
(followers D2, D3):
{noformat}
00:00  C closes on D1, D2, D3 at BCSID N
00:04  balancer moves C from D1 to D4, then sends a forced delete to D1
00:06  D1: C is OPEN, BCSID 0 (recreated)
00:07  D1: GetBlock for an existing block of C returns NO_SUCH_BLOCK
00:09  D1: C CLOSED at BCSID N + 824
{noformat}
For containers in a window where SCM logs were available, SCM then found four 
replicas and sent a forced delete 7 to 10 minutes after the recreate. In all 
three sampled cases the over replication delete removed a real replica (the 
balancer target, or an original pipeline member) and kept the recreated one. In 
one case all real replicas were eventually removed and the container was later 
deleted as empty while OM still referenced its keys.

h3. Reproduction
Not run yet. A mini cluster test should be able to show it: write a key so that 
the pipeline leader holds the container, close the container, force delete the 
replica on the leader with a delete container command while keeping the 
pipeline, then write another block into the same container ID through the same 
pipeline and check that the leader recreates the container and that the write 
succeeds.

h3. Suggested fix
Datanode: when a replica is deleted, keep its ID as a tombstone (for example in 
the container ID table with a deleted flag, or in the missing container set) 
instead of removing it. Reject a tombstoned ID with CONTAINER_NOT_FOUND both in 
{{validateContainerCommand}} on the leader and in the auto create path of 
{{dispatchRequest}}, so the client gets an error and allocates a new block. 
Container import and EC RECOVERING create must clear the tombstone. This also 
closes the replay path in HDDS-17435.

SCM, as defense in depth: do not raise the sequence ID of a CLOSED container 
(HDDS-16448), and treat a CLOSED replica whose BCSID differs from the 
container's sequence ID as mismatched instead of healthy.




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to