3

I am creating client libraries for our REST server. For the C# library I use HttpClient.PostAsync() which works great, it returns an object that the caller can just wait on (making it synchronous), they can complete some other actions and then wait, or they can use the C# await mechanism. Great solution.

For Java I have to write the library for Java 8, because that's the version in the largest use. With Java 8 we cover 98% of the programmers out there. (If we get enough demand, I'll do a Java 11 one also and then we have native async calls.)

So here's my question, There are ways to get async behavior, either using DeferredResult or some 3rd party classes. But is there any advantage to my building my API around this and forcing it? Because if I create a synchronous API, the caller can still call it in their own DeferredResult code. Which is the same end result.

So it seems to me the way to provide a simple straightforward API is to deliver a synchronous one. And those that want async wrap it up in whatever mechanism they prefer to make it async. An important advantage here is I don't force a mechanism or 3rd party library they don't use on them.

Is there any downside to this approach?

Update: Here it is in more detail.

If all I have is a synchronous API, then the caller can wrap my synchronous API in many different ways. The easiest using vanilla Java 8 is:

// API is: public Metrics postMetrics(Template template)    
CompletableFuture<Metrics> completableFuture = CompletableFuture.supplyAsync(() -> { return client.postMetrics(template); });

If instead I create an async API, then I am choosing which of these approaches (I would use CompletableFuture) and so the API becomes:

// API is: public CompletableFuture<Metrics> postMetrics(Template template)
CompletableFuture<Metrics> completableFuture = client.postMetricsAsync(template);

Granted, it's a bit easier with that async API. But very little difference. And the downside is I've now forced the async approach on them. Am I missing some larger advantage of providing an async API?

David Thielen
  • 28,723
  • 34
  • 119
  • 193
  • If you're _really_ wanting to be hip and modern, the current approach would be to use a reactive stack (likely with `WebClient`). – chrylis -cautiouslyoptimistic- May 30 '20 at 16:21
  • @chrylis-cautiouslyoptimistic- As we're a library customers will add to their projects, I'm trying to fit in to their stack as painlessly as possible. And across all of our customers that means pretty much any stack you can imagine, and some you've never hear of. – David Thielen May 30 '20 at 16:36

2 Answers2

0

It is not the same end result.

Synchronous I/O consumes a lot of memory for blocked threads. This is the main disadvantage of synchronous approach. Making synchronous I/O as primary, your asynchronous I/O still will require synchronous I/O and so will consume a lot of resources.

Rational approach is to make asynchronous interface primary and augment it with synchronous methods. The user then can choose if he can afford to spend memory for blocked threads or to employ multiple I/O operations using minimal resources.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
  • 1
    Ok, I'm not understanding something. Because if I create an async API using DeferredResult, I'm still making a synchronous call within that (Java 8) aren't I? In this case it returns immediately so the code can do other things, but inside that initial call there's a synchronous call waiting for the response. Is that not the case? – David Thielen May 30 '20 at 20:42
  • DeferredResult is truly asynchronous and does not contain any synchronous calls inside, unless you explicitly call DeferredResult.getResult(). I recommend expose it in your API, it already has both synchronous and asynchronous interface, and you need not to make additional layer around it. – Alexei Kaigorodov May 30 '20 at 20:50
  • 1
    I thought somewhere, something, somehow is always waiting for the result from the API call to come back. It might be in a layer far away from the actual one the DEV use to make the call but still there is something waiting for the result to come back. – Amar Dev May 31 '20 at 10:39
  • I just put up what I see as the two different approaches. Am I missing something in what I could provide with an async API? – David Thielen Jun 06 '20 at 21:26
  • @AmarDev something which waits can be of two kinds - thread or callback procedure. So two kinds of I/O - synchronous and asynchronous. – Alexei Kaigorodov Jun 07 '20 at 06:18
  • 1
    @AlexeiKaigorodov A callback procedure must be invoked. As a developer, I dont invoke the callback and expect it to be invoked when the async operation finishes. The code which invokes it must be waiting for the result, right ? Or have I got it all wrong. – Amar Dev Jun 07 '20 at 08:58
  • @AmarDev when an I/O operation finishes, a hardware interrupt is risen, which calls a procedure from operating system, which looks at the activities waiting for this operation. Threads are put to the queue to processors, callbacks are put to the queue to the thread pool. – Alexei Kaigorodov Jun 07 '20 at 10:53
  • @AlexeiKaigorodov The question is about client API design and not server async processing. The client will not necessarily be used by a server and calling an endpoint that does or does not use async processing does not require the client to be async. Even if the client is async, a client Thread is waiting for the result. – fdelsert Dec 01 '20 at 10:11
  • no, client need not be synchronous. Client and server I/O are identical, except for the initial moment of opening connection. – Alexei Kaigorodov Dec 01 '20 at 12:20
0

Perhaps you should think in these terms, what if tomorrow there is another competing library that provides async behavior out of the box and sync as well ?

If I was a developer, I would perhaps choose the one that allows me the async behavior out of the box and also gives me the options to configure it.

What I have said only makes sense if you are thinking in terms of easy developer adoption and inbuilt ease of making async API calls.

I hope this helps.

Amar Dev
  • 1,360
  • 2
  • 18
  • 38
  • I just added an update listing out the code difference. Would you prefer it force the one async approach? – David Thielen Jun 06 '20 at 21:25
  • 1
    clientBuilder.build() gives the sync version of the client, but clientBuilder.Async().build() gives an async client. The ones who want to be aync but not the default one given by clientBuilder.Async().build() can wrap the clientBuilder.build() as per their need. This way you dont force a specific way and yet give reliable defaults and flexibility to customize. – Amar Dev Jun 07 '20 at 09:06