Skip to content

Commit 559ec7f

Browse files
committed
chore: add examples for reusing workers
1 parent b3f612d commit 559ec7f

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

examples/main-session/index.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// @ts-ignore
2+
import { STATUS_CODE } from 'https://deno.land/std/http/status.ts';
3+
4+
const SESSION_HEADER_NAME = 'X-Edge-Runtime-Session-Id';
5+
const WORKERS = new Map<string, EdgeRuntime.UserWorker>();
6+
7+
setInterval(() => {
8+
const shouldBeRemoved: string[] = [];
9+
10+
for (const [uuid, worker] of WORKERS) {
11+
if (!worker.active) {
12+
shouldBeRemoved.push(uuid);
13+
}
14+
}
15+
16+
for (const uuid of shouldBeRemoved) {
17+
console.log("deleted: ", uuid);
18+
WORKERS.delete(uuid);
19+
}
20+
}, 2500);
21+
22+
console.log('main function started (session mode)');
23+
24+
Deno.serve(async (req: Request) => {
25+
const headers = new Headers({
26+
'Content-Type': 'application/json',
27+
});
28+
29+
const url = new URL(req.url);
30+
const { pathname } = url;
31+
32+
// handle health checks
33+
if (pathname === '/_internal/health') {
34+
return new Response(
35+
JSON.stringify({ 'message': 'ok' }),
36+
{
37+
status: STATUS_CODE.OK,
38+
headers,
39+
},
40+
);
41+
}
42+
43+
if (pathname === '/_internal/metric') {
44+
const metric = await EdgeRuntime.getRuntimeMetrics();
45+
return Response.json(metric);
46+
}
47+
48+
const path_parts = pathname.split('/');
49+
const service_name = path_parts[1];
50+
51+
if (!service_name || service_name === '') {
52+
const error = { msg: 'missing function name in request' };
53+
return new Response(
54+
JSON.stringify(error),
55+
{ status: STATUS_CODE.BadRequest, headers: { 'Content-Type': 'application/json' } },
56+
);
57+
}
58+
59+
const servicePath = `./examples/${service_name}`;
60+
const createWorker = async (): Promise<EdgeRuntime.UserWorker> => {
61+
const memoryLimitMb = 150;
62+
const workerTimeoutMs = 30 * 1000;
63+
const noModuleCache = false;
64+
65+
const importMapPath = null;
66+
const envVarsObj = Deno.env.toObject();
67+
const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]);
68+
const forceCreate = false;
69+
const netAccessDisabled = false;
70+
const cpuTimeSoftLimitMs = 10000;
71+
const cpuTimeHardLimitMs = 20000;
72+
73+
return await EdgeRuntime.userWorkers.create({
74+
servicePath,
75+
memoryLimitMb,
76+
workerTimeoutMs,
77+
noModuleCache,
78+
importMapPath,
79+
envVars,
80+
forceCreate,
81+
netAccessDisabled,
82+
cpuTimeSoftLimitMs,
83+
cpuTimeHardLimitMs,
84+
});
85+
};
86+
87+
const callWorker = async () => {
88+
89+
try {
90+
let worker: EdgeRuntime.UserWorker | null = null;
91+
92+
if (req.headers.get(SESSION_HEADER_NAME)) {
93+
const sessionId = req.headers.get(SESSION_HEADER_NAME)!;
94+
const complexSessionId = `${servicePath}/${sessionId}`;
95+
96+
const maybeWorker = WORKERS.get(complexSessionId);
97+
98+
if (maybeWorker && maybeWorker.active) {
99+
worker = maybeWorker;
100+
}
101+
}
102+
103+
if (!worker) {
104+
worker = await createWorker();
105+
}
106+
107+
const resp = await worker.fetch(req);
108+
109+
if (resp.headers.has(SESSION_HEADER_NAME)) {
110+
const sessionIdFromWorker = resp.headers.get(SESSION_HEADER_NAME)!;
111+
const complexSessionId = `${servicePath}/${sessionIdFromWorker}`;
112+
113+
WORKERS.set(complexSessionId, worker);
114+
}
115+
116+
return resp;
117+
} catch (e) {
118+
console.error(e);
119+
120+
if (e instanceof Deno.errors.WorkerRequestCancelled) {
121+
headers.append('Connection', 'close');
122+
}
123+
124+
const error = { msg: e.toString() };
125+
return new Response(
126+
JSON.stringify(error),
127+
{
128+
status: STATUS_CODE.InternalServerError,
129+
headers,
130+
},
131+
);
132+
}
133+
};
134+
135+
return callWorker();
136+
});

examples/serve-session/index.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// @ts-ignore
2+
import { STATUS_CODE } from "https://deno.land/std/http/status.ts";
3+
4+
type SessionStroage = { [key: string]: unknown };
5+
6+
const SESSION_HEADER_NAME = "X-Edge-Runtime-Session-Id";
7+
const SESSIONS = new Map<string, SessionStroage>();
8+
9+
function makeNewSession(): [string, SessionStroage] {
10+
const uuid = crypto.randomUUID();
11+
const storage = {};
12+
13+
SESSIONS.set(uuid, storage);
14+
return [uuid, storage];
15+
}
16+
17+
function getSessionStorageFromRequest(req: Request): SessionStroage | void {
18+
const maybeSessionId = req.headers.get(SESSION_HEADER_NAME);
19+
20+
if (typeof maybeSessionId === "string" && SESSIONS.has(maybeSessionId)) {
21+
return SESSIONS.get(maybeSessionId);
22+
}
23+
}
24+
25+
export default {
26+
fetch(req: Request) {
27+
const headers = new Headers();
28+
let storage: SessionStroage;
29+
30+
if (req.headers.get(SESSION_HEADER_NAME)) {
31+
const maybeStorage = getSessionStorageFromRequest(req);
32+
33+
if (!maybeStorage) {
34+
return new Response(null, {
35+
status: STATUS_CODE.BadRequest
36+
});
37+
}
38+
39+
storage = maybeStorage;
40+
} else {
41+
const [sessionId, newStorage] = makeNewSession();
42+
43+
headers.set(SESSION_HEADER_NAME, sessionId);
44+
45+
storage = newStorage;
46+
}
47+
48+
if (!("count" in storage)) {
49+
storage["count"] = 0;
50+
} else {
51+
(storage["count"] as number)++;
52+
}
53+
54+
const count = storage["count"] as number;
55+
56+
return new Response(
57+
JSON.stringify({ count }),
58+
{
59+
headers
60+
}
61+
);
62+
}
63+
}

0 commit comments

Comments
 (0)