Node.js API - Duplicate data requests from multiple clients - javascript

I have a Node.JS api using Restify which is basically an aggregater for several other API's for use inside a Google sheet.
When the Google sheet is initially opened, it makes several requests to my server to request a bunch of data from various API's, which my server then dutifully looks up and returns.
I have implemented rudimentary memory based caching - If a request for the same data comes in it will serve it from memory until an expiry time is reached (I'm open to moving to Redis for this soon).
My issue is that quite regularly a second request for the same data will come in while the first request is still being looked up/parsed/served, meaning I'm requesting the same (several gigabytes) of data in parallel.
How can I effectively pause the second request and have it wait until the data is available from the first request? I don't mind having a high timeout and waiting for the first request to end before the second starts, or alternatively some sort of "back off and try again in a minute" logic is doable.
I imagine some sort of promises or saving callbacks somewhere would be best for this, but I'm not sure what kind of best practices or suggested methods there are for this.
It is not possible for me to regularly request and cache the data server-side as the potential range of values that clients can request is fairly high.

Keep a cache of promises. If a request is in progress, the cache will indicate it was already requested and you can still await the unresolved promise, then respond to both requests when the data is ready.
The rest of your code will need to be refactored to await the values in the cache regardless of whether the promises are resolved or not, and to add entries to the cache when a request is initiated rather than completed.
If a promise in the cache is already resolved, then await will only stall the asynchronous flow of your user function by a single tick in the event loop, meaning you get the advantage of seeing pending requests basically for free.

Related

Fastest way to make a million POST requests to a cloud function?

I have an array with a length of one million. Each element is a string. I have a cloud function that takes a string and processes it. What is the fastest way to POST all million strings in my array to the cloud function? I don't care for the response of the cloud function. Ideally, it would POST, not wait for a response, then move on and POST the next one, iterating through the entire list as fast as possible. The issue is apparently with HTTP you cannot not wait for a response. You must wait for the response. Each cloud function takes about 10 seconds to execute. So if I need to wait for each response before moving to the next one, this would take 10 million seconds. Whereas if I could post each and not wait, I could probably run through the entire array in a few seconds.
A lot of this has been covered before in prior questions/answers, but none that I found is a pure duplicate of what you're asking so I'll reference some that have come before and add some explanation. First the ones that have come before:
How to make millions of parallel http requests from nodejs app
How to fire off 1,000,000 requests
Is there a limit to how many promises can or should run concurrently when making requests
In Node js. How many simultaneous requests can I send with the "request" package
What is the limit of sending concurrent ajax requests with node.js?
How to loop many http requests with axios in node.js
Handling large number of outbound HTTP requests
Promise.all consumes all my RAM
Properly batch nested promises in Node
How can I handle a file of 30,000 urls without memory leaks?
First off, you can send a lot of parallel outbound requests. You do not have to wait for a prior response before sending the next one.
Second, you have resource limits on both client and server and ultimately, you will have to explore with testing your local configuration and your target server to find out where those resource limits are and then write your code to stay within those limits. There is no way to reliably send a request and then immediately kill the socket because you don't care about the response. If your socket gets queued by the target server (because you've already overwhelmed it), then killing the socket may drop it from the target server's queue before it gets processed by the target server.
Your local configuration will be limited by how many simultaneous sockets you can have open and how much memory you have (as each outbound request takes some amount of memory to keep track of).
The target server will be limited by its own resources. It may have protections built-in to limit how many posts/sec it can received from one particular source (rate limiting). It may have overall server protections against how many incoming requests at once it can handle. Typically servers protect themselves from overload by configuring things so that once an incoming request queue gets to a certain level, they just immediately hang up on new requests. The idea is to provide some level of protection of service and just deflect new requests when they come in too fast.
If this isn't your target server and there isn't any documentation about what its limits are supposed to be, then you will just have to test how many simutaneous requests you can have "in-flight" at the same time. If they implement rate limiting from a given source, then it's not uncommon that this might be a fairly low number such as 5. If no rate limiting, then you're really just trying to figure out what their http server can handle without causing it to drop connections in defense of service.
Once you figure out (with testing) how many simultaneous requests in flight the target server can comfortably handle, you will have to structure your code to deliver that. Usually, you would take an approach like is show in this mapConcurrent() function where you code things so that only N requests are in flight at the same time where N is a number you figured out experimentally by testing the target server.
Relevant pieces of helper code:
mapConcurrent(array, maxConcurrent, fn)
rateLimitMap(array, requestsPerSec, maxInFlight, fn)
runN(fn, limit, cnt, options)
pMap(array, fn, limit)
And, if you want a pre-made library, the async library contains a bunch of control flow helpers like these.

Any downsides to an API endpoint returning without awaiting promise?

To allow an express endpoint to return more quickly to the caller, is it reasonable to invoke an async task, but not await it before returning if the caller does not need a confirmation?
This obviously limits the error handling and retry options if the async task fails, but assuming you don’t care about that, it does allow for express API calls to complete more quickly (assuming awaiting the async task is not semantically meaningful for the API call)
How would this approach compare to other web background job approaches that are invoked via an API request?
It is perfectly appropriate sometimes and not appropriate at other times. It really depends upon the specific API operation, the expectations of the client, the likely use by the client and how the API is documented.
A core database operation, for example, would never do that because obviously the client usually needs to know whether the database operation was successful or not.
But, an advertising-related API that is just collecting tracking data and storing it away for later use probably has a client that is just going to ignore any return from the API call anyway and the whole thing may scale better if the HTTP connection can get shut-down as soon as possible (without waiting for some storage commit to finish). In addition, if some of this kind of data is lost when some unusual circumstance happens, it's no big deal and the client wouldn't be doing anything differently anyway. So, if you've determined that the client doesn't need a final status on the operation and there are scale or performance benefits to ending the request before the async operation is done, then it can be perfectly appropriate.
In another example, imagine you have a write queue where you queue up N log items in memory to write to disk before you actually write them all to disk because that improves scalability and performance of your disk I/O significantly. In that case, the final write to disk doesn't happen until some future request by some other client when the queue hits a certain size that triggers the actual physical write. It would be a significant penalty to the host API server (and perhaps even to the client) to wait until the final write occurs in order to return a successful API response.
Its kind of ok, but as you mentioned it has some drawbacks with regards to retrying and monitoring and synchronizing calls.
What you probably should do is push a message into a queue and have a background worker do the processing. Save a record to a database which represents the state of the operation, then queue a message to the queue. You can monitor the progress of that work by looking at the state of the record you just saved, have the worker update it.

Synchronous http interface with javascript

I have a synchronous API to invoke a server (HTTP GET) currently implemented with XMLHttpRequest.
The API does caching and will, if cache isn't deemed too old, return from the cache and invoke the server asynchronous to refresh cache.
Sometimes cache isn't available or too old, and then the API will synchronous call the server to fetch an accurate value before returning result to caller.
Result will contain a boolean success flag along with payload and clients handles result accordingly by looking at this flag.
There are two problems I can see with doing like this;
When cache isn't available and server isn't reachable or answering slow I would like to bring up a spinner so that the user is aware we are waiting for server.
In addition I would like to set a timeout value where we abort server request and handle the error accordingly.
Seems like I should be able to use setTimout operations but I have not been successful.
Preferably I would like to keep clients intact (not change the API to asynchronous).
Is there a way to achieve this?
The synchronous API was made responsive by maintaining a cache that was pulled from server asynchronous.
The cache was protected by a grace period under which we do not pull new value from server to avoid hammering the server.
For the most cases this was enough to assert there was always a cached value that could be provided to the client.
For a few cases where we have to pull new data the best solution would be to go fully asynchronous, that is also update client code.
Currently that is not an option, so in addition to above a heartbeat mechanism was put in place that toggles online/offline status to prevent trying synchronous pulls when offline.

Is it better to compose multiple AJAX calls parallel or serial?

I'm developing a single-page application, which sends multiple AJAX request to the server.
The system works with polling, because some data-request can take about 10-20minutes to calculate.
client asks server for data
server hands out a job-id
client asks server every few seconds for the result
The polling algorithm lowers the polling frequency over time, stopping at intervals of 10seconds.
But when a client sends different data requests in a short time, he ends up with about 10-20 job-ids and starts polling for all of them.
Is it better to simply do it this way and let the browser handle those requests in parallel or should I schedule every request and serialize them all?
Would it bring performance benefits to serialize them?
If each initial request returns a unique id and each page has a unique user id then you can poll on what information for each request.
In the JSON I would return the results for any completed request, and the current status of those that haven't completed, such as whether it has started being processed, and perhaps a percentage of completion, or how many requests are ahead of that request.
This will simplify the work as you won't be making several polling calls, but just one, getting back a complex result to give feedback to the user the status of each request.
I find it useful to give some information on status for long-running queries otherwise the user may think the request was lost.
Some months ago, I faced performance issues due to multiple ajax calls, but I haven't investigated deeper this topic since then : High latencies loading stores in an ExtJS 4.1 MVC application.

javascript UI components making too many http request. Any idea how to optimize this?

This question is purely based on assumptions. May or may not be valid problem. Anyway, here it goes
Let's say we have a heavy javascript client app with some numbers
of UI components / widgets, Each of these widgets has
an endpoint to query data from
On a page load, these components will make http request.
Multiple of them; to multiple different endpoints.
Obviously we see that the number of http requests will increase
with heavy client side architecture as compared to traditional web where
UI is generated from the server side.
Sample case:
widget A requests resource A
widget B requests resource B
Of course, we can minimize the http request by having:
parent widget requests an endpoint that return { resource A, resource B }
parent widget distributes data to widget A
parent widget distributes data to widget B
This can be done by, sort of, grouping related widgets based on business logic
Not all can be framed this way. Even if it can, how would maintain code modularity?
Is there any well known design pattern for large javascript apps wrt. performance?
Maybe I am overthinking as I certainly dont have the numbers here.
Any thought guys?
for starters I would consider creating a client JavaScript library that would handle fetching/sending data and make all the widgets use this API.
this way you can optimize/group the flow of data to/from all of your widgets in one place.
One idea that comes to mind (which wouldn't reduce the amount of data transferred, but would reduce the number of HTTP requests) is to route all your AJAX requests on the client side through some common Javascript interface that you control.
Then, instead of sending out one HTTP request per UI request, you can wait a few milliseconds and batch all the requests that occur within that interval, sending out just one HTTP request for the whole batch (you'd have to be careful to only do this for requests going to your server).
On the server, you could have a special generic "batched" endpoint that internally services all the batched requests (preferably in parallel) and returns the results in a batched response.
Then the client side distributes the batched results to the original requesters.
Note that this only works if the requests all take approximately the same length of time to service; you wouldn't want the batched response waiting 30s for one sub-request to finish when all the others are already done. You might be able to address this with a blacklist or something.
Also, try to identify which requests need to be serviced first and assign them priority!
It sounds like what you want is a central queue for all the data requests. Each widget on it's own can make requests by queuing up a specific request. Then, you would have a common piece of code that examines all the requests in the queue and figures out if they can be optimized a bit (requesting multiple pieces of data in one request from the same endpoint).
Using this type of design pattern still keeps all the widgets modular, yet has one small library of code that handles the optimization of the requests.
Technically, how this would work is that you'd create a library function for adding a request to the queue. That function would require an endpoint, a callback function when the data is ready and a description of the request. Each widget would call this common function for making it's data request.
This common function would put each request into a queue and then do a setTimeout() for 0ms (if one wasn't already set). That setTimeout() will be called when the current thread of execution is done which will be when all requests for this current thread of initialization are now in the queue. The queue can then be examined and any requests to the same endpoint that can be combined into one request and the request can be sent. When the data arrives, the separate pieces of data are then parceled out and the appropriate widget's callback is called.
If caching of data would be helpful (if multiple requests over time for the exact same data are happening), this layer could also implement a cache.

Categories