[Prestaties][P1] Renderpad doet per afbeelding per build twee synchrone realpath-aanroepen — zwaar op Windows #2624

Closed
opened 2026-10-28 16:31:37 +00:00 by brenno · 0 comments
Owner

Symptoom

Decks met veel afbeeldingen voelen bij typen en scrollen trager aan, en vooral op Windows (Defender, OneDrive-mappen, netwerkschijven).

Oorzaak

De symlink-containmentcheck op het renderpad, isRenderPathContained (lib/utils/project_path.dart:62), doet per aanroep twee synchrone resolveSymbolicLinksSync (regel 46-50). Alleen geweigerde paden worden gecachet (regel 57: "Only blocked paths are cached. A positive result must be revalidated"). Elke toegestane afbeelding betaalt dus bij elke build opnieuw twee synchrone bestandssysteemaanroepen op de UI-isolate.

Het commentaar bij de aanroeper zegt het tegenovergestelde: "cached, so the per-frame cost is O(1) after the first render of each image" (lib/widgets/slides/previews/media_previews_image.dart:217). En de kop van hetzelfde bestand waarschuwt er zelf voor: "a stat per image per frame would jank on network-mounted projects".

Aanroepers: media_previews_image.dart:221, preview_scaffold.dart:20 (achtergronden), lib/widgets/reader/parts/document_markdown_image.dart:70 (documenten). Per toetsaanslag: zichtbare miniaturen × afbeeldingen × 2, plus preview, presentator (huidige + volgende) en beamervenster.

Op Windows is resolveSymbolicLinksSync een CreateFileW + GetFinalPathNameByHandleW + CloseHandle — elke open passeert de filterdrivers van de virusscanner en, in een OneDrive-map, de cloud-filter.

Meting

19,4 µs per aanroep op macOS (APFS, lokaal). Op Windows is dit niet gemeten; verwacht een veelvoud, en op OneDrive/SMB milliseconden. Meet dit op een Windows 10-machine voordat de prioriteit vastligt (de diagnose uit #2618 helpt daarbij).

Voorgestelde oplossing

Verplaats de check van de build naar het laden: doe hem async in de loader van CappedImage (cappedFileImage, lib/utils/image_limits.dart:195), direct vóór het lezen van de bytes. Dat is één keer per decodering in plaats van per build, het is het moment dat het dichtst op het werkelijke lezen zit (dus de TOCTOU-garantie van #1692 wordt eerder sterker dan zwakker), en de UI-isolate blokkeert niet. Een geweigerd pad levert een laadfout op die de bestaande placeholder (outsideDeck) toont.

Alternatief als dat niet kan: positieven cachen met sleutel (pad, mtime, grootte) en hervalideren bij het laden.

Wat overeind moet blijven

Een symlink binnen het project die naar buiten wijst, wordt nooit getoond, gepresenteerd of geëxporteerd; de tests rond #1692 blijven groen.

Toets

Unit: tien builds van dezelfde dia met één afbeelding → nul resolveSymbolicLinksSync-aanroepen in build (via een injecteerbare resolver); symlink-ontsnapping levert nog steeds de outsideDeck-placeholder.

Onderdeel van de prestatie- en stabiliteitsinspectie van 28-10-2026 (EPIC #2637).

## Symptoom Decks met veel afbeeldingen voelen bij typen en scrollen trager aan, en vooral op Windows (Defender, OneDrive-mappen, netwerkschijven). ## Oorzaak De symlink-containmentcheck op het **renderpad**, `isRenderPathContained` (`lib/utils/project_path.dart:62`), doet per aanroep twee synchrone `resolveSymbolicLinksSync` (regel 46-50). Alleen *geweigerde* paden worden gecachet (regel 57: "Only blocked paths are cached. A positive result must be revalidated"). Elke toegestane afbeelding betaalt dus bij **elke build** opnieuw twee synchrone bestandssysteemaanroepen op de UI-isolate. Het commentaar bij de aanroeper zegt het tegenovergestelde: "cached, so the per-frame cost is O(1) after the first render of each image" (`lib/widgets/slides/previews/media_previews_image.dart:217`). En de kop van hetzelfde bestand waarschuwt er zelf voor: "a stat per image per frame would jank on network-mounted projects". Aanroepers: `media_previews_image.dart:221`, `preview_scaffold.dart:20` (achtergronden), `lib/widgets/reader/parts/document_markdown_image.dart:70` (documenten). Per toetsaanslag: zichtbare miniaturen × afbeeldingen × 2, plus preview, presentator (huidige + volgende) en beamervenster. Op Windows is `resolveSymbolicLinksSync` een `CreateFileW` + `GetFinalPathNameByHandleW` + `CloseHandle` — elke open passeert de filterdrivers van de virusscanner en, in een OneDrive-map, de cloud-filter. ## Meting 19,4 µs per aanroep op macOS (APFS, lokaal). Op Windows is dit niet gemeten; verwacht een veelvoud, en op OneDrive/SMB milliseconden. **Meet dit op een Windows 10-machine** voordat de prioriteit vastligt (de diagnose uit #2618 helpt daarbij). ## Voorgestelde oplossing Verplaats de check van de build naar het **laden**: doe hem async in de loader van `CappedImage` (`cappedFileImage`, `lib/utils/image_limits.dart:195`), direct vóór het lezen van de bytes. Dat is één keer per decodering in plaats van per build, het is het moment dat het dichtst op het werkelijke lezen zit (dus de TOCTOU-garantie van #1692 wordt eerder sterker dan zwakker), en de UI-isolate blokkeert niet. Een geweigerd pad levert een laadfout op die de bestaande placeholder (`outsideDeck`) toont. Alternatief als dat niet kan: positieven cachen met sleutel (pad, mtime, grootte) en hervalideren bij het laden. ## Wat overeind moet blijven Een symlink binnen het project die naar buiten wijst, wordt nooit getoond, gepresenteerd of geëxporteerd; de tests rond #1692 blijven groen. ## Toets Unit: tien builds van dezelfde dia met één afbeelding → nul `resolveSymbolicLinksSync`-aanroepen in build (via een injecteerbare resolver); symlink-ontsnapping levert nog steeds de `outsideDeck`-placeholder. Onderdeel van de prestatie- en stabiliteitsinspectie van 28-10-2026 (EPIC #2637).
brenno 2026-10-29 12:19:42 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#2624
No description provided.