1

I'm writing a Nodejs based service, N, that sits between entities A and B.

A <----> N <----> B

App A POSTs a synchronous request to N, which N must respond to with data it can only get by calling an external service B.

The problem is, B is an asynchronous service/API: Anything N POSTs to B is responded to first with an HTTP 202 ("request accepted"), and then later with the actual data as a callback POST request from B to N. So, essentially, inside the single-threaded N, I have to somehow keep the current, synchronous request from A "on hold" without blocking other potential requests from A to N while the asynchronous message exchange between N and B completes to service the request from A currently on hold.

Is this possible in Nodejs, say, using promises or async/await magic?

Harry
  • 3,684
  • 6
  • 39
  • 48
  • Typically a 202 Accepted response would include a Location the client could poll for information on progress and eventually the result. If B is making the request back to N, the equivalent would be to have the data from that request stored somewhere that the first request could be polling with a given delay (e.g. checking to see if the relevant row in a database had been updated). – jonrsharpe May 05 '22 at 11:21
  • Does this answer your question? [Polling until getting specific result?](https://stackoverflow.com/questions/46208031/polling-until-getting-specific-result) – jonrsharpe May 05 '22 at 11:23
  • For what time range are we talking? seconds, minutes or even hours? If we are talking about seconds, just keep the connection from A open and do nothing till you get everything from B back. Keep in mind that there are timeouts for TCP connections can destroy this practice. BTW: All in all, this is a bad practice. – Marc May 05 '22 at 11:24
  • @Marc We are talking seconds, maybe at most a minute. – Harry May 05 '22 at 11:37
  • @jonrsharpe B is an external service that I don't have a control over. Its 202 responses don't include a Location that can be polled; the response comes after a 202 as an independent POST. The call and the callback POSTs to and from B are tracked via IDs in the requests. – Harry May 05 '22 at 11:41

1 Answers1

1

With request id, local cache & timeout, you could do something like this:


const requestIdAndResponseDetailsMap = {};


router.post('/request-from-a', function requestFromAHandler(request, response) {

  const requestId = request.body.id;
  makeCallToB(request.body); // ensure that request id is passed to B and sent in the call from B to N.
  cacheServiceAResponseWithTimeout(requestId, response);

});

router.post('/callback-from-b', function callbackFromBHandler(request, response) {

  const dataFromB = request.body;

  tryRespondToA(dataFromB);

  response.json({ success: true });

});

function tryRespondToA(responseFromB) {
  const requestId = responseFromB.id;
  const cachedData = requestIdAndResponseDetailsMap[requestId];

  if(cachedData === undefined) {
    return;
  }
  delete requestIdAndResponseDetailsMap[requestId];
  clearTimeout(cachedData.timerReference);
  cachedData.response.json(responseFromB);
  
}

function cacheServiceAResponseWithTimeout(requestId, response) {
  
  const timerReference = setTimeout(function timerCallback() {
    const cachedData = requestIdAndResponseDetailsMap[requestId];
    delete requestIdAndResponseDetailsMap[requestId];
    cachedData.response.json({ message: 'Service B request timed out' });
  }, TIMEOUT_IN_MILLISECONDS);

  requestIdAndResponseDetailsMap[requestId] = { response, timerReference };

}


  • It will take me some time to process your answer and get back with comments/questions. Thanks, and meanwhile upvoting. – Harry May 05 '22 at 12:39
  • It should work, only that I will first do it without timeouts. Reason being twofold: (1) I'm not sure if a timer per request will scale both in terms of timer resource memory and their scheduling accuracy. (2) I'm pretty much certain that B will call back even if it's a little delayed, so I'll let the underlying http/s layer handle response timeouts. – Harry May 12 '22 at 12:21
  • 1
    @Harry, `timeout` is needed for the worst case scenario. That is `What if B never calls back, due to internal issues, network issues? What if A needs to know either the result or error in some duration?` Of course it is upto you, if you want to implement the timeouts or not. But, being critical about that might prevent suffering in future. – Tamil Vendhan Kanagarasu May 13 '22 at 04:30