App Engine and Socket.IO

03 Oct 2022 | AgileData DataOps, AgileData Product, Blog, Google Cloud

TD:LR

We wanted to be able to dynamically notify Data Magicians when a task had completed,  without them having to refresh their browser screen constantly.  Implementing websockets allowed us to achieve this.

Nigel Vining - AgileData.io

Bringing near realtime notifications to AgileData.io

Sockets.io

In previous articles I’ve talked about how we embrace serverless for the architecture underneath our SaaS platform Agiledata.

That includes the frontend web application that our customers use to interact with the platform. Under the covers we’re using Google App Engine.

We run two app engine instances for each customer, one running python which is our /api layer, and one running svelte (Node.js) which is our /app layer. These two are used in a typical pattern, the web application makes GET/POST requests to the /api layer which handles all the interaction with our config database and the other services we use.

 

 

AgileData.io sample /api layer

The reasoning for this separation was to allow advanced users access to the api layer where all the features of the frontend can be invoked directly, to support custom application development and automation.

 

AgileData.io sample /app layer

These server instances automatically scale to zero when not in use, then automatically start up again within a few seconds when a user interacts with the application. No server running = no charge, perfect !

Back to the topic of this article … websockets

We run one other App Engine instance, a single flexible instance which exists solely to act as a websocket server.

Why flex ?

Flex is the only option currently that supports websocket connections and session affinity which is required to allow bidirectional messages between connected clients and the server.

Ok, the details.

The flex instance is a python server and runs socket.io server. Apart from Cloud Spanner for our config and metadata, this is the only other service we pay for and i’ll explain why.

Because our frontend app is basically running on an ephemeral platform that shuts down when not in use we have no concept of state unless we make a request to a database to refresh the data in the app.

To get around this, we push notifications and refreshed content straight to connected clients using a websocket which is basically an ‘always on’ connection. A good example is notifications to users when back end processes are running.

 

Agiledata.io sample /ws notifications via Websocket

The example above shows notifications being pushed into the frontend application via the websocket connection. The socket pushes message payloads into a svelte store which the notifications widget is subscribed to.

This pattern also allows us to ‘catchup’ a user when they haven’t used the app for awhile. When they open the app, it automatically reconnects to the websocket server and the first event after ‘connected’ is ‘refresh-store’ , this payload is used to re-populate the main svelte store with updated content since their last login. This happens very quickly and mitigates some of the delay if the web server is performing a cold start.

We don’t use websockets for everything, most of the data interactions under the covers are still GET/POST to the /api layer. We use it for config and event data that is constantly updating, where the overhead of a ‘refresh’ to show changes isn’t a very magical experience for the user.

Get more magical DataOps plumbing tips

Keep making data simply magical