Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add StreamMessage.timeout() #5761

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

sjy982
Copy link
Contributor

@sjy982 sjy982 commented Jun 13, 2024

Motivation:
Currently, the aggregate() and subscribe() methods of StreamMessage do not have the ability to set a timeout. This provides the ability to detect when a client or server has not responded for a period of time and handle it appropriately. Additionally, the timeout API can be used to detect idle streams by setting a timeout until the next message.

Modifications:

  1. Added the TimeoutStreamMessage class
  • Wrap a StreamMessage to provide timeout functionality
  • You can set a timeout feature by adding the timeout() method to the StreamMessage interface.
  1. Added the TimeoutSubscriber class
  • Schedule a timeout for each message.
  • StreamTimeoutMode allows you to set different timeout modes.
  1. Added StreamTimeoutMode enumeration
  • Defines the UNTIL_FIRST, UNTIL_NEXT, and UNTIL_EOS modes.
  1. Added a timeout method
  • Added the timeout method to the StreamMessage, HttpResponse, and HttpRequest interfaces to provide the ability to set a timeout.

Result:

  • Closes Add StreamMessage.timeout() #5744
  • This change adds timeout functionality to the aggregate() and subscribe() methods of StreamMessage and HTTP requests/responses.
  • This allows idle streams to be detected and handled appropriately.
  • You can use StreamTimeoutMode to set the timeout between the arrival of the first message, the arrival of the next message, or the end of the stream.

@CLAassistant
Copy link

CLAassistant commented Jun 13, 2024

CLA assistant check
All committers have signed the CLA.

@Bue-von-hon
Copy link
Contributor

Some of the descriptions in the Modifications field contain Korean.

Added StreamTimeoutMode enumeration
UNTIL_FIRST, UNTIL_NEXT, UNTIL_EOS 모드를 정의합니다.

p.s. It's an interesting feature, so I'm watching with interest

@sjy982 sjy982 requested a review from trustin June 24, 2024 13:22
@sjy982
Copy link
Contributor Author

sjy982 commented Jun 24, 2024

Some of the descriptions in the Modifications field contain Korean.

Added StreamTimeoutMode enumeration
UNTIL_FIRST, UNTIL_NEXT, UNTIL_EOS 모드를 정의합니다.

p.s. It's an interesting feature, so I'm watching with interest

Thank you for your interest.

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

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

Thanks! Please make the build pass as well.

long currentTimeNanos = System.nanoTime();
long elapsedNanos = currentTimeNanos - lastOnNextTimeNanos;

if(elapsedNanos <= timeoutNanos) {
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we should use < instead of <=? If elapsedNanos == timeoutNanos, it means it's been timed out.

long elapsedNanos = currentTimeNanos - lastOnNextTimeNanos;

if(elapsedNanos <= timeoutNanos) {
long delayNanos = timeoutNanos - (currentTimeNanos - lastOnNextTimeNanos);
Copy link
Member

Choose a reason for hiding this comment

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

We don't need to calculate this more than once:

long delayNanos = timeoutNanos - (currentTimeNanos - lastOnNextTimeNanos);
if (delayNanos > 0) {
    timeoutFuture = ...;
}

}

private ScheduledFuture<?> scheduleTimeout() {
return executor.schedule(() -> {
private Runnable createTimeoutTask() {
Copy link
Member

Choose a reason for hiding this comment

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

Not very pretty, but how about making TimeoutSubscriber implement Runnable to save one object allocation? i.e.

final class TimeoutSubscriber implements Runnable, ... {
  @Override
  public void run() {
    if (timeoutMode == ...
  }
}

@@ -29,70 +29,88 @@
import io.netty.util.concurrent.ScheduledFuture;

public class TimeoutSubscriber<T> implements Subscriber<T> {
Copy link
Member

Choose a reason for hiding this comment

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

Can we hide this class from the public API? i.e. final class TimeoutSubscriber ...

@trustin
Copy link
Member

trustin commented Jun 25, 2024

Last but not least, please sign the ICLA.

@sjy982
Copy link
Contributor Author

sjy982 commented Jun 25, 2024

Last but not least, please sign the ICLA.

I checked, thank you for your review.

@sjy982 sjy982 force-pushed the feature/stream-message-timeout branch from 7c52cef to d1ea297 Compare June 25, 2024 15:37
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Looks mostly done 👍 Left a couple of comments regarding edge cases

Comment on lines +64 to +65
subscription.cancel();
delegate.onError(new TimeoutException(String.format(TIMEOUT_MESSAGE, timeoutDuration.toMillis(), timeoutMode)));
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no guarantee that subscription.cancel will synchronize with upstream to not send additional signals.

e.g.delegate.onError could be called twice, once from L65 and once from L108, or delegate.onError (L65) and delegate.onComplete (L114) could be called.

If a Subscription is cancelled its Subscriber MUST eventually stop being signaled ... The reason for eventually is because signals can have propagation delay due to being asynchronous.

ref: https://github.com/reactive-streams/reactive-streams-jvm?tab=readme-ov-file#1.8
ref: https://github.com/reactive-streams/reactive-streams-jvm?tab=readme-ov-file#1.7

}

@Override
public void abort() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Question) If TimeoutStreamMessage#abort is called, shouldn't we also cancel the scheduled timeout future as well?

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

Successfully merging this pull request may close these issues.

Add StreamMessage.timeout()
6 participants