feat(anyhunt/video-transcript): implement dual-mode transcript pipeline#80
feat(anyhunt/video-transcript): implement dual-mode transcript pipeline#80
Conversation
add LOCAL+CLOUD_FALLBACK workers, queue orchestration, runtime switch, and budget guard add console/admin pages, prisma migration, local deployment script, and deployment checklist docs
ensure activeTasks is decremented and task is finalized when createWorkspace fails
use domain-boundary hostname checks to prevent suffix-matching bypass
cloud fallback completion uses terminal-state guard and conditional update
keep default 5m timeout config for existing queues; use a dedicated Bull configKey for long-running video transcript jobs
|
已按最佳实践补齐并修复 review threads(已全部 resolve):
|
|
已按 PR review 修复并推送到分支
对应变更:
|
| if (reason === 'timeout' && !preemptSignaled) { | ||
| cloudOwnershipAcquired = await this.acquireCloudOwnership( | ||
| taskId, | ||
| 'DOWNLOADING', | ||
| startedAt, | ||
| ); | ||
| if (!cloudOwnershipAcquired) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
🔴 Budget reservation leak when non-probe ownership acquisition fails
In the cloud fallback processor's handleCloudRun, when probedDurationSec === 0 (probe failed) and reason === 'timeout', budget is reserved using actual audio duration at video-transcript-cloud-fallback.processor.ts:196-197. Then at lines 208-216, if acquireCloudOwnership fails (e.g., local already completed or task was cancelled), the code returns without releasing the reserved budget. This is a budget leak that inflates the daily usage counter in Redis.
The PR explicitly fixed the analogous leak on the probe path (lines 163-167 correctly call releaseCloudBudgetReservation before returning), but missed the non-probe fallback path. The CLAUDE.md constraint at apps/anyhunt/server/src/video-transcript/CLAUDE.md states: "timeout 路径若在 probe 预算后丢失 CLOUD_FALLBACK 执行权,必须回滚这次 budget reservation" — this same principle applies to the non-probe reservation path.
| if (reason === 'timeout' && !preemptSignaled) { | |
| cloudOwnershipAcquired = await this.acquireCloudOwnership( | |
| taskId, | |
| 'DOWNLOADING', | |
| startedAt, | |
| ); | |
| if (!cloudOwnershipAcquired) { | |
| return; | |
| } | |
| if (reason === 'timeout' && !preemptSignaled) { | |
| cloudOwnershipAcquired = await this.acquireCloudOwnership( | |
| taskId, | |
| 'DOWNLOADING', | |
| startedAt, | |
| ); | |
| if (!cloudOwnershipAcquired) { | |
| if (budgetReservation) { | |
| await this.budgetService.releaseCloudBudgetReservation( | |
| budgetReservation, | |
| ); | |
| } | |
| return; | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 28d91c109d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const dir = path.join(os.tmpdir(), 'anyhunt-video-transcript', taskId); | ||
| await mkdir(dir, { recursive: true }); |
There was a problem hiding this comment.
Isolate workspace directory per executor
createWorkspace derives the path only from taskId, and both LOCAL and CLOUD_FALLBACK processors call it with the same ID. In timeout fallback races (where cloud starts while local may still be running), both workers read/write video.*, audio.wav, and transcript files in the same directory, and either worker's cleanup can delete files the other still needs, causing spurious failures or corrupted outputs.
Useful? React with 👍 / 👎.
| startedAt, | ||
| ); | ||
| if (!cloudOwnershipAcquired) { | ||
| return; |
There was a problem hiding this comment.
Release reserved budget when timeout takeover loses race
In the probeVideoDurationSeconds() === 0 path, budget is reserved later from measured audio duration, but if cloud ownership acquisition then fails, execution returns immediately without rolling back that reservation. This leaks daily cloud budget for tasks that never actually entered cloud execution (e.g., local completed/cancelled during the pre-takeover work), which can incorrectly exhaust the budget gate for subsequent tasks.
Useful? React with 👍 / 👎.
add LOCAL+CLOUD_FALLBACK workers, queue orchestration, runtime switch, and budget guard
add console/admin pages, prisma migration, local deployment script, and deployment checklist docs