Skip to content

HDDS-16722. Serve ReadBlock over Ratis read-only data streams on the datanode - #11412

Open
peterxcli wants to merge 3 commits into
apache:masterfrom
peterxcli:HDDS-16722
Open

peterxcli wants to merge 3 commits into
apache:masterfrom
peterxcli:HDDS-16722

Conversation

@peterxcli

@peterxcli peterxcli commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

HDDS-9904 adds block reads over Ratis data streams. Ratis 3.3.1 supports read-only data streams (RATIS-1240): the client sends one request, and the server sends the data back in a sequence of replies. This PR adds the datanode side for ReadBlock. The client side comes in later sub-tasks of HDDS-9904.

Changes:

  • ContainerStateMachine#transferTo serves ReadBlock and rejects other commands. It passes the request to the dispatcher's streaming read (streamDataReadOnly, the same one the gRPC streaming read uses) and writes each response to the stream as one reply, with layout: [int: metadata length][the response without its data][data].
    • When the read is done, it closes the stream, which sends the terminal reply.
    • When the read fails (for example, the offset is past the end of the block, or the connection breaks), it throws without closing the stream, and Ratis sends the failure as the terminal reply.
    • An error response from the dispatcher, such as CONTAINER_NOT_FOUND, is sent as a reply with no data, so the client still gets the result code.
  • ClosedContainerReadResolver is a Ratis DataStreamApi.Resolver (RATIS-2603), registered in XceiverServerRatis. Ratis asks it before it looks up the Raft group. It serves the read by container ID when the request is a ReadBlock, the container is CLOSED, and the request names this datanode. For any other request it returns null, and Ratis uses the Raft group as before. A closed container does not change, so its read needs neither the group nor the leader.

Block tokens are checked as in the gRPC read path: the dispatcher validates the token when the DispatcherContext is null.

Each reply is built in one heap buffer, which copies the data once more. Removing that copy is left to a later sub-task. because it require:

  1. streamDataReadOnly and Handler.readBlock take a new observer type that keeps data outside the protobuf and supplies the buffer to read into.
  2. A rewrite of the readBlockImpl, which would be easier to do after HDDS-16258. Refactor streaming block reads and fix checksum verification for variable-sized chunks #11302 is merged.
  3. Pooled direct buffers.

What is the link to the Apache JIRA

https://issues.apache.org/jira/browse/HDDS-16722

How was this patch tested?

  • New tests in ContainerStateMachineTests (run by TestContainerStateMachineLeader and TestContainerStateMachineFollower):
    • each response becomes one reply with the response without its data, then the data;
    • an error response becomes a reply without data;
    • a failed read or a failed write throws and leaves the stream open, and nothing more is written after a failed write;
    • commands other than ReadBlock are rejected.
  • New TestClosedContainerReadResolver: the resolver serves ReadBlock only for a closed container on this datanode.
  • New TestStreamRead#testRatisStreamReadBlock: on a mini cluster, a plain Ratis client reads a block on a read-only stream, first from the open container through its Raft group, then, after the container is closed, through the read pipeline from SCM, which has a random ID and no Raft group. Both reads match the block file.

Copilot AI balanced review requested due to automatic review settings October 5, 2026 16:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@peterxcli
peterxcli marked this pull request as ready for review October 6, 2026 03:19
# Conflicts:
#	hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/transport/server/ratis/ContainerStateMachine.java
*
* @return the number of bytes written to the stream
*/
static long streamReadBlock(ContainerDispatcher dispatcher, ContainerCommandRequestProto request,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Curious why we make this as static? Is it for easy testing?

CertificateClient caClient, StateContext context) throws IOException {
Parameters parameters = createTlsParameters(
new SecurityConfig(ozoneConf), caClient);
if (parameters == null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In this case, createTlsParameters could just return new Parameters() than null?

} catch (IOException e) {
error.set(e);
// Throw to stop reading the block
throw new UncheckedIOException(e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we let UncheckedIOException propagate through KeyValueHandler.readBlock? It currently becomes CONTAINER_INTERNAL_ERROR and triggers a container scan on client disconnect. Please add a test asserting no scan is triggered.

}

final Container<?> container = containerController.getContainer(requestProto.getContainerID());
if (container == null || container.getContainerState() != CLOSED) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this also accept QUASI_CLOSED? SCM returns a read pipeline without an existing Raft group for that state too.

@rich7420

rich7420 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

@peterxcli thanks for the patch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants