Database Edge Caching With Deno Deploy

Sam Aybar
Nov 08, 2022
Share:

Introduction

If you haven’t checked Deno Deploy yet, you should. (And if you don’t know what Deno Deploy is, it is super fast and super easy. In minutes, you can have a globally deployed application, serving your users with lightning fast responses.)

At PolyScale, we like fast and easy. Our focus is on helping you scale the database you already have to the edge, without writing code, configuration or deploying servers. Because Deno Deploy, by definition, means your application is globally distributed, PolyScale is a natural fit for any data driven application because it helps you reduce query execution times and lower global latency.

deno circle 160

PolyScale is an intelligent, serverless database edge cache. Using PolyScale, you can distribute your database compute and storage globally, dramatically lowering latency for data-driven applications. Delight users of data-driven apps, everywhere.

In this blog post, we will provide an overview of Deno Deploy and illustrate how PolyScale can be used in conjunction to further improve response times. In a future blog post, we’ll dive into more depth about databases and Deno Deploy, to understand some of the nuances of pooling, cold starts, and so forth. If you already know about Deno Deploy and want to jump ahead to the section on usage with PolyScale, feel free to click here. Otherwise, read on!

What is Deno?

Deno describes itself as “a simple, modern and secure runtime for JavaScript, TypeScript, and WebAssembly that uses V8 and is built in Rust.” Ryan Dahl – who also created Node.js – is the co-founder and creator of Deno. Deno was created, in part, to help alleviate the challenges of transpiling TypeScript code down to JavaScript.

Deno also helps address dependency management and security issues that impact the use of JavaScript via Node.js.

Like Node.js, Deno is built on Google’s V8 JavaScript engine, but supports only ES Modules, versus both ES Modules and CommonJS for Node. Dependencies are loaded via a URL, rather than by module, and there is no package manager required.

Deno was first released in 2018, with the 1.0 version released in May of 2020.

What is Deno Deploy?

Deno Deploy is “a distributed system that runs JavScript, TypeScript, and WebAssembly at the edge, worldwide.” Like Cloudflare Workers, Deno Deploy enables speedy deployment of high performance applications globally. Deno Deploy (at the time of writing) has 34 regions located around the globe to ensure that regardless of where a user is located, the content will be served from close by. Combined with the light weight of Deno, this results in blazingly fast response times.

The Problem with Data and Deno Deploy?

A traditional database such as Postgres for example, will typically be located in a single geographic region. Creating a new TCP connection to the origin database can be slow as it may be far away. When combined with the fact that edge functions are ephemeral – so a database connection might not already be open – the latency benefits of serving your application from the edge will be offset by the time required to retrieve the data.

Additional read-replicas can help, but come with financial and operational costs (dealing with replication lag for example) as well as development overheads to manage reads vs writes. There are also limits on the supported number based on your database provider. Distributed databases also help reduce regional latency, but deploying dozens of nodes is either unsupported or cost prohibitive.

For many use cases, you can mitigate this problem by creating a cache layer at the application tier. However, in addition to the coding required to create the cache (in-memory, Redis etc), there is also the complexity of creating cache invalidation rules to ensure you are serving up-to-date data. Additionally, infrastructure often needs to be sized, deployed, managed, observed etc to support the cache.

PolyScale addresses these issues by providing a serverless caching layer that is geographically very close to the app tier (in this case the Deno Deploy regions) and that automatically handles cache lifetimes and invalidation. Best of all, it is wire-protocol compatible with multiple databases (such as Postgres, MySQL and MS SQL Server), meaning that you don’t need to modify your code at all. Writes simply pass through to the origin, so your transactionality does not change.

Working with Deno Deploy and PolyScale

To illustrate working with Deno Deploy and PolyScale, we are going to:

  1. Build a simple ToDo API, deployed to Deno Deploy and powered by a Postgres database
  2. Implement PolyScale as a database caching layer with a simple config change (replace the direct connection to the database with a connection via PolyScale)
  3. Compare the performance of the API with and without PolyScale.

Part 1 - Accessing Postgres from Deno Deploy

Deno has an excellent tutorial on working with Postgres and Deno Deploy. In that tutorial, you write a simple API to create and read todo items. You can follow along the steps, or just go directly to https://dash.deno.com/playground/tutorial-postgres and click the “Fork to Edit” button to deploy the code right to your own Deno Deploy playground if you prefer.

deno fork
Deno Tutorial Code Fork

If you’ve not walked through the steps in the tutorial, once you fork the project, you will need to click on the settings button to add a database environment variable. (And if you don’t have a Postgres database to use, see https://deno.com/deploy/docs/tutorial-postgres#setup-postgres for easy instructions on creating a free one.)

Add an environment variable called DATABASE_URL with a value of something like: postgres://USERNAME:PASSWORD@HOST:5432/DBNAME

deno settings
Setting Environment Variables

Click “Save” and you’ve now got a basic API for a ToDo app. Grab the provided URL, and you can create new ToDo items with eg:

curl -X POST -d '"Buy ice"' https://your-subdomain.deno.dev/todos

And you can retrieve ToDos with

curl -X GET https://your-subdomain.deno.dev/todos

This API endpoint will be served by the Deno Deploy region closest to wherever the request comes from. However, to get the data, Deno Deploy needs to make the request all the way back to your origin database. If the requester is close to the origin database, there shouldn’t be much latency. But what if someone around the world is trying to use the API?

Connecting PolyScale

This is where PolyScale comes in. In just a few minutes we can modify this Deno Deploy function to leverage PolyScale.

Step 0: Create a PolyScale account​

If you do not already have a PolyScale account, you can create an account here. PolyScale offers a free tier and no credit card is required.

Step 1: Create your PolyScale Cache

  • In your PolyScale account, click on the New Cache button in the top-right corner
  • Give the cache a Name
  • Select PostgreSQL for the Type
  • Enter the Host from your database URI
  • Enter 5432 for the Port
  • Click Create

cache configure 400
Creating a PolyScale Cache

Your cache is now created. PolyScale automatically checks to see that your database is accessible from all our global regions and alerts you if there are any connectivity issues.

Step 2: Connect to your PolyScale Cache​

Using your PolyScale cache is simple — instead of connecting to your database directly, you’ll update your original connection string with the PolyScale credentials.

For example, if your original connection string was: postgres://postgres:zqGHFAbPLvVCKw@db.rogpiubvixysbakciwqz.supabase.co:5432/main

Your PolyScale connection string would be: postgres://postgres:zqGHFAbPLvVCKw@psedge.global:5432/main?application_name=a645cb93-fa53-46b2-9d6c-227e357e5bfb

Note that the hostname has been updated (psedge.global) to route traffic via the PolyScale edge network and also an application_name (in this case, a645cb93-fa53-46b2-9d6c-227e357e5bfb) has been appended to authenticate with PolyScale.

Keep this string for the next step.

Step 3: Update your Deno Deploy DATABASE_URL

In your Deno Deploy function, click on the “settings” icon again and replace your DATABASE_URL with a new one, this time using the PolyScale connection string instead.

Now all requests will be routed through PolyScale, and PolyScale will automatically cache responses (and clear the cache when a new ToDo item is added).

That’s it. Your data is now cached at the edge for your Deno Deploy function!

Performance Results

So the natural question is, how much benefit does the addition of PolyScale create? To illustrate this, a database was deployed to US East (Virginia) using Supabase. (Note that Supabase is hosted on AWS – whereas Deno is on GCP.)

deno polyscale diagram 600
Deno Deploy and PolyScale Example Architecture

One hundred requests were then made to the ToDo endpoint using the direct database connection and another 100 via PolyScale to compare response times. This was repeated from each of four locations:

  • Virginia
  • South Carolina
  • California
  • Singapore

In order to be able to look at just the database component of time, within the Deno Deploy function, a timer was added that measured from before the database request until after the database request, and then included that as a property in the response payload.

// timer start
 const startTime = new Date();
 // Grab a connection from the database pool
 const connection = await pool.connect();

. [Make to-do query)

       // timer end & calculate time
       const endTime = new Date();
       const databaseTime = endTime - startTime;

       // build enhanced body
       const rawBody = {
           "todos": result.rows,
           "databaseTime": databaseTime
       }
       // Encode the result as JSON
       const body = JSON.stringify(rawBody, null, 2);

       // Return the result as JSON
       return new Response(body, {
         headers: { "content-type": "application/json" },
       });

In the chart below, we can see the clear benefits of PolyScale. Here we show how much time it takes – within the Deno Deploy function – to connect to, query, and retrieve data from the database.

performance chart
Performance Test Results

For edge regions where PolyScale is co-located, the database portion of the function completes in ~1ms.

When accessing the function from Virginia – where the host database is deployed – you can see that the database time is the same (3ms) whether you go directly to the database or serve cached responses from PolyScale. In this example, the SQL is very simple. Combined with the fact the database is under no concurrent load, the overall query execution time is < 3ms (including network latency). If the query was more complex and/or the server was under more load, PolyScale would yield further benefits for the execution time as cached queries execute in < 1ms.

If you look at locations further away from the origin database in Virginia, you can see the dramatic improvement: 20 milliseconds in South Carolina, over 65 milliseconds in California, and over 200 milliseconds in Singapore.

Keen observers may notice that our response time from Virginia – where the host database is deployed – is actually slower than from our other regions. This is because in this example, our database is actually hosted on AWS whereas Deno Deploy (at the time of writing) utilizes Google Cloud Platform. Consequently, requests from Deno Deploy to the database from Virginia have to transverse from GCP to AWS, leading to the additional latency.

But, regardless of location, PolyScale allows you to deliver data from your existing database with low latency around the globe to Deno Deploy.

Next Steps

If you didn’t already set up a PolyScale account to try this walkthrough, sign up for your own free PolyScale account here (no credit card required). Connect your database to a cache and start delivering your data around the globe, faster.

Share:
Written by

Sam Aybar