Skip to content

Refine frame cache management with tests#2

Open
aaandreyev wants to merge 6 commits intomainfrom
codex/fix-bug-reported-in-issue-628
Open

Refine frame cache management with tests#2
aaandreyev wants to merge 6 commits intomainfrom
codex/fix-bug-reported-in-issue-628

Conversation

@aaandreyev
Copy link
Owner

@aaandreyev aaandreyev commented Oct 31, 2025

Summary

  • extract the frame cache logic into a standalone manager with stronger limit enforcement
  • update the React hook to delegate to the manager while preserving its public API
  • add Bun tests that cover cache eviction, memory budgeting, and timeline invalidation scenarios

Testing


https://chatgpt.com/codex/tasks/task_e_6904c8bd5ad48323876d94ce55cc7519

Summary by CodeRabbit

  • New Features

    • Centralized frame caching with hash-based invalidation, pre-rendering support, and tracked cache size/memory usage to improve rendering performance.
  • Tests

    • Comprehensive test suite covering caching behavior: eviction, memory limits, replacement, recency, and invalidation on timeline or project changes.
  • Chores

    • CI now runs the test suite and fails builds on test failures.

@coderabbitai
Copy link

coderabbitai bot commented Oct 31, 2025

Warning

Rate limit exceeded

@aaandreyev has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 19 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 656c279 and 2ef2a28.

📒 Files selected for processing (2)
  • apps/web/src/hooks/__tests__/frame-cache-manager.test.ts (1 hunks)
  • apps/web/src/hooks/frame-cache-manager.ts (1 hunks)

Walkthrough

Adds a FrameCacheManager module with memory- and size-aware eviction, refactors use-frame-cache to delegate caching, adds tests for cache behavior, and updates CI to run bun test (failing the job on test failures).

Changes

Cohort / File(s) Summary
CI workflow updates
\.github/workflows/bun-ci.yml
Replaced placeholder test command with bun test and removed continue-on-error: true, so test failures now fail the job.
Frame cache manager implementation
apps/web/src/hooks/frame-cache-manager.ts
New FrameCacheManager with exports: FrameCacheOptions, FrameCacheSharedResources, DEFAULT_FRAME_CACHE_OPTIONS, normalizeOptions, getSharedResources, CachedFrame, and FrameCacheManager. Implements timeline hashing, per-frame keys, caching, LRU-style eviction by count and memory (bytes/MB), and cache lifecycle APIs.
Hook refactor to use manager
apps/web/src/hooks/use-frame-cache.ts
Replaced inline cache logic with FrameCacheManager usage (via getSharedResources, normalizeOptions, manager instance). Delegates isFrameCached/getCachedFrame/cacheFrame/clear; exposes cacheSize and memoryUsageMB; re-exports FrameCacheOptions.
Frame cache tests
apps/web/src/hooks/__tests__/frame-cache-manager.test.ts
Added comprehensive tests for eviction by max cache size, memory-based eviction, skip-caching oversized frames, invalidation on timeline changes, and replacement of existing cache entries.

Sequence Diagram(s)

sequenceDiagram
    participant Component as React Component
    participant Hook as useFrameCache Hook
    participant Manager as FrameCacheManager
    participant Shared as Shared Resources

    Component->>Hook: call useFrameCache(options)
    Hook->>Hook: normalizeOptions(), create/get manager

    Note over Hook,Manager: Cache Lookup
    Component->>Hook: isFrameCached(time, sceneId)
    Hook->>Manager: isFrameCached(frameKey, timelineHash)
    Manager-->>Hook: hit / miss

    alt Cache Hit
        Component->>Hook: getCachedFrame(...)
        Hook->>Manager: getCachedFrame(frameKey, timelineHash)
        Manager->>Shared: read Map
        Manager-->>Hook: CachedFrame (ImageData)
        Hook-->>Component: ImageData
    else Cache Miss
        Component->>Hook: renderFrame()
        Hook->>Hook: schedule cache via requestIdleCallback
        Hook->>Manager: cacheFrame(frameKey, timelineHash, imageData)
        Manager->>Manager: ensureCapacity()
        alt Within limits
            Manager->>Shared: store entry, update memoryUsage
        else Overflow
            Manager->>Manager: evict oldest entries
            Manager->>Shared: remove entries, update memoryUsage
            Manager->>Shared: store entry
        end
        Manager-->>Hook: done
    end

    Note over Hook,Manager: Invalidation
    Component->>Hook: timeline/project state changed
    Hook->>Manager: invalidateCache() / clear()
    Manager->>Shared: clear Map, reset counters
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review areas to focus on:
    • frame-cache-manager.ts — eviction logic, memory accounting, timelineHash correctness.
    • use-frame-cache.ts — correct delegation, lifecycle integration, and public return shape changes.
    • __tests__/frame-cache-manager.test.ts — test coverage for edge cases and timing assumptions.
    • CI workflow change — verify bun test behavior in CI environment.

Poem

🐰 I nudge the frames and tuck them tight,
Old pixels sleep while new ones light,
Hashes hum and memory trims,
Tests now run and pass on whims,
A rabbit cheers — cache kept right. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description is incomplete relative to the provided template. While it includes a useful summary of changes and mentions testing commands, it is missing several required sections: the Type of change checkboxes, a complete "How Has This Been Tested?" section with test configuration details, the verification checklist, and a proper issue reference under "Fixes #". The testing section lists only commands rather than describing what tests were performed and their configuration (Node version, OS, browser if applicable), and there is no indication which type of change this represents (bug fix, new feature, refactoring, etc.). The description has partial content but lacks sufficient adherence to the template structure.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Refine frame cache management with tests" directly and accurately summarizes the primary changes in this pull request. The changeset involves extracting frame cache logic into a standalone FrameCacheManager module, updating the React hook to delegate to this manager, and adding comprehensive test coverage for cache eviction, memory budgeting, and timeline invalidation scenarios. The title is concise, clear, and contains no noise or vague terminology, making it immediately understandable to someone scanning the commit history.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bee7aba and 2bbacac.

