Workers Are So Slow

I'm facing an issue with my Cloudflare Worker. I deployed some APIs to my worker, but the api responses are very slow. I found that the problem lies in the latency and fetch method during the request process. I have identified three services provided by Cloudflare that might help with speeding up the worker API response: 1. Smart Placement 2. Load Balancing 3. Argo Smart Routing Although I'm using the D1 database in my workers, I have discovered that the issue is not related to database queries based on the timing logs. Can you please clarify which of the above three services are helpful for accelerating the worker API response? It is currently too slow (average 4 seconds).
4 Replies
omar4289
omar42898mo ago
I had similar issues before. In my case, the reasons were: - The DB was far away from the worker edge location. Solved it by making the DB close to the customer in location hints. - Smart placement would work only if you are making more than one query - Created a replica through multiple databases (one in each region) and used the cf parameter continent to determine which db to call first, and then fallback to the master db if nothing could be found - in case replica did not run yet for some reason
piko
piko8mo ago
Thanks omar. Great advice. Can you please show me some demo codes of advice 3?( how to determine which db to call first by cf parameter continent) And how to create a replica through multiple databases (one in each region) ?
omar4289
omar42898mo ago
Sure, let me create a quick example
import { Env } from '.';

type CloudflareContext = { req: Request; env: Env; ctx: ExecutionContext };
type CloudflareRegion = 'eu' | 'us' | 'asia' | 'global';
const GLOBAL_REGION: CloudflareRegion = 'global';
const SupportedLocalRegions: CloudflareRegion[] = ['eu', 'us', 'asia'];

const REGIONS_BY_CONTINENT: Record<string, CloudflareRegion> = {
// AF – Africa
AF: 'eu',
// AN – Antarctica
AN: 'us',
// AS – Asia
AS: 'asia',
// EU – Europe
EU: 'eu',
// NA – North America
NA: 'us',
// OC – Oceania
OC: 'asia',
// South America
SA: 'us',
T1: 'eu',
};

export function getRequestRegion(req: Request): CloudflareRegion {
const continent = (req.cf?.continent as string | undefined)?.toUpperCase();
if (continent && REGIONS_BY_CONTINENT[continent.toUpperCase()]) {
return REGIONS_BY_CONTINENT[continent.toUpperCase()];
} else {
return GLOBAL_REGION;
}
}

export function getRegionDB(env: Record<string, any>, region: string): D1Database | null {
const regionalDB = `db-${region}`;
const instance = env[regionalDB];
if (instance && instance.prepare) {
return instance;
}
return null;
}

export async function synchronizeChange<T>(env: Env, callback: (db: D1Database) => Promise<T>) {
for (const localRegion of SupportedLocalRegions) {
const localDB = getRegionDB(env, localRegion);
if (localDB) {
await callback(localDB).catch(() => {
// Do better handling, like adding errors to a queue and reprocessing the DB query
console.log(`Could not synchronize to local region ${localRegion}`);
});
} else {
console.log(`Could not find the binding for the DB on the region ${localRegion}`);
}
}
}
import { Env } from '.';

type CloudflareContext = { req: Request; env: Env; ctx: ExecutionContext };
type CloudflareRegion = 'eu' | 'us' | 'asia' | 'global';
const GLOBAL_REGION: CloudflareRegion = 'global';
const SupportedLocalRegions: CloudflareRegion[] = ['eu', 'us', 'asia'];

const REGIONS_BY_CONTINENT: Record<string, CloudflareRegion> = {
// AF – Africa
AF: 'eu',
// AN – Antarctica
AN: 'us',
// AS – Asia
AS: 'asia',
// EU – Europe
EU: 'eu',
// NA – North America
NA: 'us',
// OC – Oceania
OC: 'asia',
// South America
SA: 'us',
T1: 'eu',
};

export function getRequestRegion(req: Request): CloudflareRegion {
const continent = (req.cf?.continent as string | undefined)?.toUpperCase();
if (continent && REGIONS_BY_CONTINENT[continent.toUpperCase()]) {
return REGIONS_BY_CONTINENT[continent.toUpperCase()];
} else {
return GLOBAL_REGION;
}
}

export function getRegionDB(env: Record<string, any>, region: string): D1Database | null {
const regionalDB = `db-${region}`;
const instance = env[regionalDB];
if (instance && instance.prepare) {
return instance;
}
return null;
}

export async function synchronizeChange<T>(env: Env, callback: (db: D1Database) => Promise<T>) {
for (const localRegion of SupportedLocalRegions) {
const localDB = getRegionDB(env, localRegion);
if (localDB) {
await callback(localDB).catch(() => {
// Do better handling, like adding errors to a queue and reprocessing the DB query
console.log(`Could not synchronize to local region ${localRegion}`);
});
} else {
console.log(`Could not find the binding for the DB on the region ${localRegion}`);
}
}
}
export async function executeUpdate<T>(context: CloudflareContext, updateDB: (db: D1Database) => Promise<T>) {
const globalDB = getRegionDB(context.env, GLOBAL_REGION);
if (!globalDB) throw new Error('Global DB could not be found');
const result = await updateDB(globalDB);
// Do the synchronization in a non blocking way
context.ctx.waitUntil(synchronizeChange(context.env, updateDB));
return result;
}

export async function executeSelect<T>(context: CloudflareContext, getResult: (db: D1Database) => Promise<T>): Promise<T> {
const region = getRequestRegion(context.req);
const db = getRegionDB(context.env, region);
if (db) {
return await getResult(db);
} else {
const globalDB = getRegionDB(context.env, GLOBAL_REGION);
if (!globalDB) throw new Error('Global DB could not be found');
return await getResult(globalDB);
}
}
export async function executeUpdate<T>(context: CloudflareContext, updateDB: (db: D1Database) => Promise<T>) {
const globalDB = getRegionDB(context.env, GLOBAL_REGION);
if (!globalDB) throw new Error('Global DB could not be found');
const result = await updateDB(globalDB);
// Do the synchronization in a non blocking way
context.ctx.waitUntil(synchronizeChange(context.env, updateDB));
return result;
}

export async function executeSelect<T>(context: CloudflareContext, getResult: (db: D1Database) => Promise<T>): Promise<T> {
const region = getRequestRegion(context.req);
const db = getRegionDB(context.env, region);
if (db) {
return await getResult(db);
} else {
const globalDB = getRegionDB(context.env, GLOBAL_REGION);
if (!globalDB) throw new Error('Global DB could not be found');
return await getResult(globalDB);
}
}
export async function exampleCreateUser(context: CloudflareContext, username: string) {
await executeUpdate(context, async (db) => {
await db.prepare(`insert into users(username) values(?)`).bind(username).run();
});
}
export async function exampleCreateUser(context: CloudflareContext, username: string) {
await executeUpdate(context, async (db) => {
await db.prepare(`insert into users(username) values(?)`).bind(username).run();
});
}
You can still distinguish between us-east and us-west + eu-east and eu-west if you want to by adjusting the REGIONS_BY_CONTINENT array, or doing the mapping based on the country code instead Obviously, in your bindings, you will have multiple dbs with the names: db-eu, db-us, db-asia and db-global @piko
piko
piko8mo ago
wow, thanks a emma