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

Track latency, retry and backoff time of GCS JSON API requests #1207

Conversation

arunkumarchacko
Copy link
Contributor

No description provided.

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

}

@VisibleForTesting
GcsJsonApiEvent(@Nonnull HttpRequest request, EventType eventType) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just extract the meaningful information from HttpRequest and wrap it around RequestContext. Doing that will make it easier to extend it for gRPC as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For gRPC we can create another method or refactor this once we add it.

Copy link
Contributor

Choose a reason for hiding this comment

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

gRPC implementation is already there are we are even moving towards GA. Ideally, we should add this support for gRPC as well. Least we can do is, any new code added should be extendable to gRPC as well, else we are just be pilling on the tech debt.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gRPC support will come in a separate change.


private GcsJsonApiEvent(HttpRequest request, EventType eventType, int capacity) {
this(request, eventType);
this.properties = new HashMap<>(capacity);
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the purpose of setting capacity? Looking into the usage of it in getBackoffEvent it seems it will immediately allocate extra capacity as well as rehash it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is done to limit the size of the map. BackOffEvent has two additional properties, hence setting the capacity to 2.

Copy link
Contributor

Choose a reason for hiding this comment

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

setting this capacity will not limit the size of hashMap. It's actually not capacity but initialCapacity
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#HashMap-int-

There is no way you will be able to limit the size of hashMap unless ofcourse you customize and override the implementation.

Am I missing something here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you provide the capacity, it will set the capacity to the next power of 2. It will not resize. What you think it will resize?

Copy link
Contributor

Choose a reason for hiding this comment

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

As per the documentation When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed. With Default load factor of 0.75 it will rehash as soon as there are two entries in HashMap.

}

@Override
protected RequestTracker getRequestTracker(HttpRequest request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Correct me If I am wrong, With this override what we are able to use the same RequestTracker instance against all the gcs requests made in test case i.e. getMetadata, getObject, listObject etc, all the retires of the request are also captured via same tracer but in actual production scenario it will not be the case, every request will have a separate tracker.

If yes, will it even be a valid test then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, same tracker is used. Otherwise the test would have failed. Same things is happening in RetryHttpInitializer as well since since initialize is called only once.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, same tracker will be used across all retries of same request, but if there are multiple requests, say two different request to access object metadata of object1 and object2. TestRetryHttpInitializer configured here will not be able to provide different tracker but provide the same tracker which will yield weird outcomes at verification step.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will sync offline.

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

@arunkumarchacko
Copy link
Contributor Author

/gcbrun


private GcsJsonApiEvent(HttpRequest request, EventType eventType, int capacity) {
this(request, eventType);
this.properties = new HashMap<>(capacity);
Copy link
Contributor

Choose a reason for hiding this comment

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

setting this capacity will not limit the size of hashMap. It's actually not capacity but initialCapacity
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#HashMap-int-

There is no way you will be able to limit the size of hashMap unless ofcourse you customize and override the implementation.

Am I missing something here?

}

@VisibleForTesting
GcsJsonApiEvent(@Nonnull HttpRequest request, EventType eventType) {
Copy link
Contributor

Choose a reason for hiding this comment

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

gRPC implementation is already there are we are even moving towards GA. Ideally, we should add this support for gRPC as well. Least we can do is, any new code added should be extendable to gRPC as well, else we are just be pilling on the tech debt.

import com.google.common.flogger.GoogleLogger;
import java.util.concurrent.TimeUnit;

class RequestTracker {
Copy link
Contributor

Choose a reason for hiding this comment

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

similar comment.

postToEventQueue(GcsJsonApiEvent.getExceptionEveent(httpRequest));
}

void trackUnsuccessfulResponseHandler(HttpResponse response) {
Copy link
Contributor

Choose a reason for hiding this comment

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

trackResponse is called from response interceptor and trackUnsuccessfulResponseHandler is called from Unsuccessful response handler. I expect the responseInterceptor to be called no matter if response was unsuccessful. Weird.

stopWatch.stop();

if (stopWatch.elapsed().toMillis() > LOGGING_THRESHOLD) {
logger.atInfo().atMostEvery(10, TimeUnit.SECONDS).log(
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, but it's not capturing elapsedTime correct?

}

@Override
protected RequestTracker getRequestTracker(HttpRequest request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, same tracker will be used across all retries of same request, but if there are multiple requests, say two different request to access object metadata of object1 and object2. TestRetryHttpInitializer configured here will not be able to provide different tracker but provide the same tracker which will yield weird outcomes at verification step.

Comment on lines 67 to 68
assertThat(backOffTime).isAtLeast(expectedBackoffTime);
assertThat(backOffTime).isLessThan(expectedBackoffTime + 10);
Copy link
Contributor

Choose a reason for hiding this comment

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

why it varies as range?
How we came up with 10?
will it cause flakiness?
what if some adds a test where many more retires are done, this verification will still be valid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are unit tests, not actual backoff is happening. The buffer is the potential overhead. I do not expect it to be more than 10 seconds.

Copy link
Contributor

Choose a reason for hiding this comment

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

These are unit tests, not actual backoff is happening.

Well, that is not always true, is it? One can mock the transport in unit tests but still use RetryHttpIniitalizer which comes with ExponentialBackOff strategy and hence the actual backoff will will happen.

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

1 similar comment
@arunkumarchacko
Copy link
Contributor Author

/gcbrun

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

@arunkumarchacko
Copy link
Contributor Author

/gcbrun

1 similar comment
@arunkumarchacko
Copy link
Contributor Author

/gcbrun

@@ -166,4 +246,23 @@ private static RetryHttpInitializer createRetryHttpInitializer(Credentials crede
.setReadTimeout(Duration.ofSeconds(5))
.build());
}

// Helper class which help provide a custom test implementation of RequestTracker
Copy link
Contributor

Choose a reason for hiding this comment

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

I would recommend adding a note on limitation of TestRetryHttpInitializer, which is, "it can only used against test with only one request because RequestTracker is initialized along with HttpInitializer".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an inner class and is an implementation detail. I think it is obvious from the code that this applies for one request per TestRetryHttpInitializer.

@arunkumarchacko arunkumarchacko merged commit 1ba5bc2 into GoogleCloudDataproc:branch-3.0.x Jul 15, 2024
4 checks passed
@arunkumarchacko arunkumarchacko deleted the gcs-api-metrics-3-0 branch July 15, 2024 12:34
arunkumarchacko added a commit to arunkumarchacko/hadoop-connectors that referenced this pull request Jul 15, 2024
…eCloudDataproc#1207)

* Track latency, retry and backoff time of GCS JSON API requests
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