gax-java icon indicating copy to clipboard operation
gax-java copied to clipboard

UnaryCallable.call(request) never returns if access token cannot be obtained

Open devchas opened this issue 4 years ago • 15 comments

I am working on this bug in the Google Ads client library for Java. I have narrowed the cause to the fact that UnaryCallable.call(request) never returns when instantiated with RequestT of SearchGoogleAdsRequest and ResponseT of GoogleAdsServiceClient.SearchedPageResponse IF the access token cannot be created (e.g. if the refresh token is incorrect).

To reproduce, try running this example with an invalid refresh token.

The issue does not exist with other types of RequestT and ResponseT even with an invalid refresh token (e.g. UnaryCallable<MutateCampaignsRequest, MutateCampaignsResponse>).

This issue may actually stem from the OAuth library used in this library. However, I've tried tracing the issue, and my lack of knowledge regarding this codebase makes it challenging to actually see what's going on once the call method is invoked.

devchas avatar Mar 11 '20 21:03 devchas

Just checking in on this.

devchas avatar Jul 22 '20 23:07 devchas

Hey folks, just following up on this. Is it possible to investigate? We have a workaround available which is to use streaming instead of paging RPC. It would also be fine if this is resolved in the Java microgenerator.

nwbirnie avatar Aug 20 '20 11:08 nwbirnie

@miraleung , any updates on this?

hkdevandla avatar Sep 09 '20 20:09 hkdevandla

We're tracking this in the microgenerator and will address it after code completion.

miraleung avatar Sep 16 '20 04:09 miraleung

Hey Mira, did you get a chance to look at this one?

nwbirnie avatar Jan 22 '21 13:01 nwbirnie

Not yet for the microgenerator, but I can pick this up now. Could you please provide some repro snippets and env setup so I could try this out?

miraleung avatar Jan 22 '21 21:01 miraleung

Sure, here's a project that triggers the issue.

Run with ./gradlew run.

I also grabbed a thread dump of it hanging on my machine in case it's helpful.

nwbirnie avatar Jan 25 '21 18:01 nwbirnie

Friendly ping :)

nwbirnie avatar Feb 08 '21 10:02 nwbirnie

Thanks for the ping. We have a Fixit next week, so I've earmarked this for more digging then.

miraleung avatar Feb 08 '21 20:02 miraleung

I tried out the sample with invalid credentials from a different library (which is the same approach used in gax-java's ClientContextTest). In this case, it fails immediately with the error below and does not hang. As such, this is looking like an OAuth bug to me rather than a gax-java issue.

Please feel free to reopen if you think we should dig into this more.

Exception in thread "main" com.google.api.gax.rpc.UnauthenticatedException: io.grpc.StatusRuntimeException: UNAUTHENTICATED: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
        at com.google.api.gax.rpc.ApiExceptionFactory.createException(ApiExceptionFactory.java:73)
        at com.google.api.gax.grpc.GrpcApiExceptionFactory.create(GrpcApiExceptionFactory.java:72)
        at com.google.api.gax.grpc.GrpcApiExceptionFactory.create(GrpcApiExceptionFactory.java:60)
        at com.google.api.gax.grpc.GrpcExceptionCallable$ExceptionTransformingFuture.onFailure(GrpcExceptionCallable.java:97)
        at com.google.api.core.ApiFutures$1.onFailure(ApiFutures.java:68)
        at com.google.common.util.concurrent.Futures$CallbackListener.run(Futures.java:1041)
        at com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:30)
        at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1215)
        at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:983)
        at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:771)
        at io.grpc.stub.ClientCalls$GrpcFuture.setException(ClientCalls.java:563)
        at io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:533)
        at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:413)
        at io.grpc.internal.ClientCallImpl.access$500(ClientCallImpl.java:66)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:742)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:721)
        at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
        at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
        Suppressed: com.google.api.gax.rpc.AsyncTaskException: Asynchronous task failed
                at com.google.api.gax.rpc.ApiExceptions.callAndTranslateApiException(ApiExceptions.java:57)
                at com.google.api.gax.rpc.UnaryCallable.call(UnaryCallable.java:112)
                at com.google.ads.googleads.v6.services.GoogleAdsServiceClient.search(GoogleAdsServiceClient.java:170)
                at com.google.ads.googleads.v6.services.GoogleAdsServiceClient.search(GoogleAdsServiceClient.java:159)
                at Issue.main(Issue.java:74)
Caused by: io.grpc.StatusRuntimeException: UNAUTHENTICATED: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
        at io.grpc.Status.asRuntimeException(Status.java:533)
        ... 14 more

miraleung avatar Feb 18 '21 19:02 miraleung

Hey pretty sure that this is an issue with GAPIC retries. I've added logging to the test project and you can see one error message repeats (I'm sure it will eventually give up). It looks like an issue with retrying failed OAuth requests... we shouldn't be attempting to retry these, the OAuth server is returning 400 bad request which should immediately fail the GAPIC call.

Logs here Updated test project here

nwbirnie avatar Mar 10 '21 16:03 nwbirnie

More generally I think there's some subset of the OAuth errors that we should never retry. This is one of them (invalid credentials), but there could be others such as invalid grant (a revoked token).

nwbirnie avatar Mar 15 '21 18:03 nwbirnie

Thanks for the updates Nick, I'll take another look.

miraleung avatar Mar 15 '21 20:03 miraleung

Sijun will dig into this further - thank you!

miraleung avatar Mar 19 '21 20:03 miraleung

I think I find the root cause. Filed an issue in grpc-java, https://github.com/grpc/grpc-java/issues/8003

arithmetic1728 avatar Mar 22 '21 21:03 arithmetic1728

This is not an issue GAPIC. GAPIC retries whatever codes are "retryable".

meltsufin avatar Oct 05 '22 15:10 meltsufin