## Global Serverless Architectures
### with Cloudflare Workers
Mitch McGinnis
The AWS Way
## A Simpler Path
#### Cloudflare Workers
- Runtime & Platform
- Cloudflare Provided Tools
- Demos

Cloudflare
- Global Cloud Network
- Networking & Security Tools
- Serverless Compute
### Cloudflare Workers Runtime
- V8 Engine
- JS/TS & WASM (C, C++, Rust, etc)
- 0ms Startup
- Built-in
- Fetch
- Web Crypto
- Streams

### Workers
- Bundled
- CPU Limit: Under 50ms (10ms free)
- Unbundled
- CPU Limit: 30s HTTP, 15m Cron
- Not killed after limit but higher chance of eviction
- Duration: No Limit, only killed for worker runtime updates
- Max Size of Worker 5MB (1MB)
- 128 MB Memory
### Workers
```typescript
export interface Env {
// MY_KV_NAMESPACE: KVNamespace;
// MY_DURABLE_OBJECT: DurableObjectNamespace;
// MY_BUCKET: R2Bucket;
// MY_SERVICE: Fetcher;
// MY_QUEUE: Queue;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise {
return new Response('Hello World!');
},
};
```
### Pages

- Front-end deployments with Github/GitLab
- preview deploys
- functions, uses worker runtime

### Tool - Wrangler & Miniflare
- CLI Tool ⛅️
- Includes init, deploy, dev, rollback, etc.
- create cloudflare resources
- Miniflare 
- run locally
- includes most features
- Local development can use Miniflare or Proxy
### wrangler.toml
```toml
name = "fancy-hall-c7cc"
main = "src/worker.ts"
compatibility_date = "2023-06-20"
[vars]
API_TOKEN = "dev_token"
# [env.production]
# name = "fancier-hall"
[env.production.vars]
API_TOKEN = "prod_token"
```
### [River Me This](https://rivermethis.com/)
Daily Game to choose the winning hand in texas hold-em, uses websockets with a server based timer

### Storage
- Cache - local cache
- 512 MB Objects
- KV - Key Value Store
- 512 byte Key
- 25MiB Value
- 1KB Metadata
- R2 - Object Storage
- S3 Compatible API
- No Egress Fees
- up to 5TB (5GB per upload)

```javascript
export default {
async fetch(request, env) {
const contents = await env.MY_KV_NAMESPACE
.get("groups::foo");
const groupLogs = await env.LOGS
.list(contents.prefix);
return new Response(JSON.stringify({
contents, groupLogs
}))
},
};
```
### Storage
```toml
[[kv_namespaces]]
binding = "MY_KV_NAMESPACE"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[[buckets]]
binding = "MY_BUCKET"
name = "my-bucket"
bucket_id = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
```
### Storage
- Durable Objects - Atomic Storage
- Single Instance (per ID)
- Unlimited Objects
- 50 GB per Account (contact Cloudflare to raise)
- 2048 B Key
- 128 KiB Value
- Unlimited Storage per Class & Object
```javascript
export default {
async fetch(request, env) {
let id = env.BATCHER.idFromName("foo");
return await env.BATCHER.get(id).fetch(request);
},
};
```
```toml
[[durable_objects]]
binding = "BATCHER"
class_name = "Batcher"
[[migrations]]
tag = "v1" # Should be unique for each entry
new_classes = ["DurableObjectExample"]
[[migrations]]
tag = "v2"
renamed_classes = [{
from = "DurableObjectExample",
to = "UpdatedName"
}]
deleted_classes = ["DeprecatedClass"]
```
###  Storage
- D1 - Global sqlite
- 1MB row limit
- 100 MB per DB during Alpha
- 10 per Account
- Queries per worker 50 (Bundled) / 1000 (Unbound)
- Coming Soon
- Read Replicas
- Metrics and Observability
```javascript
const { results } = await env.DB.prepare(
"SELECT * FROM Customers WHERE CompanyName = ?"
)
.bind("Open AI")
.all();
```
```toml
[[ d1_databases ]]
binding = "DB" # available in your Worker on `env.DB`
database_name = "d1-example"
database_id = "4e1c28a9-90e4-41da-8b4b-6cf36e5abb29"
```
### Services
- Service Bindings
- Zero-overhead to call other services
- dependent services are loaded up on invocation

