Calling external services using dynamic (HATEOAS-based) urls

My team has a Lagom microservice which exposes a resource modeled using HAL+JSON, i.e. we expose links to related resources from our resource. We let our clients set some of these links. We would like to crawl some of these links in our service to get related information, mostly in read side processors, so our service is not synchronously coupled to the external service.

Because the link is dynamically set per-resource, we don’t think we can use an injected ServiceClient. I think we possibly could use the LagomClientFactory, but we’re hesitant to do so because it seems like it would create an entire client instance per invocation, which seems heavy. We think we can use the underlying WSClient. Would the WSClient be a suggested approach here for making dynamic calls to services if their url is not know prior to the invocation? Are we overthinking avoiding the LagomClientFactory for this?

We actually have built this using the underlying WSClient, and it worked well for some time. However, we ran into an issue recently when we introduced a separate injected ServiceClient which happened to use the same url as some of the links in our resources. We started (intermittently, sometimes hours, sometimes days apart) to see some exceptions being thrown by the WSClient specifically when we call the url that the injected service client also uses. Somehow, whenever the exception is thrown, a Read Side processor silently stops executing, like it is deadlocked. So we are curious if it is intended to be safe to directly use both of these interfaces in tandem.

We are on Lagom 1.4.8. Exception trace (some links to source code to help me better understand what requirement has failed):

Caused by: java.lang.IllegalArgumentException: requirement failed

at scala.Predef$.require(Predef.scala:212)

at scala.concurrent.BatchingExecutor$Batch.run(BatchingExecutor.scala:51)

at scala.concurrent.Future$InternalCallbackExecutor$.unbatchedExecute(Future.scala:601)

at scala.concurrent.BatchingExecutor$Batch.blockOn(BatchingExecutor.scala:92)

at scala.concurrent.Await$.result(package.scala:190)

at play.api.libs.ws.ahc.cache.CachingAsyncHttpClient.execute(CachingAsyncHttpClient.scala:74)

at play.api.libs.ws.ahc.cache.CachingAsyncHttpClient.executeRequest(CachingAsyncHttpClient.scala:54)

at play.libs.ws.ahc.StandaloneAhcWSClient.execute(StandaloneAhcWSClient.java:86)

at play.libs.ws.ahc.StandaloneAhcWSRequest.lambda$execute$1(StandaloneAhcWSRequest.java:373)

at play.libs.ws.ahc.StandaloneAhcWSRequest.execute(StandaloneAhcWSRequest.java:375)

at play.libs.ws.ahc.StandaloneAhcWSRequest.execute(StandaloneAhcWSRequest.java:365)

at play.libs.ws.ahc.StandaloneAhcWSRequest.get(StandaloneAhcWSRequest.java:330)

at play.libs.ws.ahc.AhcWSRequest.get(AhcWSRequest.java:56)

Looking at this exception, it seems like we are sharing an instance of something that isn’t intended to be shared, because we broke the BatchingExecutor’s execution invariant. But I’m not the most proficient in Scala, so I could be far off base.

We are going to try removing this injected ServiceClient and just use the WSClient for these calls to see if using the two in tandem is causing some sort of locked resource, because we know in the past we had multiple threads utilizing the WSClient concurrently without problems.

Hi,

I’m using LagomClientFactory for all “dynamic” url use cases without any problems.
I did not had a case where the same url was used by a injected service client and LagomClientFactory so can not comment on the exception.
For the LagoClientFactory you should use 1.4.9 because 1.4.x searies introduced share actor system problem. Check this
If you want to reuse LagomServiceClient you should wrap it in the singleton class.

Hope this helps.

Br,
Alan

Thanks for the data point @aklikic. We swapped to use only WSClient for this endpoint a couple days ago, and haven’t had an issue yet. Too soon to call it successful, but I’ll update this thread with our results.

Even if this does work, I’d like to also swap to use the LagomClientFactory alongside an injected service client and see if that has similar issues.

We’ve been using only the WSClient for this endpoint for nearly two weeks now without a problem, so I’m pretty sure that is a solution.

Pushing a change now to try using the LagomClientFactory in conjunction with an injected service client to see if we run into the dynamically/statically defined url collision when using the LagomClientFactory with the injected service client

To close out this thread: We’ve been using the LagomClientFactory in conjunction with an injected service client for 2 months without issue.

One slight reason that I’m not 100% convinced what the problem was: Our specific usage at the start (injected service client + WSClient) was to use the injected service client only to back-populate GETs for specific resources that didn’t have data pre-populated, but all new instances of the resource would always have the data without using the service client. The old resources are retrieved less over time. This means that our usage of the injected service client has decreased over time, and perhaps by January 15, the likelyhood of a collision was so low that we still might have a problem using a LagomClientFactory in conjunction with an injected service client, but we are just unlikely to hit it.