question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature Request] Non-Blocking Async Interceptors for Async calls

See original GitHub issue

Introduction

In current interceptor model, it’s impossible to run time consuming async operations without blocking the that interceptor’s thread.

We can still do some parallel operations by awaiting the thread, for example:

.addInterceptor(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        if(response.code() == STATUS_THAT_NEEDS_TIME_CONSUMING_OPERATION_TO_HANDLE) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    // Show UI and take some credentials from user by using UI thread
                    // Access to remote authentication server
                    // do some time consuming cryptographic parallel operation using multiple CPU core
                    // store generated credentials via OS services
                    countDownLatch.countDown();
                }
            });
            countDownLatch.await(); // interceptor waits here until all other parallel operations get done.
            return chain.proceed(modifyRequestWithTheResultOfLongOperation(chain.request()));
        }
        return response;
    }
})

Dispatcher in OkHttpClient allows us the create new thread as much as the count of Integer.MAX_VALUE for AsyncCall’s. So we don’t get starve about threads. But I don’t sure about the solution above is good for performance or best practice.

Motivation

Having non-blocking interceptors looks like more suitable for async calls. I mean, interceptors may be implemented like Callbacks. In this way, we can proceed interceptor chain whenever we want, without the blocking interceptors thread.

Proposed Solution

In order to achieve this, we can create AsyncInterceptor interface like Interceptor interface. For instance, that interface and OkHttpClient implementation may be like this:

public class OkHttpClient {
  ...
  public List<Interceptor> interceptors();
  public List<Interceptor> networkInterceptors();
  public List<AsyncInterceptor> asyncInterceptors();
  ...
}
public interface AsyncInterceptor {
  void interceptRequest(Chain chain) throws IOException;
  void interceptResponse(Chain chain) throws IOException;

  interface Chain {
    Request request();
    Response response();
    void proceed(Request request) throws IOException;
    void proceed(Response response) throws IOException;
    void proceed(Exception error);
    ...
  }
}

In that case, interceptor chain should be look like this: okhttp-async-interceptors

Same Example above with non-blocking interceptor

.addAsyncInterceptor(new AsyncInterceptor() {
  @Override
  public void interceptRequest(Chain chain) throws IOException {
    chain.proceed(chain.request());
  }
  @Override
  public void interceptResponse(Chain chain) throws IOException {
    if(chain.response().code == STATUS_THAT_NEEDS_TIME_CONSUMING_OPERATION_TO_HANDLE) {
      executor.submit(new Runnable() {
        @Override
        public void run() {
          // Show UI and take some credentials from user by using UI thread
          // Access to remote authentication server
          // do some time consuming cryptographic parallel operation using multiple CPU core
          // store generated credentials via OS services
          // Modifies request and sends again with Chain#proceed(Request)
          chain.proceed(modifyRequestWithTheResultOfLongOperation(chain.request()));
        }
      });
    }
    // Chain#proceed(Response)
    chain.proceed(chain.response());
  }
});

POC?

I implement basic POC version here: https://github.com/tolpp/okhttp/commit/3f3da9f284c06a599aa12861118c771038c82c05 This is not production ready, only for poc and show my purpose.

Is anyone else faced with same problem?

I can’t find too much actually. Some of that I find: This is the close one:

Maybe:

Similar issues:

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:2
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

20reactions
ZakTaccardicommented, Mar 21, 2019

I think this kind of problem is better solved at the application layer

It’s certainly solvable because we can attach headers asynchronously before delivering the initial request to OkHttpClient, but it’s certainly not better solved.

As coroutines users, we leverage actor coroutines to own and store state - this is how we achieve simple thread safety. This becomes a problem for us when APIs (like an OkHttpInterceptor) requests synchronous access to data. This also leads to developers being lazy and leveraging runBlocking to access the async actor data, simply because of the OkHttpInterceptor API. This leads to deadlocks in some rare edge cases.

In the builder of our SDK, we provide () -> SomeData (sync) and suspend () -> SomeData (async) overloads. If a client injects the sync overload, it gets internally wrapped by an async overload and our SDK only uses the async overload. That said, this is trivial to do because of coroutines (which OkHttp3 does not leverage). @swankjesse Maybe this is possible for something like OkHttp4 or OkHttp5?

1reaction
ZakTaccardicommented, Dec 12, 2018

hopefully this will be on the wishlist for 4.0 #2903

Read more comments on GitHub >

github_iconTop Results From Across the Web

17.13 Nonblocking I/O - Java Platform, Enterprise Edition
Table 17-4 Nonblocking I/O Support in javax.servlet. ... The service method first puts the request in asynchronous mode by calling the startAsync() method ......
Read more >
Chapter 31. Asynchronous HTTP Request Processing
Asynchronous HTTP Request Processing is a relatively new technique that allows you to process a single HTTP request using non-blocking I/O and, ...
Read more >
40.4. Implementing an Asynchronous Client with the Polling ...
The client invokes the asynchronous operation and periodically checks to see if the result is returned. Example 40.6. Non-Blocking Polling Approach for an ......
Read more >
Retrofit — Synchronous and Asynchronous Requests
Retrofit supports synchronous and asynchronous request execution. Users define the concrete execution by setting a return type (synchronous) or ...
Read more >
10 Asynchronous Programming 3.0.17 - The Grails Framework
For example, if you have 70 available container threads and an action takes a minute to complete, if the actions are not executed...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found