mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
perf(services): share the startup inventory read (J1) (#1716)
* perf(workspace): share the startup inventory read and defer non-first-card IPC (J1) Journey J1 / renderer.ipcCallsToFirstCard: 12 -> 7. - PlaylistsService.getAllPlaylists() shares one in-flight SQLite read between concurrent callers (the playlist effect and the XMLTV source reconciliation at startup). Settled reads are never reused and every SQLite write detaches the pending read. - getM3uFavoriteChannels() stops re-reading the write-once IndexedDB -> SQLite migration receipt once it has been seen. - StartupDeferralService holds the download list, app update status and the dashboard's recent items and favorites until one task after the render that reveals the routed content (5 s safety timeout). IPTVNATOR_DISABLE_STARTUP_DEFERRAL=1 is the kill switch. - reconcileEpgSources and the two distinct migration-flag reads stay on the critical path: the first is the #1548 revision fence, the second are different keys, not duplicates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(services): trust a migration receipt written in this session; correct J1 note - Mark the IndexedDB -> SQLite receipt as confirmed after an empty-store receipt write or a committed dbMigrateAppPlaylists, not only when it was already present, so M3U favorites skip the per-playlist re-read on the first launch after an upgrade too. - The release note no longer claims a wall-clock speedup the J1 benchmark did not show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(services): keep only the shared startup inventory read (J1) Drop the startup deferral gate, its kill switch and the deferred loads: they lowered renderer.ipcCallsToFirstCard but moved neither spawnToFirstCardMs nor load->card beyond drift, and they grew renderer.initialBytes. Keep the shared in-flight inventory read, inline it in PlaylistsService with short property names, and copy the joiner's result with structuredClone (the Electron renderer has it; the services test setup polyfills it for jsdom). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(performance): describe the shared inventory read and the J1 validation result Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(services): drop the migration receipt memo for M3U favorites Skipping the receipt read let the dashboard ask the database for M3U favorite channels before a just-toggled favorite was written: the write waits in the per-playlist queue and the cross-context lock, and the dashboard does not reload when the store's favorites are unchanged. The extra round trip had been masking that race, and the Windows E2E run hit it (epg.e2e.ts "dashboard live rails find a programme that only another playlist's XMLTV carries"). The memo was off the first card's path, so it bought nothing measurable for J1; restore the per-call read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(services): share only the startup inventory read A pending read was shared with any concurrent caller, but not every playlist write goes through PlaylistsService: the settings reset deletes all playlists through DatabaseService, so a caller could join a read taken before that deletion (Codex review). Share only the first read, which the startup pair (playlist effect and XMLTV reconciliation) needs while the startup screen still hides every writing action; sharing ends when it settles or a PlaylistsService write starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
5 files changed
+198
-10
No files matched your search
@@ -179,6 +179,32 @@ harness, which is what the ratchet needs. The main process start
|
||||
(`Date.now() - process.uptime()`) is recorded per iteration under
|
||||
`evidence.epochs` for cross-checks.
|
||||
|
||||
### Startup work before the first card
|
||||
|
||||
`renderer.ipcCallsToFirstCard` counts what the renderer asks of the main
|
||||
process before the first card.
|
||||
|
||||
- `PlaylistsService.getAllPlaylists()` shares its first SQLite read: at
|
||||
startup the playlist effect and the XMLTV source reconciliation both read
|
||||
the inventory, and the second caller joins the first read and receives a
|
||||
`structuredClone` of its result. Sharing ends when that read settles or a
|
||||
`PlaylistsService` write starts. It is limited to startup on purpose:
|
||||
other services write playlists too (the settings reset deletes them
|
||||
through `DatabaseService`), and while the startup screen is up no such
|
||||
action can run. `dbGetAppPlaylistMetas` before the first card: 2 → 1.
|
||||
- `reconcileEpgSources` stays before the first card on purpose: its
|
||||
completion bumps `EpgSourceSettingsService.revision()`, the fence that
|
||||
keeps XMLTV lookups from returning data of a removed source.
|
||||
|
||||
Validation (#1716, Principle 3): deferring the download list, update status
|
||||
and dashboard recent/favorites reads until after the first render took the
|
||||
counter from 12 to 7 on a Mac, but moved neither `spawnToFirstCardMs` nor
|
||||
load→card beyond run-to-run drift on a quiet machine, and it grew
|
||||
`renderer.initialBytes`, so it was dropped. Those calls were never on the
|
||||
path the first card waits for. That path is a serial chain of round trips
|
||||
(the migration reads, the inventory read and `reconcileEpgSources`), so a
|
||||
serial-depth counter is a better guardrail candidate than a raw call count.
|
||||
|
||||
### Summary schema
|
||||
|
||||
```json
|
||||
|
||||
Reference in new issue
Block a user