feat: d1 global read replicas (#149)

This commit is contained in:
Zack Pollard
2025-07-01 12:38:38 +01:00
committed by GitHub
parent dbce3c3b50
commit 3f8d5b266c
5 changed files with 5751 additions and 89 deletions
@@ -31,14 +31,6 @@ resource "cloudflare_workers_script" "tiles" {
}
}
dynamic "d1_database_binding" {
for_each = data.terraform_remote_state.tiles_state.outputs.d1_regional_databases
content {
name = "D1_${d1_database_binding.key}"
database_id = d1_database_binding.value
}
}
d1_database_binding {
database_id = data.terraform_remote_state.tiles_state.outputs.d1_global_database
name = "D1_GLOBAL"
@@ -62,15 +54,3 @@ resource "cloudflare_workers_domain" "tiles_prod" {
service = cloudflare_workers_script.tiles.id
zone_id = data.cloudflare_zone.immich_cloud.zone_id
}
import {
for_each = var.env == "prod" ? { import : cloudflare_workers_script.tiles } : {}
to = cloudflare_workers_script.tiles
id = "${var.cloudflare_account_id}/tiles-prod"
}
import {
for_each = var.env == "prod" ? { import : cloudflare_workers_domain.tiles } : {}
to = cloudflare_workers_domain.tiles
id = "${var.cloudflare_account_id}/17b3213004b9d5f077563e3c4ea3171ef9d0c5fc"
}
+2 -12
View File
@@ -89,6 +89,8 @@ async function handleRequest(
deferredRepository: CloudflareDeferredRepository,
metrics: IMetricsRepository,
) {
const d1Repository = new CloudflareD1Repository(env.D1_GLOBAL, metrics);
void d1Repository.query('SELECT 1');
const cacheResponse = async (response: Response): Promise<Response> => {
if (!response.body) {
throw new Error('Response body is undefined');
@@ -177,24 +179,12 @@ async function handleRequest(
weur: env.BUCKET_WEUR,
oc: env.BUCKET_OC,
};
const d1Map: Record<R2BucketRegion, D1Database> = {
apac: env.D1_APAC,
eeur: env.D1_EEUR,
enam: env.D1_ENAM,
wnam: env.D1_WNAM,
weur: env.D1_WEUR,
oc: env.D1_OC,
};
const colo = request.cf?.colo || '';
const buckets: R2BucketRegion[] = preferredBuckets[colo] || ['weur', 'eeur', 'enam', 'wnam', 'apac', 'oc'];
console.log('Buckets', buckets);
const filteredBucketMap = Object.fromEntries(
Object.entries(bucketMap).filter(([key]) => buckets.includes(key as R2BucketRegion)),
);
const filteredD1Map = Object.fromEntries(
Object.entries(d1Map).filter(([key]) => buckets.includes(key as R2BucketRegion)),
);
const d1Repository = new CloudflareD1Repository(filteredD1Map, metrics);
const storageRepository = new R2StorageRepository(filteredBucketMap, env.DEPLOYMENT_KEY, metrics);
const pmTilesService = await metrics.monitorAsyncFunction({ name: 'pmtiles_init' }, PMTilesService.init)(
+9 -12
View File
@@ -15,26 +15,23 @@ import { monitorAsyncFunction } from './monitor';
export class CloudflareD1Repository implements IDatabaseRepository {
constructor(
private databases: { [key: string]: D1Database },
private database: D1Database,
private metrics: IMetricsRepository,
) {}
async query(query: string, ...values: unknown[]) {
const metric = Metric.create('d1_query');
const { key: databaseKey, resp } = await Promise.race(
Object.entries(this.databases).map(async ([key, database]) => {
const resp = await database
.prepare(query)
.bind(...values)
.run();
return { key, resp };
}),
);
const resp = await this.database
.withSession('first-unconstrained')
.prepare(query)
.bind(...values)
.run();
console.log('resp_meta', resp.meta);
metric
.durationField('duration')
.addTag('database_key', databaseKey)
.addTag('served_by_region', resp.meta.served_by as string)
.addTag('query', query)
.addTag('served_by_region', resp.meta.served_by_region as string)
.addTag('served_by_primary', (resp.meta.served_by_primary as boolean).toString())
.intField('rows_read', resp.meta.rows_read)
.intField('sql_duration', resp.meta.duration)
+5737 -17
View File
File diff suppressed because it is too large Load Diff
+3 -28
View File
@@ -12,34 +12,9 @@ id = "5a4b82694e8b490db8b8904cdaea4f00"
preview_id = "5a4b82694e8b490db8b8904cdaea4f00"
[[d1_databases]]
binding = "D1_WNAM"
database_name = "tiles-wnam"
database_id = "837aa5e4-f404-4613-b30c-34f3b8bca150"
[[d1_databases]]
binding = "D1_ENAM"
database_name = "tiles-enam"
database_id = "f63d8430-3e5b-42bd-a6a6-45fd3f552a9a"
[[d1_databases]]
binding = "D1_WEUR"
database_name = "tiles-weur"
database_id = "ce5b6777-c533-4ff1-91a4-d6b5e94293da"
[[d1_databases]]
binding = "D1_EEUR"
database_name = "tiles-eeur"
database_id = "538b5ef8-9a73-4bd8-a088-3a43c992cfd0"
[[d1_databases]]
binding = "D1_APAC"
database_name = "tiles-apac"
database_id = "d8501ddc-adea-48b4-a577-074640b56493"
[[d1_databases]]
binding = "D1_OC"
database_name = "tiles-oc"
database_id = "403f37a6-ebe8-4d05-9ffb-088aa73fa014"
binding = "D1_GLOBAL"
database_name = "tiles"
database_id = "c60521e7-59bd-4186-81e7-9a676edf9b75"
# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#r2-buckets