perf(series): list a series' episodes with one concurrent season walk

"More info" on Frasier took ~10 s. The series page asked Rust for the
episodes and for the current episode as two commands; each walked every
season, and each walk fetched the eleven seasons one after another. So the
wait was the sum of twenty-two listings, each a cache read queued behind
whatever the database was writing — measured at ~4 s per walk on a Fairphone
5 while the launch-time catalog sync ran.

Seasons are now fetched together (gather_season_episodes), so a walk waits
for its slowest season, not the sum. And repository_get_series_view returns
the episodes and the current episode from one walk, with Next Up and resume
fetched alongside it; the series page makes that one call.

Under today's single database connection the cache reads themselves still
queue on its mutex; the concurrency pays off fully once reads get their own
connections. Halving the walks helps regardless.

Test first: ten 100 ms seasons took 1.01 s sequentially; now well under the
400 ms bound, with a failing season still leaving the rest.

DR-295, UT-264.
This commit is contained in:
2026-09-24 03:22:31 +02:00
parent 9dd44eeada
commit f6efa7208a
7 changed files with 190 additions and 25 deletions
+18 -1
View File
@@ -3,7 +3,13 @@
// NO direct HTTP calls - everything routes through Rust backend
import { commands } from "./bindings";
import type { DownloadDiskUsage, JRayActor, SearchScope, StreamSelection } from "./bindings";
import type {
DownloadDiskUsage,
JRayActor,
SearchScope,
SeriesView,
StreamSelection,
} from "./bindings";
import type { QualityPreset } from "./quality-presets";
import type {
Library,
@@ -164,6 +170,17 @@ export class RepositoryClient {
return commands.repositoryGetSeriesCurrentEpisode(this.ensureHandle(), seriesId);
}
/**
* A series' episodes and the episode the viewer is up to, from one season
* fan-out in Rust. Prefer this over calling `getSeriesEpisodes` and
* `getSeriesCurrentEpisode` together — each of those walks every season.
*
* TRACES: UR-062 | DR-101, DR-295
*/
async getSeriesView(seriesId: string): Promise<SeriesView> {
return commands.repositoryGetSeriesView(this.ensureHandle(), seriesId);
}
/**
* Erase watch history for an item. On a series or season the server applies
* it to everything inside, so the container returns to "never watched".