背景
在自部署 Supabase 环境中,Edge Functions 默认超时时间为 60 秒 。业务场景需要一个耗时较长的函数(约 350 秒),因此需要将超时延长至 360 秒。
自部署 Supabase 的 Edge Functions 超时涉及多个层级,任何一层配置不正确都会导致请求被提前切断。本文记录了完整的排查过程和最终解决方案。
一、问题现象
创建一个测试函数 slow-test,等待 90 秒后返回:
javascript
Deno.serve(async (req: Request) => {
const startTime = Date.now();
console.log("[slow-test] 函数开始执行,时间戳: " + startTime);
await new Promise((resolve) => setTimeout(resolve, 90000));
const duration = (Date.now() - startTime) / 1000;
console.log("[slow-test] 等待结束,实际耗时: " + duration + " 秒");
return new Response(JSON.stringify({ message: "成功执行了 " + duration + " 秒" }), {
headers: { "Content-Type": "application/json" },
});
});
现象:函数在约 60 秒时被强制终止,日志显示:
vbscript
wall clock duration warning: isolate: xxx
wall clock duration reached: isolate: xxx (in_flight_req_exists = true)
failed to send request to user worker: request has been cancelled by supervisor
user worker failed to respond: request has been cancelled by supervisor
二、超时链路分析
Supabase Edge Functions 的请求链路涉及三个独立层级,每个层级都有自己的超时限制:
java
客户端 → Kong 网关 → Edge Runtime (Worker) → 用户函数
↓ ↓
read_timeout workerTimeoutMs
| 层级 | 配置位置 | 默认值 | 作用 |
|---|---|---|---|
| Kong 网关 | kong.yml 中 functions-v1.read_timeout | 150000ms (150s) | 网关等待上游响应的时长 |
| Edge Runtime 参数 | docker-compose.yml 命令行参数 | 60s | Worker 空闲超时 |
| Edge Runtime 硬编码 | main/index.ts 中 workerTimeoutMs | 60s | 最终生效的硬上限 |
| 环境变量 | docker-compose.yml 环境变量 | --- | 辅助配置,可能被覆盖 |
关键原则 :main/index.ts 中的 workerTimeoutMs 是最终生效的硬编码值,优先级高于环境变量和命令行参数。
三、完整解决方案
3.1 修改 main/index.ts(核心)
这是最关键的一步,Edge Runtime 内部的硬编码超时值优先于所有环境变量和命令行参数。
找到 volumes/functions/main/index.ts,修改 workerTimeoutMs:
arduino
// 修改前
const workerTimeoutMs = 60 * 1000; // 60秒
// 修改后
const workerTimeoutMs = 360 * 1000; // 360秒
3.2 修改 kong.yml
找到 volumes/api/kong.yml 中的 functions-v1 服务,修改超时配置:
yaml
- name: functions-v1
_comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*'
url: http://functions:9000/
read_timeout: 370000 # 从 150000 改为 370000(370秒)
write_timeout: 370000 # 新增
connect_timeout: 30000 # 新增
routes:
- name: functions-v1-all
strip_path: true
paths:
- /functions/v1/
plugins:
- name: cors
注意 :Kong 的超时单位是毫秒。370000 表示 370 秒,比 Edge Runtime 的 360 秒多 10 秒缓冲,避免边界问题。
3.3 修改 docker-compose.yml
kong 服务 --- 环境变量:
yaml
kong:
environment:
# ... 已有配置 ...
KONG_NGINX_PROXY_PROXY_READ_TIMEOUT: "370s"
KONG_NGINX_PROXY_PROXY_SEND_TIMEOUT: "370s"
KONG_NGINX_PROXY_PROXY_CONNECT_TIMEOUT: "30s"
functions 服务 --- 环境变量与启动命令:
bash
functions:
environment:
# ... 已有配置 ...
FUNCTIONS_WORKER_TIMEOUT_MS: "360000"
EDGE_RUNTIME_WORKER_TIMEOUT_MS: "360000"
command:
[
"start",
"--main-service",
"/home/deno/functions/main",
"--user-worker-request-idle-timeout",
"360000",
"--request-read-timeout",
"360000",
"--request-wait-timeout",
"360000"
]
顶层 volumes --- 补充 deno-cache :
yaml
volumes:
db-config:
deno-cache: # 确保这一行存在
supabase_db_data:
四、配置层级总结
| 层级 | 配置文件 | 配置项 | 值 |
|---|---|---|---|
| Kong 网关 | kong.yml | functions-v1.read_timeout | 370000ms |
| Kong 网关(兜底) | docker-compose.yml (kong) | KONG_NGINX_PROXY_PROXY_READ_TIMEOUT | 370s |
| Edge Runtime 参数 | docker-compose.yml (functions) | --user-worker-request-idle-timeout | 360000ms |
| Edge Runtime 环境变量 | docker-compose.yml (functions) | FUNCTIONS_WORKER_TIMEOUT_MS | 360000 |
| Edge Runtime 硬编码 | main/index.ts | workerTimeoutMs | 360 * 1000 |
必须形成的关系:Kong(370s) > Edge Runtime(360s) > 实际需求
五、验证测试
5.1 测试函数
javascript
// volumes/functions/slow-test/index.ts
Deno.serve(async (req: Request) => {
const startTime = Date.now();
console.log("[slow-test] 函数开始执行,时间戳: " + startTime);
const waitSeconds = 350; // 测试值
console.log("[slow-test] 开始等待 " + waitSeconds + " 秒...");
await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
const duration = (Date.now() - startTime) / 1000;
console.log("[slow-test] 等待结束,实际耗时: " + duration + " 秒");
return new Response(
JSON.stringify({
message: "成功执行了 " + duration + " 秒的操作",
durationSeconds: duration,
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
});
5.2 触发测试
bash
ANON_KEY=$(grep ANON_KEY .env | cut -d '=' -f2)
curl -X POST "http://127.0.0.1:8000/functions/v1/slow-test" \
-H "Authorization: Bearer ${ANON_KEY}" \
-H "Content-Type: application/json"
5.3 成功日志
css
[Info] [slow-test] 开始等待 350 秒...
[Info] [slow-test] 等待结束,实际耗时: 349.986 秒
5.4 验证命令
perl
# 确认 Kong 环境变量
docker exec -it supabase-kong env | grep -iE "PROXY_READ|PROXY_SEND|PROXY_CONNECT"
# 确认 Edge Runtime 启动参数
docker inspect supabase-edge-functions --format '{{.Config.Cmd}}'
# 确认 main/index.ts 中的超时值
grep -n "workerTimeoutMs" ./volumes/functions/main/index.ts
# 确认 kong.yml 中的超时值
grep -n "read_timeout" ./volumes/api/kong.yml
六、踩坑记录
坑 1:只改环境变量不生效
原因:main/index.ts 中的 workerTimeoutMs 是硬编码值,优先级高于环境变量。
解决:必须修改 main/index.ts 中的 workerTimeoutMs。
坑 2:Kong 返回 The upstream server is timing out
原因 :kong.yml 中 functions-v1 服务的 read_timeout 是 150000(150秒),服务级配置优先级高于环境变量。
解决:修改 kong.yml 中的 read_timeout 为 370000。
坑 3:开发环境配置不生效
原因 :开发环境使用了独立的 main/index.ts 文件,其 workerTimeoutMs 仍然是默认的 60 秒。
解决:每个环境都需要单独修改对应的 main/index.ts 文件。
坑 4:参数传递正确但仍不生效
原因:参数虽然被正确传递到容器(通过 docker inspect 确认),但 Edge Runtime 版本可能不识别这些参数。
解决:确认 Edge Runtime 版本兼容性,并确保 main/index.ts 中的硬编码值已修改。
七、注意事项
7.1 CPU 时间限制仍然存在
即使墙上时钟延长到 360 秒,CPU 执行时间限制仍然是 2 秒。纯 setTimeout 等待(I/O 密集型)不受影响,但计算密集型任务仍会在 2 秒左右被终止。
7.2 请求空闲超时
请求空闲超时(Request Idle Timeout)默认是 150 秒。如果函数在 150 秒内没有发出响应,平台仍会返回 504 错误。
7.3 最大支持 400 秒
自部署 Supabase 的 Edge Functions 墙上时钟上限最大可配置为 400 秒。如需延长到 400 秒,将所有相关值从 360000 改为 400000,Kong 的 read_timeout 改为 410000 即可。
7.4 重启方式
修改配置后必须完全停止并重建,不能用 restart:
bash
docker compose down kong functions
docker compose up -d kong functions
八、总结
| 问题 | 解决方案 |
|---|---|
| 默认 60 秒超时 | 修改 main/index.ts 中的 workerTimeoutMs |
| Kong 150 秒切断 | 修改 kong.yml 中 functions-v1.read_timeout |
| 环境变量不生效 | 硬编码值优先级最高,必须改代码 |
| 开发环境独立配置 | 每个环境单独修改 main/index.ts |
核心结论 :Supabase Edge Functions 超时配置需要三层对齐,其中 main/index.ts 中的 workerTimeoutMs 是最终生效的硬编码值,是最关键的配置项。