我们的视频在一周内静默失败 — 过期的 Env Var 让我们损失了 $60 并导致 12 位不满意的用户
Source: Dev.to
请提供您希望翻译的完整文本(除代码块和 URL 之外),我将把它翻译成简体中文并保持原有的 Markdown 格式。
TL;DR
- 发生了什么? Fal.ai 的积分被消耗,但没有交付视频,因为 Remotion Lambda 函数名称已过时。
- 为什么? 部署脚本中的手动步骤在升级 Remotion 后未能更新
REMOTION_LAMBDA_FUNCTION_NAME环境变量。 - 如何修复? 通过有针对性的重试脚本重新渲染已保存的资产,并将部署脚本自动化以保持环境变量同步。
- 接下来怎么办? 每小时的 Inngest cron 会监控卡在
rendering状态的项目,并在计费激增前提醒我们。
1. The symptom
“You know that sinking feeling when you check your billing dashboard and something doesn’t add up?”
RepoClip – my AI video‑generation SaaS – was burning ≈ $20 / day on Fal.ai credits for three consecutive days (Mar 7‑9).
The first guess was “more users = more videos”, but the videos never reached any user.
2. RepoClip 视频流水线
GitHub URL → Gemini Analysis → Kling Video Clips (fal.ai) → Remotion Lambda Render → Done
- 每个 “Video Short” → 5 AI 剪辑 (Kling 3.0 Pro) → 与旁白拼接 (Remotion on AWS Lambda)。
该流水线由 Inngest 编排,它会对每个步骤进行记忆化,以便重试时不会重新执行已完成的工作。
相关步骤(简化)
| 步骤 | 描述 |
|---|---|
| 1 | 获取 GitHub 代码 |
| 2 | 使用 Gemini 进行分析 |
| 3 | 生成视频剪辑 (fal.ai) ← 这里花费金钱 |
| 4 | 触发 Remotion Lambda 渲染 ← 失败 |
| 5 | 轮询渲染完成 |
| 6 | 将项目状态更新为 completed |
3. 计费线索
Fal.ai 仪表盘显示 $20 / day 在 Mar 7, 8, 9 → 大约 每日至少 2–4 次视频生成,这看起来对自然流量是合理的。
4. 数据库查询
SELECT
created_at::date AS date,
status,
COUNT(*) AS count
FROM projects
WHERE created_at >= '2026-03-01'
GROUP BY date, status
ORDER BY date;
结果
| 日期 | 状态 | 计数 |
|---|---|---|
| Mar 1 | completed | 3 |
| Mar 1 | failed | 5 |
| Mar 1 | rendering | 1 |
| Mar 2 | rendering | 2 |
| Mar 5 | rendering | 1 |
| Mar 6 | rendering | 2 |
| Mar 7 | rendering | 2 |
| Mar 8 | rendering | 4 |
自 3 月 1 日起,任何项目都未再达到
completed。 所有视频都卡在rendering——即 Fal.ai 完成生成剪辑后的下一步。
5. 根本原因:过期的 Lambda 函数名称
环境变量(我们原以为的)
REMOTION_LAMBDA_FUNCTION_NAME=remotion-render-4-0-414-mem2048mb-disk2048mb-600sec
实际的 AWS Lambda 函数
aws lambda list-functions --query 'Functions[?starts_with(FunctionName, `remotion`)]'
输出:
remotion-render-4-0-429-mem2048mb-disk2048mb-600sec
remotion-render-4-0-429-mem3008mb-disk4096mb-900sec
函数 remotion-render-4-0-414… 已经不存在。
我们将 Remotion 从 v4.0.414 → v4.0.429 升级,部署了新 Lambda,删除了旧的,但 忘记在 Vercel 上更新环境变量。
后果
renderMediaOnLambda()抛出ResourceNotFoundException。- Inngest 静默重试;客户端没有错误,GA4 也没有 “video_generate_complete” 事件,Sentry 也没有警报。
- 计费仍在继续,因为 Fal.ai 仍在生成剪辑。
6. Recovery – targeted retry script
所有 12 个卡住的项目已经将资产持久化(assets JSONB 列)。我们避免重新生成昂贵的 Fal.ai 剪辑,而是直接重新渲染已保存的资产。
// The key insight: assets are already saved, just re‑render
const { renderId, bucketName } = await renderMediaOnLambda({
region: REGION,
functionName: FUNCTION_NAME, // now pointing to the correct function
serveUrl: SERVE_URL,
composition: "ProductVideo",
inputProps, // built from saved assets
codec: "h264",
// …
});
结果: 12/12 视频已恢复(首次尝试恢复 9 个,因瞬时网络超时再恢复 3 个)。未产生额外的 Fal.ai 费用;用户收到了完成邮件。
7. 自动化 – 再也别忘记更新环境变量
旧的手动步骤
echo "Set the following environment variables:"
echo " REMOTION_LAMBDA_FUNCTION_NAME="
新的自动化步骤
# Extract function name from deploy output
FUNC_NAME=$(echo "$FUNC_OUTPUT" | grep -oE 'remotion-render-[a-zA-Z0-9-]+' | head -1)
# Verify function exists
aws lambda get-function --function-name "$FUNC_NAME" --region "$REGION"
# Auto‑update Vercel + local env
echo -n "$FUNC_NAME" | npx vercel env rm REMOTION_LAMBDA_FUNCTION_NAME production -y
echo -n "$FUNC_NAME" | npx vercel env add REMOTION_LAMBDA_FUNCTION_NAME production
sed -i '' "s|^REMOTION_LAMBDA_FUNCTION_NAME=.*|REMOTION_LAMBDA_FUNCTION_NAME=$FUNC_NAME|" .env.local
现在部署脚本 提取新的 Lambda 名称,进行验证,并自动更新 Vercel 和本地 .env。
8. 持续监控 – 用于卡住渲染的 cron 任务
export const monitorStuckRendersFunction = inngest.createFunction(
{ id: "monitor-stuck-renders" },
{ cron: "0 * * * *" }, // every hour
async ({ step }) => {
const stuckProjects = await step.run("check-stuck-projects", async () => {
const threshold = new Date(Date.now() - 30 * 60 * 1000).toISOString();
const { data } = await supabase
.from("projects")
.select("*")
.eq("status", "rendering")
.lt("updated_at", threshold);
return data;
});
if (stuckProjects?.length) {
// Notify Slack / email / create issue
await step.run("alert", async () => {
// …implementation…
});
}
}
);
- 它的作用: 每小时获取状态为
rendering且已超过 30 分钟的项目,并向团队发送警报。 - 为什么: 防止出现无声的计费激增,并为未来的回归提供安全保障。
9. 要点
| ✅ 成功之处 | ❌ 失败之处 |
|---|---|
| 在渲染前持久化资产 → 低成本恢复 | 手动环境变量更新步骤被遗漏 |
| Inngest 记忆化防止了 Fal.ai 的重复收费 | 静默重试隐藏了 ResourceNotFoundException |
| 计费异常触发了调查 | 缺少 GA4 “complete” 事件 → 可视性缺失 |
| 自动化部署脚本现在确保环境变量同步 | 之前没有监控卡住的渲染 |
结论: 一个微小的手动步骤导致了超过 60 美元的浪费和糟糕的用户体验。通过持久化中间资产、自动化环境更新并加入主动监控,我们将一次代价高昂的宕机转化为学习机会。 🚀
监控卡住的项目
// Example query to find projects stuck in the “rendering” state
const { data: stuckProjects } = await supabase
.from("projects")
.select("id, repo_name, content_mode, updated_at")
.eq("status", "rendering")
.lt("updated_at", threshold);
return data ?? [];
// If any projects are stuck, send an alert email with their details
if (stuckProjects.length > 0) {
// Send alert email with project details
}
如果这段代码一周前就已经存在,我们本可以在一小时内发现问题,而不是等上七天。
实时状态事件
我们添加了两个事件,当用户的浏览器通过 Supabase Realtime 接收到状态更改时触发:
// ProjectStatusListener.tsx
const channel = supabase
.channel(`project-${projectId}`)
.on(
"postgres_changes",
{ /* … */ },
(payload) => {
if (payload.new?.status === "completed") {
gaEvent("video_generate_complete", { project_id: projectId });
} else if (payload.new?.status === "failed") {
gaEvent("video_generate_fail", { project_id: projectId });
}
}
)
.subscribe();
漏斗查询(BigQuery)
-- Start‑to‑complete ratio per day
SELECT
event_date,
COUNT(DISTINCT CASE WHEN event_name = 'video_generate_start'
THEN user_pseudo_id END) AS start_users,
COUNT(DISTINCT CASE WHEN event_name = 'video_generate_complete'
THEN user_pseudo_id END) AS complete_users
FROM events_*
GROUP BY event_date;
现在,complete/start 比例的突然下降显得像是一个明确的信号。
成本发现
在调查过程中,我们意识到 每位免费用户都在收到与付费客户相同的 “Kling 3.0 Pro” 片段。
- 每段视频成本(Pro):≈ $5.60
- 转化率:≈ 3 %
- 结果:客户获取成本不可持续
解决方案
| 计划 | 片段类型 | 片段数量 | 大约时长 | 每段视频成本 |
|---|---|---|---|---|
| 免费 | Kling 3.0 Standard | 3 | ~15 秒 | $2.52 |
| 付费 | Kling 3.0 Pro | 5 | ~25 秒 | $5.60 |
- 将 “Kling 3.0 Pro 质量” 作为可感知的升级激励。
- 将免费层成本降低 55 %。
Lessons Learned
- Env vars are a silent single point of failure – 自动化它们的生命周期。
- Background‑job failures are invisible by default – 为“应该完成但未完成的任务”添加显式监控。
- Track completion, not just initiation –
video_generate_complete数据的缺失是一个关键信号。 - Persist intermediate results – 允许在不产生额外 Fal.ai 费用的情况下恢复。
- Billing anomalies are monitoring signals – 对异常的支出模式设置警报。
Try RepoClip
RepoClip generates AI‑powered promotional videos from GitHub repositories.
Paste any public repo URL and get a video in minutes — free, no credit card required.