Skip to content

HDDS-16324. XceiverClientGrpc.streamRead should wait for flow-control readiness via onReadyHandler instead of a 10 ms poll - #11195

Open
ss77892 wants to merge 1 commit into
apache:masterfrom
ss77892:HDDS-16324
Open

HDDS-16324. XceiverClientGrpc.streamRead should wait for flow-control readiness via onReadyHandler instead of a 10 ms poll#11195
ss77892 wants to merge 1 commit into
apache:masterfrom
ss77892:HDDS-16324

Conversation

@ss77892

@ss77892 ss77892 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

XceiverClientGrpc.streamRead should wait for flow-control readiness via onReadyHandler instead of a 10 ms poll
XceiverClientGrpc.streamRead() waits for the gRPC request stream to become ready (flow control) by polling isReady() in a loop with a 10 ms sleep between checks. This adds up to 10 ms of latency to every send that hits back-pressure, burns CPU on a busy client, and keeps the caller waiting for the full timeout if the call fails in the meantime, since a terminated stream never becomes ready.
PR uses the readiness callback that gRPC already provides instead of polling. initStreamRead() now wraps the reader in a ClientResponseObserver so the onReadyHandler can be registered in beforeStart(), the only place gRPC allows it. The handler wakes the sender via StreamingReadResponse.signalReady().streamRead() calls the new StreamingReadResponse.awaitReady(timeout), which blocks on a monitor until the stream is ready, the timeout expires, or the call terminates. onError/onCompleted call signalTerminated(), so a sender blocked in awaitReady() fails immediately with the termination cause instead of waiting out the timeout.

What is the link to the Apache JIRA

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

How was this patch tested?

UTs has been added.

… readiness via onReadyHandler instead of a 10 ms poll

Co-authored-by: Claude Opus 5.0
@ss77892

ss77892 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

The failure is not related to the patch.
cc: @sodonnel, @chungen0126

@chihsuan chihsuan 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.

Thanks @ss77892 Overall, this looks good. Left a few small inline comments.

throw new TimeoutIOException("Timed out waiting for stream to become ready: " + streamObserver);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new InterruptedIOException("Interrupted while waiting for stream to become ready: " + streamObserver);

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.

nit: this line predates the PR, but awaitReady() now gives us a real InterruptedException to work with. Would it be worth preserving the interrupt cause here?

final AtomicBoolean result = new AtomicBoolean();
final Thread waiter = new Thread(() -> {
try {
waiting.countDown();

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.

I wonder if there's a race here. countDown() fires before the waiter calls awaitReady(), so this could pass even with a broken notifyAll() if the main thread wins that race. It may be helpful to add a spurious notification case.

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