Skip to content

Cache full processed pipeline for faster response#1075

Open
Djangowaming wants to merge 111 commits into
Viren070:mainfrom
Djangowaming:feature/full-pipeline-cache
Open

Cache full processed pipeline for faster response#1075
Djangowaming wants to merge 111 commits into
Viren070:mainfrom
Djangowaming:feature/full-pipeline-cache

Conversation

@Djangowaming

@Djangowaming Djangowaming commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Adds a configurable in-memory cache for the fully-processed Stremio stream response, so repeat requests for the same (user, type, id) skip the entire pipeline. Off by default

Problem

Every Stremio stream request runs the full pipeline — fetching manifests, service wrapping, deduplication, filtering, sorting, SEL evaluation, formatting, and proxying. Stremio frequently re-requests the same (type, id) when users refresh, navigate back, or click between episodes, making this redundant.

Solution

Cache the final AIOStreamResponse in an in-memory Map<string, CachedStreamResult>. Uses a dedicated Map rather than the generic Cache class to avoid structuredClone overhead on large stream arrays.

  • Key: {uuid}:{type}:{id} — per-user, per-media-item
  • TTL: Configurable via STREAM_RESULT_CACHE_TTL (default 30s, 0 = disabled)
  • Max size: Configurable via STREAM_RESULT_CACHE_MAX_SIZE (default 1000)
  • Eviction: LRU — oldest entry evicted when at capacity; expired entries cleaned lazily on access plus a 30s background timer

Changes

File Change
packages/core/src/config/schema/resources.ts Added cache.streamResult config section with ttl and maxSize
packages/server/src/routes/stremio/stream.ts Added in-memory stream result cache with LRU eviction, wired into the stream request handler

Configuration

Env var Default Description
STREAM_RESULT_CACHE_TTL 0 TTL in seconds for cached stream results (0 to disable)
STREAM_RESULT_CACHE_MAX_SIZE 1000 Maximum number of cached entries (requires restart)

Can be viewed/edited in dashboard:
image

This will be a stricter cache than the one used by STREAM_CACHE_TTL.

Summary by CodeRabbit

  • New Features
    • Added stream-result caching to improve performance on repeated requests (applies to non-precaching calls when enabled).
    • Introduced configurable cache settings for stream result ttl and maxSize, with environment variable support.
  • Bug Fixes
    • Cached responses now correctly restore stream context and refresh required stream data before returning.
    • Improved stream endpoint response handling by generating the transformed output once prior to sending the JSON response.

Viren070 added 30 commits July 5, 2026 19:16
The old nzbFailover.ts was nzb-only; this overhauls it into a generic failover
chain (FailoverContentType = 'usenet' | 'debrid') that also handles debrid and
supports parallel queuing / staggered races (parallel, staggerMs,
preferredGraceMs, maxWaitMs).
Viren070 and others added 16 commits July 5, 2026 19:17
…lity (Viren070#1070)

Cloudflare classifies requests by the URL path extension alone: a stream
URL ending in /<filename>.mkv is treated as a cacheable file type, so on
every cache miss (guaranteed, as the route sends no-store) the proxy
requests the entire file from origin and never forwards the client's
Range header. Every seek then costs a full-file pull, playback stalls,
and abandoned pulls pile up as orphaned full-file readers.

Dropping the filename segment makes Cloudflare treat the response as
dynamic and pass Range through verbatim. The route already accepts both
forms, so previously minted URLs keep working; players still receive the
real filename via Content-Disposition.

Co-authored-by: Viren070 <viren070@protonmail.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 14dca93e-11d8-4747-9b9f-f244db76c123

📥 Commits

Reviewing files that changed from the base of the PR and between 5e9277c and 885b265.

📒 Files selected for processing (1)
  • packages/core/src/main/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/main/index.ts

Walkthrough

Adds a cache.streamResult schema section, implements conditional pipeline-result caching in AIOStreams.getStreams keyed by user, type, and id, and updates the stream route to remove now-unused caching imports and refactor response building.

Changes

Stream result caching

Layer / File(s) Summary
Cache configuration schema
packages/core/src/config/schema/resources.ts
Adds nonNegativeInt import and a new cache.streamResult block defining ttl (non-negative integer, default 0) and maxSize (positive integer, default 1000) with env vars and UI metadata.
Pipeline result caching in getStreams
packages/core/src/main/index.ts
getStreams conditionally caches non-precaching results when TTL is positive, using a Cache instance keyed by user/type/id, recreating streamContext on cache hits, and writing fresh results on misses.
Stream route cleanup
packages/server/src/routes/stremio/stream.ts
Removes unused Cache and IdParser imports, adds a comment noting caching now lives in getStreams(), and refactors the response path to assign transformedResponse before sending it.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Route as stream.ts route
  participant AIOStreams as AIOStreams.getStreams
  participant Cache as Cache('pipeline-result')
  participant Pipeline as _getStreams

  Route->>AIOStreams: getStreams(type, id, userData)
  AIOStreams->>Cache: get(cacheKey)
  alt cache hit
    Cache-->>AIOStreams: cached result
    AIOStreams->>AIOStreams: recreate streamContext
    AIOStreams-->>Route: cached result
  else cache miss
    AIOStreams->>Pipeline: _getStreams(type, id, userData)
    Pipeline-->>AIOStreams: result
    AIOStreams->>Cache: set(cacheKey, result, ttl)
    AIOStreams-->>Route: result
  end
Loading

Poem

A rabbit hops through streams so fast,
caching hits to make things last,
ttl set, keys well spun,
results reused, the work is done,
hop, hop, cache — the race is won! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: caching the fully processed stream pipeline to improve response time.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@Djangowaming Djangowaming changed the title Feature/full pipeline cache Cache full processed pipeline for faster response Jul 5, 2026

@Viren070 Viren070 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

the caching itself needs to be done in the AIOStreams class so all consumers of it benefit (e.g. search api route, torznab apis etc.).

why not use the existing Cache class ?

@Djangowaming

Copy link
Copy Markdown
Contributor Author

the caching itself needs to be done in the AIOStreams class so all consumers of it benefit (e.g. search api route, torznab apis etc.).

why not use the existing Cache class ?

Lemme look into it.

@Djangowaming

Copy link
Copy Markdown
Contributor Author

the caching itself needs to be done in the AIOStreams class so all consumers of it benefit (e.g. search api route, torznab apis etc.).

why not use the existing Cache class ?

I think I addressed your feedback, please take a look now.

@Djangowaming Djangowaming changed the base branch from dev to main July 5, 2026 21:53

@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: 1

🧹 Nitpick comments (1)
packages/core/src/main/index.ts (1)

88-124: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Concurrent requests for the same key will both miss and recompute (cache stampede).

Two simultaneous requests for the same (uuid, type, id) before the cache is populated will both fall through to _getStreams, doing the full pipeline work twice. Since the pipeline is described as expensive (addon fetching, filtering, sorting), an in-flight request de-duplication (e.g., caching the in-flight promise) would improve the benefit of this cache under concurrent access such as page refresh bursts.

🤖 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 `@packages/core/src/main/index.ts` around lines 88 - 124, The pipeline result
cache in the _getStreams flow currently only stores completed values, so
concurrent requests for the same cacheKey can both miss and run the expensive
pipeline work twice. Update the cache logic around pipelineCache.get/set to
deduplicate in-flight requests for the same (uuid, type, id), for example by
storing the pending _getStreams promise or otherwise sharing a single execution
until it resolves, while keeping the existing cache hit behavior and
StreamContext.create reset on hits.
🤖 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 `@packages/core/src/main/index.ts`:
- Around line 94-122: The cache-hit branch in index.ts only recreates
StreamContext with StreamContext.create(), but it misses formatter-relevant
state used by toFormatterContext() such as _metadata, _episodeDetails, and
_seadex. Update the pipeline-result cache flow so the cache key/result also
carries this formatter context, or rehydrate those fields on a cache hit using
the same data populated by startAllFetches() in _getStreams, ensuring cached and
non-cached paths produce equivalent formatting behavior.

---

Nitpick comments:
In `@packages/core/src/main/index.ts`:
- Around line 88-124: The pipeline result cache in the _getStreams flow
currently only stores completed values, so concurrent requests for the same
cacheKey can both miss and run the expensive pipeline work twice. Update the
cache logic around pipelineCache.get/set to deduplicate in-flight requests for
the same (uuid, type, id), for example by storing the pending _getStreams
promise or otherwise sharing a single execution until it resolves, while keeping
the existing cache hit behavior and StreamContext.create reset on hits.
🪄 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: 716429fe-49e3-4c58-b28f-6aaaa9fb759f

📥 Commits

Reviewing files that changed from the base of the PR and between c7fa0fc and 5e9277c.

📒 Files selected for processing (3)
  • packages/core/src/config/schema/resources.ts
  • packages/core/src/main/index.ts
  • packages/server/src/routes/stremio/stream.ts

Comment thread packages/core/src/main/index.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants