4

A part of a system I am currently reviewing, consists of a master node which calls several slave nodes using REST APIs.

The slave nodes are scattered throughout the world on different servers, and the master can run the job on the servers up-to every minute. The result of the API call will not take longer than about 50 seconds. Both the master and the slave are based on node.js, with the slaves using Express framework.

For reference, here is a simplified diagram: master communicating with three slaves using REST

Once the master node makes requests to all slave nodes, it waits for all requests to either complete (or timeout), and processes the resulting data.

According to this StackOverflow post, it is not okay for a web application to wait several minutes before returning a response. What about in a case where the job takes less than a minute, but still more than a few seconds, is it appropriate to use a blocking REST call in this way? Considering that this is a non-public API, the fact that a slave failure/unavailability is still considered useful data for the master node as well as the fact that the master cannot proceed without data from all slaves.

  • Does Master sends the upstream requests sequentally? Does it has to process the responses in specific order? – Laiv Mar 14 '19 at 16:26
  • @Laiv it calls the events asynchronously at the same time, the order doesn't matter at all. – J. Pinkman Mar 18 '19 at 23:42
0

According to this StackOverflow post, it is not okay for a web application to wait several minutes before returning a response

I would dare to say that it's not convenient for the major part of the web developments. The problem is not at keeping connections alive, it's at locking resources on the server-side. Create, run and hold threads is costly. Mainly in terms of memory. The thing is, if we don't release resources quickly, the capacity of the server for allocating more threads decrease, hence the throughput.

Fortunately, that should not be a problem for you because NodeJS is mono-thread and it doesn't lock resources and remain idle. Unless you force it. If you force it to wait for responses, you will turn a non-blocking performant IO process into a non-so-performant and blocking one, what would defeat the nature of NodeJS and one of its most valuable features.

it calls the events asynchronously at the same time, the order doesn't matter at all

Then wait for the responses asynchronously too. This situation seems suitable (IMO) for an enterprise integration design pattern based on messages. Briefly summarised, it could be something similar to :

  • The master starts a new transactio and provide it with a correlation identifier or Transaction ID.
  • The master sends a message to each worker involved in the transaction (or to a broker). The message includes the transaction ID, the instructions and the endpoint to report to.
  • The wroker does the job and send a report (result) back to the master by request to the reporting endpoint.

For example:

{   
    "headers":{ 
      "Transaction-ID":"xxxx", 
      "Transaction-URI":"proto://host:port/endpoint/transaction/xxxx"
      "Created":"yyyy-MM-ddTHH:mm:ssZ" 
    }, 
    "body":{ ... }     
}

the fact that a slave failure/unavailability is still considered useful data for the master node as well as the fact that the master cannot proceed without data from all slaves.

You have two options.

  1. Make the master careless. it doesn't care how long takes for all the slaves to respond. It will gather all the responses eventualy.

  2. Make the transaction expirable. Set it with a timout.

-The result of the API call will not take longer than about 50-

Looks like you already have it. If the transaction expire, the master sets the transaction as finished/unfinished and no longer updates the state. If workers respond late, you could track it too.

If you make the reporting endpoint idempotent, workers shall not care whether they respond on time or not. The master will care.

Altogether, all protocol is quite fire-and-forget, and this is (naively oversimplified) what NodeJS is good at.

Considering that this is a non-public API

It doesn't matter.

The master can proceed as @Hans-MartinMosner suggests. It can respond with Http Status 202 Created to the clients alongside with an estimation (60s ?) for the client to wait before proceeding to fetch the transaction result.

If you make transctions persistent, you could even allow clients to track the state of the transaction. From "Aknowledged" to "In process" and "Finished successfully" or "Finished with errors".

| improve this answer | |
8

The key insight here is to treat job activations as resources that have a significant lifetime.

In a similar situation, I have implemented job creation using a POST method that returns a "202 Accepted" response with the location of the newly created job.

The client can then poll the status of this job until it is finished.

To avoid unreasonably frequent polling or unnecessary delays, I'm using a HTTP Prefer header with a wait attribute for the polling requests, which works reasonably well. I can't judge whether I'm fully within the usage patterns as intended by the RFC authors, but as this is an internal application with client and server coded by me, this doesn't really matter.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.