Skip to content

Commit ba60801

Browse files
committed
fix: add cpu/wall clock limit tests
1 parent ea41901 commit ba60801

File tree

5 files changed

+290
-1
lines changed

5 files changed

+290
-1
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { serve } from "https://deno.land/[email protected]/http/server.ts"
2+
3+
console.log('main function started');
4+
5+
serve(async (req: Request) => {
6+
const url = new URL(req.url);
7+
const {pathname} = url;
8+
const path_parts = pathname.split("/");
9+
const service_name = path_parts[1];
10+
11+
if (!service_name || service_name === "") {
12+
const error = { msg: "missing function name in request" }
13+
return new Response(
14+
JSON.stringify(error),
15+
{ status: 400, headers: { "Content-Type": "application/json" } },
16+
)
17+
}
18+
19+
const servicePath = `./test_cases/${service_name}`;
20+
console.error(`serving the request with ${servicePath}`);
21+
22+
const createWorker = async () => {
23+
const cpuTimeSoftLimitMs = 10;
24+
const cpuTimeHardLimitMs = 30;
25+
26+
const memoryLimitMb = 150;
27+
const workerTimeoutMs = 10 * 60 * 1000;
28+
const noModuleCache = false;
29+
const importMapPath = null;
30+
const envVarsObj = Deno.env.toObject();
31+
const envVars = Object.keys(envVarsObj).map(k => [k, envVarsObj[k]]);
32+
33+
return await EdgeRuntime.userWorkers.create({
34+
servicePath,
35+
memoryLimitMb,
36+
workerTimeoutMs,
37+
cpuTimeSoftLimitMs,
38+
cpuTimeHardLimitMs,
39+
noModuleCache,
40+
importMapPath,
41+
envVars
42+
});
43+
}
44+
45+
const callWorker = async () => {
46+
try {
47+
const worker = await createWorker();
48+
return await worker.fetch(req);
49+
} catch (e) {
50+
console.error(e);
51+
52+
// if (e instanceof Deno.errors.WorkerRequestCancelled) {
53+
// return await callWorker();
54+
// }
55+
56+
const error = { msg: e.toString() }
57+
return new Response(
58+
JSON.stringify(error),
59+
{ status: 500, headers: { "Content-Type": "application/json" } },
60+
);
61+
}
62+
}
63+
64+
return callWorker();
65+
})
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { serve } from "https://deno.land/[email protected]/http/server.ts"
2+
3+
console.log('main function started');
4+
5+
serve(async (req: Request) => {
6+
const url = new URL(req.url);
7+
const {pathname} = url;
8+
const path_parts = pathname.split("/");
9+
const service_name = path_parts[1];
10+
11+
if (!service_name || service_name === "") {
12+
const error = { msg: "missing function name in request" }
13+
return new Response(
14+
JSON.stringify(error),
15+
{ status: 400, headers: { "Content-Type": "application/json" } },
16+
)
17+
}
18+
19+
const servicePath = `./test_cases/${service_name}`;
20+
console.error(`serving the request with ${servicePath}`);
21+
22+
const createWorker = async () => {
23+
// NOTE: It should be greater than 100ms
24+
const workerTimeoutMs = 120;
25+
26+
const memoryLimitMb = 150;
27+
const cpuTimeSoftLimitMs = 10 * 60 * 1000;
28+
const cpuTimeHardLimitMs = 10 * 60 * 1000;
29+
const noModuleCache = false;
30+
const importMapPath = null;
31+
const envVarsObj = Deno.env.toObject();
32+
const envVars = Object.keys(envVarsObj).map(k => [k, envVarsObj[k]]);
33+
34+
return await EdgeRuntime.userWorkers.create({
35+
servicePath,
36+
memoryLimitMb,
37+
workerTimeoutMs,
38+
cpuTimeSoftLimitMs,
39+
cpuTimeHardLimitMs,
40+
noModuleCache,
41+
importMapPath,
42+
envVars
43+
});
44+
}
45+
46+
const callWorker = async () => {
47+
try {
48+
const worker = await createWorker();
49+
return await worker.fetch(req);
50+
} catch (e) {
51+
console.error(e);
52+
53+
// if (e instanceof Deno.errors.WorkerRequestCancelled) {
54+
// return await callWorker();
55+
// }
56+
57+
const error = { msg: e.toString() }
58+
return new Response(
59+
JSON.stringify(error),
60+
{ status: 500, headers: { "Content-Type": "application/json" } },
61+
);
62+
}
63+
}
64+
65+
return callWorker();
66+
})
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { serve } from "https://deno.land/[email protected]/http/server.ts"
2+
3+
console.log('main function started');
4+
5+
serve(async (req: Request) => {
6+
const url = new URL(req.url);
7+
const {pathname} = url;
8+
const path_parts = pathname.split("/");
9+
const service_name = path_parts[1];
10+
11+
if (!service_name || service_name === "") {
12+
const error = { msg: "missing function name in request" }
13+
return new Response(
14+
JSON.stringify(error),
15+
{ status: 400, headers: { "Content-Type": "application/json" } },
16+
)
17+
}
18+
19+
const servicePath = `./test_cases/${service_name}`;
20+
console.error(`serving the request with ${servicePath}`);
21+
22+
const createWorker = async () => {
23+
const workerTimeoutMs = 99;
24+
25+
const memoryLimitMb = 150;
26+
const cpuTimeSoftLimitMs = 10 * 60 * 1000;
27+
const cpuTimeHardLimitMs = 10 * 60 * 1000;
28+
const noModuleCache = false;
29+
const importMapPath = null;
30+
const envVarsObj = Deno.env.toObject();
31+
const envVars = Object.keys(envVarsObj).map(k => [k, envVarsObj[k]]);
32+
33+
return await EdgeRuntime.userWorkers.create({
34+
servicePath,
35+
memoryLimitMb,
36+
workerTimeoutMs,
37+
cpuTimeSoftLimitMs,
38+
cpuTimeHardLimitMs,
39+
noModuleCache,
40+
importMapPath,
41+
envVars
42+
});
43+
}
44+
45+
const callWorker = async () => {
46+
try {
47+
const worker = await createWorker();
48+
return await worker.fetch(req);
49+
} catch (e) {
50+
console.error(e);
51+
52+
// if (e instanceof Deno.errors.WorkerRequestCancelled) {
53+
// return await callWorker();
54+
// }
55+
56+
const error = { msg: e.toString() }
57+
return new Response(
58+
JSON.stringify(error),
59+
{ status: 500, headers: { "Content-Type": "application/json" } },
60+
);
61+
}
62+
}
63+
64+
return callWorker();
65+
})

