Skip to content

Feature/persistent cache#754

Merged
damongolding merged 20 commits into
task/releasefrom
experiment/persistant-cache
May 21, 2026
Merged

Feature/persistent cache#754
damongolding merged 20 commits into
task/releasefrom
experiment/persistant-cache

Conversation

@damongolding

@damongolding damongolding commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Optional persistent disk cache to preserve cached data across restarts; toggle via kiosk.persistent_cache.
  • Chores

    • Enabled ./data volume mapping in Docker Compose.
    • Added data/ to .gitignore.
    • Bumped version to 0.39.0-beta.1.
    • Updated frontend date-fns range and a Go dependency version.

Review Change Stack

@damongolding damongolding added the enhancement New feature or request label May 20, 2026
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds optional disk-backed persistence for the in-memory kiosk cache, exposes Asset internals as exported fields (Ctx, RequestConfig), and updates application startup/shutdown, routes, tests and all immich modules to use the new fields and persistence wiring.

Changes

Persistent Cache & Asset Field Refactor

Layer / File(s) Summary
Configuration, ignore, compose, version
config.schema.json, internal/config/config.go, .gitignore, docker-compose.yaml, taskfile.yml
Adds kiosk.persistent_cache schema/property, KioskSettings.PersistentCache and env binding; ignores data/; enables ./data volume; bumps version.
Export Asset fields
internal/immich/immich.go
Renames and exports ctxCtx and requestConfigRequestConfig on Asset and updates constructor.
Cache persistence core
internal/cache/cache.go
Replaces Initialize() with Initialize(ctx, persistentCache bool); adds RegisterPersistence handlers, periodic SaveToDisk(), LoadFromDisk(), FlushDisk(), Wait(), persisted snapshot type and exported path constants/vars.
Cache tests & init
internal/cache/cache_test.go, internal/routes/routes_test.go
Tests updated to initialise cache with test context and persistence flag; TestMain calls cancel and Flush accordingly.
App startup/shutdown & persistence handlers
main.go
Registers JSON marshal/unmarshal handlers for persisted view-data, calls LoadFromDisk() on startup, and calls cache.Wait() during shutdown; adds persistence helper marshal/unmarshal.
Routes & helpers
internal/routes/routes_cache.go, internal/routes/routes_asset_helpers.go, internal/routes/routes_asset.go
FlushCache conditionally calls cache.FlushDisk(); asset helper assigns slice to local viewDataToSave before caching; minor spacing refactor in asset handler.
Immich consumer migration
internal/immich/*.go
All immich modules updated to use Asset.RequestConfig and Asset.Ctx for URL parsing, API calls, cache keys, per-asset wiring and config-driven behaviour (videos, archived, durations, selected user, exclusions, filter date).

Sequence Diagram — persistence flow

sequenceDiagram
  participant App as Application (main)
  participant Cache as cache.Initialize
  participant Ticker as PeriodicSaver
  participant Save as SaveToDisk
  participant Gob as gob.Encoder
  App->>Cache: Initialize(ctx, persistentCache=true)
  Cache->>Ticker: start background ticker goroutine
  loop every interval or on ctx.Done()
    Ticker->>Save: SaveToDisk()
    Save->>Gob: Encode persistedCache
    Gob->>Save: Write file at PersistentCacheFilePath
  end
  App->>Cache: RegisterPersistence(marshal, unmarshal)
  App->>Cache: LoadFromDisk()
  App->>Cache: cache.Wait() on shutdown
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble bytes in moonlit patch,
Saving hops from cache to hatch,
Ctx and RequestConfig now free,
Disk keeps memories safe for me—
A carrot-coded persistence match.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/persistent cache' directly describes the main feature being added throughout the PR: persistent disk caching for the in-memory cache system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch experiment/persistant-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
internal/common/common.go (1)

107-114: ⚡ Quick win

Remove the ViewDataToBytes and ViewDataFromBytes helpers or make them symmetric for potential future use.

These helpers are currently asymmetric—ViewDataToBytes accepts a single ViewData whilst ViewDataFromBytes returns a slice []ViewData. However, the actual persistence layer uses separate persistentCacheMarshel and persistentCacheUnmarshal functions that are already symmetric (both work with []ViewData). Since these helpers are unused in the codebase, either remove them or refactor ViewDataToBytes to accept a slice for consistency if they're intended as public utilities.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/common/common.go` around lines 107 - 114, The helpers
ViewDataToBytes and ViewDataFromBytes are asymmetric (one handles ViewData, the
other []ViewData) and unused; either delete both functions or make them
symmetric to match the persistence helpers (persistentCacheMarshel and
persistentCacheUnmarshal). To fix, choose one approach: (A) remove
ViewDataToBytes and ViewDataFromBytes entirely if unused, or (B) refactor
ViewDataToBytes to accept []ViewData and return json.Marshal([]ViewData) so its
signature and behavior mirror ViewDataFromBytes and the persistentCache*
helpers; update any callers accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config.schema.json`:
- Around line 594-596: The JSON schema property "persistant_cache" is misspelled
and must be renamed to "persistent_cache" to match the config struct/env
binding; update the property key in config.schema.json from "persistant_cache"
to "persistent_cache" so validation (with additionalProperties: false) succeeds
and the setting maps correctly to your code.

In `@internal/cache/cache.go`:
- Around line 87-98: The current logic in internal/cache/cache.go checks
os.Stat(PersistentCacheBaseDir) and returns early if it doesn't exist, which
prevents creating the persistence directories on first run; change this to
attempt to create the base directory instead of returning (e.g., if
os.IsNotExist(err) then call os.MkdirAll(PersistentCacheBaseDir, 0755) and
handle errors), then proceed to ensure PersistentCacheDir exists (remove the
early return), and update the error log calls around PersistentCacheDir creation
(the block referencing PersistentCacheBaseDir and PersistentCacheDir) to report
the actual error when MkdirAll fails so persistence is created on first run.
- Around line 323-327: FlushDisk currently removes a file from the current
working directory instead of the actual persisted cache file; update the
FlushDisk function to remove the canonical persisted cache path (use the
existing PersistantCacheFile full path or construct it via filepath.Join with
the app data/cache location) and call os.Remove on that resolved path
(optionally using filepath.Abs or a helper getter to ensure the path points to
data/cache/...), keeping the same error logging (log.Error) on failure;
reference the FlushDisk function and the PersistantCacheFile identifier when
making the change.

In `@main.go`:
- Around line 375-392: The current check treats a successfully unmarshaled empty
[]common.ViewData (vd) as a failure because of "len(vd) > 0", causing valid
empty slices to fall through to the []byte branch; change the condition to
accept any successful Unmarshal (i.e., use "err == nil" only) so empty slices
are returned as valid. Update the block that iterates vd (the loop that assigns
viewData.Config = *baseConfig and sets asset.ImmichAsset.Ctx / RequestConfig) to
still work correctly when vd is empty (it will naturally no-op), and remove
reliance on len(vd) to decide success; keep the existing fallback to
unmarshaling into raw []byte only when json.Unmarshal into vd actually errors.
This touches the vd variable, the viewData loop, and the assignments to
viewData.Config and asset.ImmichAsset.{Ctx,RequestConfig}.

---

Nitpick comments:
In `@internal/common/common.go`:
- Around line 107-114: The helpers ViewDataToBytes and ViewDataFromBytes are
asymmetric (one handles ViewData, the other []ViewData) and unused; either
delete both functions or make them symmetric to match the persistence helpers
(persistentCacheMarshel and persistentCacheUnmarshal). To fix, choose one
approach: (A) remove ViewDataToBytes and ViewDataFromBytes entirely if unused,
or (B) refactor ViewDataToBytes to accept []ViewData and return
json.Marshal([]ViewData) so its signature and behavior mirror ViewDataFromBytes
and the persistentCache* helpers; update any callers accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2b1e2b6a-832c-4a5d-87cf-85cc9a4ba954

📥 Commits

Reviewing files that changed from the base of the PR and between 7bbf382 and f715826.

📒 Files selected for processing (29)
  • .gitignore
  • config.schema.json
  • docker-compose.yaml
  • internal/cache/cache.go
  • internal/cache/cache_test.go
  • internal/common/common.go
  • internal/config/config.go
  • internal/immich/immich.go
  • internal/immich/immich_album.go
  • internal/immich/immich_cache.go
  • internal/immich/immich_date.go
  • internal/immich/immich_faces.go
  • internal/immich/immich_favourites.go
  • internal/immich/immich_helpers.go
  • internal/immich/immich_memories.go
  • internal/immich/immich_person.go
  • internal/immich/immich_random.go
  • internal/immich/immich_rating.go
  • internal/immich/immich_server.go
  • internal/immich/immich_statistics.go
  • internal/immich/immich_tag.go
  • internal/immich/immich_user.go
  • internal/immich/immich_video.go
  • internal/routes/routes_asset.go
  • internal/routes/routes_asset_helpers.go
  • internal/routes/routes_cache.go
  • internal/routes/routes_test.go
  • main.go
  • taskfile.yml
💤 Files with no reviewable changes (1)
  • internal/routes/routes_asset.go

Comment thread config.schema.json Outdated
Comment thread internal/cache/cache.go
Comment thread internal/cache/cache.go
Comment thread main.go
@damongolding damongolding merged commit 86daebf into task/release May 21, 2026
3 checks passed
@damongolding damongolding deleted the experiment/persistant-cache branch May 21, 2026 10:28
This was referenced May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant