Back to blog
5 min read

Cloudflare Workers - Learnings, Surprises & Wishes

For my project Axo Analytics I use Cloudflare Workers. The decision to use them was partly based on the desire to have a serverless architecture, at least for the ingestion and processing of analytics data. I also wanted to avoid the complexity of managing a fleet of servers around the globe, and the cost associated with that. Another solution would have been to use AWS Lambda, especially since Laravel has Vapor which utilizes Lambda under the hood.

But for me Cloudflare Workers seemed like a good fit, as they offer a very simple setup, global distribution, amazing speed, low latency, and an incredible pricing model.

Here are some learnings, surprises and wishes I have after using Cloudflare Workers for a while:

Speed at the Edge

One of the biggest advantages of using Cloudflare Workers is the speed at which they can respond to requests. In my experience, worker responses are often under 50ms. To keep the response time low, I make use of the ctx.waitUntil feature (read the docs for a deep dive) to process tasks in the background after an empty 204 response is sent back to the client. That way, the client gets a fast response, while the worker can take it’s time to process the data.

Also, each worker has a global scope, so variables defined outside the event listener are shared across all requests to that worker instance. I wrote a small caching layer for simple set/get operations with a basic TTL. This way I can cache data from my D1 (SQLite) database and avoid unnecessary database calls.

For example: An Axo customer has a Swiss website, and their visitors mainly hit the datacenter in Zurich and one single worker instance. Even caching the settings of a site for a short period of time while dozens of requests per second are coming in, saves a lot of database calls.

Pricing Model

The pricing model is based on the number of requests and the CPU time used. And for those who don’t want to jump into the $5 paid worker plan, Cloudflare offers a very generous free tier.

Since billing of every request is based on CPU time, I don’t have to worry about making another expensive database call on the other side of the globe. Such waiting times don’t count towards CPU time. This is different from AWS Lambda, where you are billed for the total request time, including waiting for external services.

Javascript/TypeScript Environment

Although I’m a big fan of Laravel and PHP, I am not religious about that. So writing the workers in Javascript/TypeScript is not a big deal for me. The advantage is that I can focus on the simple business logic of processing the analytics data. No need to worry about separate processing apps, docker images, and underlying infrastructure in general.

Issues and surprises

Cloudflare datacenters can run between 1 and multiple instances of a worker, so don’t rely on in-memory state to be consistent across requests. Cloudflare has improved on that a lot over time, as you can read in their blog post. But it’s still something to keep in mind. There are some datacenters which are quite busy and run multiple instances of a worker. And then there are others which are very consistently running only one worker instance.

Another thing to be aware of is that requests from the same user may hit different datacenters, so don’t rely on Cloudflare’s Caching API or in-memory state of the worker instance to be consistent across requests from the same user.

And last but not least: Just like with every other network service, expect the unexpected. In my case I had some issues with D1 (Cloudflare’s SQLite database).

  • As soon as I activated Replication (which is currently in beta, but still…) I had some issues with database errors. Disabling replication did not help, surprisingly. Cloudflare Community forums did not help either. So on a hunch I tried to create a new D1 database and migrate the data from the old one to the new one. That solved the issue.
  • Cloudflare updated D1 API in Workers to automatically retry read-only queries up to two more times when it encounters a retryable error. But that did not solve all retry issues for me. So I implemented my own retry logic for certain operations, based on some help in their docs.

Some modest wishes

Right now worker calls are ordered before the global CDN cache. This is useful in some cases, but I wish I could configure the order. There are some cases where I would like to have the worker script run only if the CDN cache is missed. But it seems that Cloudflare is working on that, as recently commented by Kenton Varda, their tech lead for Workers.

And another wish would be to have a bit more control over which datacenter serves which requests. If there could be a way to have some sort of sticky sessions (session affinity), so requests from the same user may at least hit the same datacenter, that would be great.

Overall, I’m very happy with my decision to use Cloudflare Workers for Axo Analytics. Thx Cloudflare for providing such a great service!