crates/base/test_cases/slow_resp/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
Deno.serve(() => {
1+
Deno.serve(async (req: Request) => {
22
// NOTE(Nyannyacha): This should be hot enough to V8 decides JIT compilation.
3+
4+
let nothing = await (req.method === "POST" ? req.text() : Promise.resolve(""));
5+
6+
if(req.method === "POST") {
7+
console.log(nothing);
8+
}
39

410
const before = performance.now();
511
const num = mySlowFunction(11);

crates/base/tests/request_failure_tests.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,93 @@ async fn req_failure_case_timeout() {
5252
assert!(found_timeout);
5353
}
5454

55+
#[tokio::test]
56+
#[serial]
57+
async fn req_failure_case_cpu_time_exhausted() {
58+
let tb = TestBedBuilder::new("./test_cases/main_small_cpu_time")
59+
.with_oneshot_policy(100000)
60+
.build()
61+
.await;
62+
63+
let mut res = tb
64+
.request(|| {
65+
Request::builder()
66+
.uri("/slow_resp")
67+
.method("GET")
68+
.body(Body::empty())
69+
.context("can't make request")
70+
})
71+
.await
72+
.unwrap();
73+
74+
let buf = to_bytes(res.body_mut()).await.unwrap();
75+
76+
assert_eq!(
77+
buf,
78+
"{\"msg\":\"WorkerRequestCancelled: request has been cancelled by supervisor\"}"
79+
);
80+
81+
tb.exit().await;
82+
}
83+
84+
#[tokio::test]
85+
#[serial]
86+
async fn req_failure_case_wall_clock_reached() {
87+
let tb = TestBedBuilder::new("./test_cases/main_small_wall_clock")
88+
.with_oneshot_policy(100000)
89+
.build()
90+
.await;
91+
92+
let mut res = tb
93+
.request(|| {
94+
Request::builder()
95+
.uri("/slow_resp")
96+
.method("GET")
97+
.body(Body::empty())
98+
.context("can't make request")
99+
})
100+
.await
101+
.unwrap();
102+
103+
let buf = to_bytes(res.body_mut()).await.unwrap();
104+
105+
assert_eq!(
106+
buf,
107+
"{\"msg\":\"WorkerRequestCancelled: request has been cancelled by supervisor\"}"
108+
);
109+
110+
tb.exit().await;
111+
}
112+
113+
#[tokio::test]
114+
#[serial]
115+
async fn req_failure_case_wall_clock_reached_less_than_100ms() {
116+
let tb = TestBedBuilder::new("./test_cases/main_small_wall_clock_less_than_100ms")
117+
.with_oneshot_policy(100000)
118+
.build()
119+
.await;
120+
121+
let mut res = tb
122+
.request(|| {
123+
Request::builder()
124+
.uri("/slow_resp")
125+
.method("GET")
126+
.body(Body::empty())
127+
.context("can't make request")
128+
})
129+
.await
130+
.unwrap();
131+
132+
let buf = to_bytes(res.body_mut()).await.unwrap();
133+
134+
assert!(
135+
buf == "{\"msg\":\"InvalidWorkerResponse: user worker failed to respond\"}"
136+
|| buf == "{\"msg\":\"InvalidWorkerCreation: worker did not respond in time\"}"
137+
);
138+
139+
tb.exit().await;
140+
}
141+
55142
#[tokio::test]
56143
#[serial]
57144
async fn req_failure_case_intentional_peer_reset() {

0 commit comments

Comments
 (0)