```javascript
export default {
async fetch(req, env) {
return await env.OPENAI.fetch(req);
},
};
```
```toml
[[services]]
binding = "OPENAI"
service = "openai"
environment = "production"
[[routes]]
binding = "MY_SERVICE"
pattern = "/api/*"
script = "api.js"
```
### Services
- Pub/sub - Distributed MQTT Service
- 1k subscribers per topic
- 64 KB Message Size
- Queues - Queue Messages for Async Processing
- Triggers Worker by Batch Size or Timeout
- One Consumer per Queue
```javascript
const mqtt = require('mqtt')
const options = {
port: 8883, protocolVersion: 5, // MQTT 5
password: process.env.BROKER_TOKEN,
}
const client = mqtt.connect(process.env.BROKER_ENDPOINT, options)
client.subscribe("example-topic")
client.publish("example-topic",
`message from ${client.options.clientId}: hello at ${Date.now()}`)
client.on("message", function (topic, message) {
console.log(`received message on ${topic}: ${message}`)
})
/// Queues
await env.MY_QUEUE.send(log);
```
```toml
[[queues]]
binding = "MY_QUEUE"
name = "my-queue"
zone_id = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
```
### Services
- Constellation - Run Machine Learning Models
- Image or audio classification, Object detection, Anomaly detection
- Natural language processing (NLP)
- Sentiment analysis, Speech recognition (or text to speech)
- Open Neural Network Exchange (ONNX) Runtime
- XGBoost in testing
```typescript
import { Tensor, run } from "@cloudflare/constellation";
async function processImage(env: Env, data: ArrayBuffer) {
///
if (input) {
const output = await run(
env.CLASSIFIER,
// Replace this with your actual model ID generated when you created your Constellation project
"297f3cda-5e55-33c0-8ffe-224876a76a39",
new Tensor("float32", [1, 3, 224, 224], input);
);
const softmaxResult = softmax(output.squeezenet0_flatten0_reshape0.value);
const results = topClasses(softmaxResult, 5);
result = results[0];
}
return result;
}
```
```toml
[[constellation]]
binding = 'CLASSIFIER'
project_id = '2193053a-af0a-40a6-b757-00fa73908ef6'
```
### Types of Workers
- fetch & Web Sockets
- Queue
- Scheduled - run on cron schedule
- Email - process emails with a worker
- reject, forward, or drop emails
- Tail Workers - run after a worker invocation
- Has access to logs and exceptions
```javascript
export default {
async email(message, env, ctx) {
message.forward("test@example.com");
},
fetch(request, env, context) {
return new Response('Hello');
},
async queue(batch, env) {
let messages = JSON.stringify(batch.messages);
console.log(`consumed from our queue: ${messages}`);
},
async scheduled(event, env, ctx) {
ctx.waitUntil(doSomeTaskOnASchedule());
},
async tail(events, env, ctx) {
fetch("https://log-storage", {
method: "POST",
body: JSON.stringify(events),
})
},
}
```
### Tools
#### Terraform
- Alternative to wrangler.toml
- missing latest worker features
```terraform
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
```
```terraform
resource "cloudflare_workers_kv_namespace" "kv" {
account_id = "f037e56e89293a057740de681ac9abbe"
title = "example"
}
resource "cloudflare_worker_script" "my_script" {
account_id = "f037e56e89293a057740de681ac9abbe"
name = "script_1"
content = file("build/index.js")
kv_namespace_binding {
name = "MY_EXAMPLE_KV_NAMESPACE"
namespace_id = cloudflare_workers_kv_namespace.kv.id
}
}
```
### Open Source Workers Platform (Beta)
- workerd - https://github.com/cloudflare/workerd
- Missing Support for:
- Persistent Durable Objects
- Cache API
- Cron Triggers

### Gotchas
- Pages and workers can't be renamed
- Charged per invocation (Service Bindings)
- CPU & Memory Limits
- Lots of alpha/beta services
# Comparison
||AWS|Cloudflare|
|-|-|-|
|Key Value|DynamoDB|KV, DurableObjects|
|Compute|EC2,ECS,EKS|x|
|Machine Learning|Bedrock,SageMaker|Constellation*|
|Object Storage|S3|R2|
|Pub/Sub|EventBridge, SNS, SQS|Pub/Sub*, Queues|
|Relational Storage|RDS|D1|
|Secrets|Paramater Store, Secrets Manager|Secrets Store*|
|Serverless|Amplify, AppSync, Lambda|Workers, Pages|
# Demo & Questions
### [Adventure GPT](https://adventure-gpt.pages.dev/)
Text Based Adventure Game powered by Open AI
### References
- https://developers.cloudflare.com/workers/
- https://developers.cloudflare.com/workers/platform/limits/
- https://developers.cloudflare.com/d1/
- https://developers.cloudflare.com/r2/
- https://blog.cloudflare.com/introducing-cloudflare-workers/
- https://blog.cloudflare.com/introducing-r2-object-storage/
- https://blog.cloudflare.com/announcing-pubsub-programmable-mqtt-messaging/
- https://blog.cloudflare.com/introducing-d1/
- https://blog.cloudflare.com/d1-turning-it-up-to-11/
- https://blog.cloudflare.com/introducing-cursor-the-ai-assistant-for-docs/
- https://developer.mozilla.org/en-US/docs/Web/API
- https://developers.cloudflare.com/workers/platform/bindings/about-service-bindings/
- https://github.com/cloudflare/miniflare/tree/master