Conversation
fix(timeline): normalize timing values for precision feat(migration): add export_jobs updated_at tracking
|
The latest updates on your projects. Learn more about Vercel for GitHub.
💡 Enable Vercel Agent with $100 free credit for automated AI reviews |
Pull Request Review: エクスポート機能の安定性改善とタイムライン値の正規化Overall AssessmentThis PR introduces important improvements to export job stability and timeline value precision. The changes are well-structured and address real production concerns around timing precision and job lifecycle management. Rating: ✅ Approve with Minor Suggestions Code Quality & Best Practices✅ Strengths
|
Pull Request Review: Export Stability Improvements and Timeline Value NormalizationSummaryThis PR introduces significant improvements to export job stability and timeline value consistency. The changes are well-structured and address important reliability concerns in the video export pipeline. ✅ Strengths1. Export Reliability Improvements
2. Timeline Value Normalization
3. Database Migration
🔍 Issues FoundHIGH PRIORITY1. Race Condition in Export Polling (app/editor/[projectId]/EditorClient.tsx:298-377)Issue: Interval is not properly cleared when component unmounts or new export starts // Line 369-371: exportPollRef.current may be overwritten without clearing previous interval
exportPollRef.current = setInterval(() => {
void pollJobStatus()
}, 2000)Impact: Multiple polling intervals can run simultaneously, causing excessive API calls and memory leaks Recommendation: const pollJobStatus = async () => {
// Clear any existing interval first
clearExportPoll()
// ... polling logic ...
// Set new interval
exportPollRef.current = setInterval(() => {
void pollJobStatus()
}, 2000)
}2. Export Quality Validation Missing Type Guard Return (app/api/render/route.ts:122-124)Issue: function isValidQuality(value: string): value is ExportQuality {
return Object.prototype.hasOwnProperty.call(EXPORT_PRESETS, value);
}Recommendation: Use safer property check: function isValidQuality(value: string): value is ExportQuality {
return value in EXPORT_PRESETS;
}3. Potential Memory Leak in Queue State (lib/export/queue.ts:78-83)Issue: // Line 78-83: If error occurs before finally, counter stays incremented
const currentCount = Math.max(0, (state.runningPerUser.get(job.userId) ?? 1) - 1);
if (currentCount <= 0) {
state.runningPerUser.delete(job.userId);
}Recommendation: Ensure cleanup happens even on early errors by wrapping job execution more defensively MEDIUM PRIORITY4. Hard-coded Poll Interval (app/editor/[projectId]/EditorClient.tsx:369)Issue: 2-second polling interval is hard-coded exportPollRef.current = setInterval(() => {
void pollJobStatus()
}, 2000)Recommendation: Make configurable via environment variable for different environments (dev vs prod) 5. Missing Error Handling for Normalization (stores/timeline.ts:13-15)Issue: No validation that input is actually a number before calling Math operations function normalizeTimelineValue(value: number | undefined): number | undefined {
if (value === undefined) return undefined
return Math.max(0, Math.round(value))
}Recommendation: Add runtime type check: function normalizeTimelineValue(value: number | undefined): number | undefined {
if (value === undefined) return undefined
if (typeof value !== 'number' || !Number.isFinite(value)) return 0
return Math.max(0, Math.round(value))
}LOW PRIORITY6. Inconsistent Error Logging (lib/export/queue.ts:70 vs lib/export/server.ts:429)Issue: Some errors use 7. Magic Numbers (lib/export/server.ts:19-22)Issue: Default timeout values are hard-coded const EXPORT_FFMPEG_TIMEOUT_SECONDS = Math.max(1, Number(process.env.EXPORT_FFMPEG_TIMEOUT_SECONDS ?? 10 * 60));Recommendation: Extract to named constants with documentation about why those specific values 🔒 Security Considerations✅ Good Practices
|
Pull Request Review: Export Stability and Timeline NormalizationSummaryThis PR introduces significant improvements to export reliability and timeline value consistency. Overall, the implementation is well-architected with good defensive programming practices. I've identified several areas for improvement and potential issues. 🟢 Strengths1. Export Job Cleanup (app/api/render/route.ts:20-56)
2. Timeline Value Normalization (stores/timeline.ts:6-34)
3. FFmpeg Timeout Implementation (lib/export/server.ts:130-166)
4. Queue TTL Management (lib/export/queue.ts:96-119)
🟡 Issues & RecommendationsHigh Priority1. Race Condition in Export Polling (app/editor/[projectId]/EditorClient.tsx:303-382)Issue: The Current code: try {
await new Promise<void>((resolve, reject) => {
// ... polling logic
exportPollRef.current = setInterval(() => {
void pollJobStatus()
}, EXPORT_POLL_INTERVAL_MS)
})
} finally {
clearExportPoll()
}Recommendation: The 2. Inconsistent Quality Type (app/api/render/route.ts:21-32)Issue: The PR description mentions "480p/720p/1080p/4K" but the type definition only includes Location: features/export/types.ts:3 export type ExportQuality = '720p' | '1080p' | '4k'Recommendation: Either:
3. Potential Data Loss in Auto-Save (features/timeline/utils/autosave.ts:173-179)Issue: When timing normalization detects differences, the store is updated synchronously: if (hasTimingDiff) {
// Refresh store with sanitized values to keep UI state consistent
useTimelineStore.getState().setEffects(sanitizedEffects);
}Concern: This could trigger another auto-save cycle. While debouncing may prevent issues, it's worth verifying this doesn't cause save loops. Recommendation: Add a comment explaining why this is safe, or add a flag to prevent recursive saves. 4. Missing Database Constraint Validation (supabase/migrations/008_export_jobs_updated_at.sql:1-9)Issue: The migration assumes the Recommendation: Add a check or comment referencing where this function is defined (likely in an earlier migration). Consider adding: -- Requires: update_updated_at_column() function from migration 001 or 002Medium Priority5. Environment Variable Type Safety (app/editor/[projectId]/EditorClient.tsx:37-40)Good practice: Validates and defaults // lib/utils/env.ts
export function getEnvNumber(key: string, defaultValue: number): number {
const raw = Number(process.env[key] ?? defaultValue)
return Number.isFinite(raw) && raw > 0 ? raw : defaultValue
}This same pattern appears in:
6. Silent Failure in Cleanup (app/api/render/route.ts:136-137)if (staleError) {
console.warn("[RenderAPI] Failed to fetch stale export jobs:", staleError);
return; // ⚠️ Silent failure
}Recommendation: Consider if this should surface to the user or retry. Silent failures can hide DB connectivity issues. 7. Queue State Mutation Safety (lib/export/queue.ts:96-119)The state.pending = state.pending.filter(...)Recommendation: This is fine for server-side code, but add a comment explaining that Low Priority8. Unused Variable (app/register-sw.tsx:79)const registration = await navigator.serviceWorker.ready // ❌ UnusedStatus: ✅ Already fixed in the PR (line removed) 9. Magic Number (features/timeline/handlers/DragHandler.ts:147, TrimHandler.ts:209)The const MIN_TIMELINE_VALUE = 010. Missing TestsObservation: No test files were modified.
🔒 Security Analysis✅ Good Practices
|
PR Review: エクスポート機能の安定性改善とタイムライン値の正規化OverviewThis PR implements export stability improvements and timeline value normalization. The changes are well-structured and address important reliability issues. Overall quality is good, with some recommendations for improvement. Code Quality & Best Practices ✅Strengths
Recommendations1. Type Safety in // Current:
function isValidQuality(value: string): value is ExportQuality {
return value in EXPORT_PRESETS;
}This is good! However, consider adding explicit type assertion in the calling code to avoid potential type widening issues. 2. Magic Numbers in const EXPORT_JOB_STALE_TIMEOUT_MS = Number(process.env.EXPORT_JOB_STALE_TIMEOUT_MS ?? 5 * 60 * 1000);Should use const EXPORT_JOB_STALE_TIMEOUT_MS = getEnvNumber('EXPORT_JOB_STALE_TIMEOUT_MS', 5 * 60 * 1000, { min: 1 });3. Defensive Programming in function normalizeTimelineValue(value: number | undefined): number | undefined {
if (value === undefined) return undefined
if (!Number.isFinite(value)) return 0 // This already handles NaN
return Math.max(0, Math.round(value))
}Potential Bugs & Issues
|
変更内容
エクスポート機能の改善
タイムライン機能の改善
データベース
検証コマンド
npm run type-check npm run lint npm run test関連タスク