Mitigate heavy django graphene request with ajax polling - graphql

I have some queries I am making that cause timeouts. Because the computing of the data takes too long on the server in some very specific edge cases. What is the best solution for my specific stack? I was thinking of polling the server every 20 seconds or so to see if my data is ready. But I'm not sure how to accomplish this. Or how to keep track of which client made which request that I'm currently processing.
My stack:
Graphene
Django
Apollo

Since you are using Django you can easly use Django Channel to enable websockets in Django.
With websocket you can send an asynchronous notification from the server to the client made the request when the data is ready so you don't need to poll every second.
In that link there are all the info to create a bidirectional communication. The tutorial is for a chat system, but you can easly adapt to your needs

You have options.
One is Apollo retry link which which you can use as follows
const myLink = new RetryLink({
delay: {
initial: 300,
max: Infinity,
jitter: true
},
attempts: {
max: 5,
retryIf: (error, _operation) => !!error
}
});
I think this is the least hustle way to do it.
Retry Link Doc
Second option is to use subscriptions instead of queries. Subscriptions are an open link between the client and server usually use for chat apps or any anything needed in real-time, so you can use that and the client will get a response when the server is done with calculation.
You'll have to do a bit work on the client side and server side to get it to work.

Related

ColdFusion API and Websockets

I am hoping someone can point me in the right direction. I have a CF2021 Server which uses a Node.js websocket server and CF pages (via javascript) as a client. Messages from user to user work as expected, so no issue there.
This CF Server also has a custom API built using CFML that handles and routes inbound SMS messages. My question is; what would be the best way to send the SMS message (by now its json) to the Node.js websocket to it can send it to the user(s).
I tried using the same javascript that the browser client uses, but it appears that the CFML API script is "browser-less", so that doesn't work, or should it?
I thought something like Apache Groovy may be the solution, but I am having difficulties with any websocket example I have found.
Any suggestions would be greatly appreciated.
thanks in advance
Flow matters.
If you want to handle an incoming message by delivering it to all currently logged in users who are subscribed to messages of the current sort: set up your message handler to deliver using lucee/adobe-coldfusion websockets. Be forewarned, Lucee takes some setup time, but once running, it is a great solution.
If you don't need immediate delivery, or you need a super simple solution: I actually have had some success with "Long Polling" you just have to remember to use "flush" early in the request, before any pause/sleep, then loop your message lookup requests for new data, with a 1-5 second delay between each loop. Once new data is found, I like to return the request to the client, close that polling request and start a new polling request using the client side. I typically won't poll for more than 60 seconds. Even if the returned json object is empty.

Are Server-Sent Events sending headers each time an event is sent from the server or just when the connection is created?

I have to make a choice between WebSockets and SSE for an app that should send events in order to update a newsfeed on my website.
I kind of want to use SSE for this task since i don't have to allow users to send events to the server, i just want them to receive events.
Question is: are SSEs sending headers each time an event is sent from the server or just when the connection is created and the sends just the content of an event? If SSE send headers with each event should i use WebSockets to reduce bandwidth?
With SSE, headers are sent only when the connection is created. After that you simply send event data to each open connection as needed.
For one-way traffic, SSE generally uses less server resources than WebSockets and runs over normal http/https protocol for lower risk of firewall/gateway issues. Even group chat can be implemented with a combination of SSE and AJAX.
Some example Node.js code:
const connections = [];
function connect(req, res) {
connections.push(res);
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
});
req.on('close', () => {
connections.splice(connections.indexOf(res), 1);
});
}
function send(data) {
connections.forEach(c => c.write(`data:${JSON.stringify(data)}\n\n`));
}
Server-Sent Events official specs:
https://html.spec.whatwg.org/multipage/server-sent-events.html
https://www.w3.org/TR/eventsource/
Marmelab has one was the most useful tutorials because it shows how to configure routes for multiple chat rooms:
https://marmelab.com/blog/2020/10/02/build-a-chat-application-using-sveltejs-and-sse.html
Here's a superb demonstration of how response objects can be stored in array with no other identifiers
and deleted when the connection is closed. Wow! How the heck does Node.js compare responses? #hashcodes?
https://cri.dev/posts/2021-05-11-simple-server-sent-events-sse-eventsource-nodejs-javascript
One of many good tutorials from DigitalOcean community:
https://www.digitalocean.com/community/tutorials/nodejs-server-sent-events-build-realtime-app
A very popular reference with a note re: Cross-document messaging security via EventSource.origin:
https://www.html5rocks.com/en/tutorials/eventsource/basics/
Watch out for compression middleware:
https://jasonbutz.info/2018/08/server-sent-events-with-node

Best way to pass messages between 2 different protocol routes connected to same client

I have a http route /checkout which initiates a workflow process in Zeebe. The checkout route will return 200 response straight-away to calling client. Now, workflow will run for a while. So to push the response back to client after completion, I have a /sse separate route for server-sent events. Here, I will store all the client connection in a global map.
My doubt is how do I find the exact client to send the response back through sse once?
Example: Client A listening to /sse and calls /checkout endpoint which will return 200. The /sse has to return the response to client A after completion.
Currently, I thought of using cookie to identify the client. Is there a better way?
If you are already using cookies in your app than that's the way to go since the very purpose of cookies is identifying the client, so if you already have that, you should use it.
But if you rely on another authentication mechanism (like JWT), what you could do is using the url as a query.
So in the client instead of
let eventSource = new EventSource("/sse");
Do
let eventSource = new EventSource("/sse?authorization=jwt_token");
In the backend, you would validate that token, extract the Client ID and hit that global map with it to retrieve the corresponding connection.
(PS: instead of a global map, you should use a proper store, like redis, or an embedded key/value store like bbolt)

Backbone.js: define timeout for Backbone.sync implementation

I'm using backbone on a project of mine, integrated with communication to an external API. I want to use real-time updating of records. Since I don't have access to the main backend of this external application, and they don't provide neither websocket server nor long-polling endpoint, I am basically left with the option of doing regular polling with setInterval and a period of 50 seconds. It has been working quite well. My problem is the edge case. If for some reason the API request hangs, for more than 50 secs, let's say, I'll be triggering a new request right away. That means, 2 hanging requests now, which will add up eventually. Is there a way to set a timeout for the request? I know all requests lead to Backbone.sync, but I was checking the source code and I don't see any feasible way of setting the timeout for the XmlHttpRequest. Is there a way to do this cleanly and without overwriting behaviour? Or are there other solutions/workarounds?
Just pass a timeout:milliseconds option in the options argument to fetch. The options get passed directly to jQuery.ajax, which handles the low-level XHR call:
collection.fetch({timeout:50000});
Alternatively you can set a global timeout for all the requests made by your application by calling jQuery.ajaxSetup in your application startup:
$.ajaxSetup({timeout:50000});

Grails: server-to-client notification

I'm building a Grails app which queries several API's across the Web. The problem is that this queries are very time consuming and it is really annoying for the user to click one button and wait so much time without nothing changes in the page.
The basic architecture of my app is, when the user clicks the button, the client side performs an Ajax request with Prototype library to the server side. The server side, then, connects to the Last.fm API and retrieve a list of events. When the server side is finished populating the list with events it sends a JSON response to the client side which allows the client side to create markers for each event on a Google map.
What I want to achieve is, instead of waiting for all the events being retrieved from the API to send the JSON response, the server side sends a JSON response as soon as it retrieve one event, allowing the client side to populate the map while other events are yet being retrieved.
Any ideas how can I implement this? I read about the concept of Ajax Push but I'm not sure if it is what I need.
Thanks for the help!
There is no way to open a listening connection on the client that your server might connect to. Instead, what you need is a connection to the server that is kept alive and can be used to receive events. This way, you could directly return the "action" request and inform the client through your persistent event connection once the last.fm request was completed.
That said, the way I would implement is using ajax keep alive.
Take a look at cometd plugin.

Resources