📒 Files selected for processing (4)
  • .github/workflows/bun-ci.yml (1 hunks)
  • apps/web/src/hooks/__tests__/frame-cache-manager.test.ts (1 hunks)
  • apps/web/src/hooks/frame-cache-manager.ts (1 hunks)
  • apps/web/src/hooks/use-frame-cache.ts (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/src/hooks/__tests__/frame-cache-manager.test.ts (2)
apps/web/src/hooks/frame-cache-manager.ts (3)
  • DEFAULT_FRAME_CACHE_OPTIONS (27-31)
  • FrameCacheSharedResources (22-25)
  • FrameCacheManager (161-398)
apps/web/src/types/timeline.ts (1)
  • TimelineTrack (83-90)
apps/web/src/hooks/frame-cache-manager.ts (3)
apps/web/src/types/timeline.ts (3)
  • TimelineTrack (83-90)
  • MediaElement (18-22)
  • TextElement (25-40)
apps/web/src/types/media.ts (1)
  • MediaFile (4-17)
apps/web/src/types/project.ts (1)
  • TProject (13-35)
apps/web/src/hooks/use-frame-cache.ts (4)
apps/web/src/hooks/frame-cache-manager.ts (7)
  • FrameCacheOptions (16-20)
  • normalizeOptions (39-49)
  • getSharedResources (51-69)
  • FrameCacheManager (161-398)
  • isFrameCached (306-329)
  • getCachedFrame (331-357)
  • cacheResolution (184-186)
apps/web/src/types/timeline.ts (1)
  • TimelineTrack (83-90)
apps/web/src/types/media.ts (1)
  • MediaFile (4-17)
apps/web/src/types/project.ts (1)
  • TProject (13-35)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/src/hooks/frame-cache-manager.ts (2)

256-268: Consider optimizing oldest-key lookup for larger caches.

The O(n) linear search is acceptable for the default maxCacheSize of 300, but could become a bottleneck with larger caches. If performance becomes an issue, consider maintaining a min-heap or using an ordered data structure.


363-396: Consider validating frame size before caching.

The logic correctly replaces existing frames and enforces capacity limits. However, if imageData.data is undefined or empty (line 379), a 0-byte cache entry would be stored. Consider skipping the cache operation if newFrameSize === 0 to avoid storing invalid frames.

   const newFrameSize = imageData.data?.byteLength ?? 0;

+  if (newFrameSize === 0) {
+    return;
+  }
+
   if (this.cache.has(frameKey)) {
     this.removeFrame(frameKey);
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bbacac and 656c279.

📒 Files selected for processing (2)
  • apps/web/src/hooks/__tests__/frame-cache-manager.test.ts (1 hunks)
  • apps/web/src/hooks/frame-cache-manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/hooks/tests/frame-cache-manager.test.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/hooks/frame-cache-manager.ts (4)
apps/web/src/hooks/use-frame-cache.ts (1)
  • FrameCacheOptions (14-14)
apps/web/src/types/timeline.ts (3)
  • TimelineTrack (83-90)
  • MediaElement (18-22)
  • TextElement (25-40)
apps/web/src/types/media.ts (1)
  • MediaFile (4-17)
apps/web/src/types/project.ts (1)
  • TProject (13-35)
🔇 Additional comments (12)
apps/web/src/hooks/frame-cache-manager.ts (12)

5-25: LGTM! Well-structured type definitions.

The interfaces are clean and comprehensive, providing strong typing for the cache implementation.


27-49: LGTM! Sensible defaults and robust option normalization.

The default cache limits and option handling are well-implemented.


51-69: LGTM! Proper global shared state management.

The lazy initialization of shared resources via globalThis enables cache sharing across manager instances while maintaining type safety.


71-159: LGTM! Comprehensive active element computation.

The function correctly identifies active elements at a given time, properly handling muted tracks, hidden elements, and trim offsets. Only media and text element types are explicitly handled; other types are silently skipped.


161-202: LGTM! Clean constructor and accessor pattern.

The dependency injection of the now() function is excellent for testability, and the getters provide appropriate read-only access to cache state.


204-245: LGTM! Thorough timeline state hashing.

The frame key quantization and comprehensive hash generation correctly capture all state that affects rendering. The clampedTime ensures hash stability within cache resolution boundaries.


247-254: LGTM! Safe frame removal with defensive memory accounting.

The fallback chain for size and the Math.max guard prevent memory tracking from going negative even if cache state is inconsistent.


270-302: LGTM! Robust capacity management with proper fix applied.

The past review concern has been addressed: oversized frames are now rejected without clearing the entire cache (lines 279-281). The eviction loop correctly enforces both size and memory limits.

The final capacity check (lines 294-299) serves as a safety net and should rarely trigger if eviction logic is working correctly.


304-308: LGTM! Simple and correct LRU timestamp update.


310-361: LGTM! Proper cache validation with automatic invalidation.

Both methods correctly validate cached frames against current timeline state. Note that isFrameCached doesn't update the LRU timestamp (line 330 removes stale entries but doesn't touch valid ones), while getCachedFrame does (line 359). This is likely intentional—checking cache validity shouldn't affect eviction order.


398-401: LGTM! Clean cache reset.


404-404: LGTM! Proper type re-export for external consumers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant