Skip to content

perf(map): raise tile concurrency and cancel superseded vector tile loads#387

Open
NearlCrews wants to merge 2 commits into
SignalK:masterfrom
NearlCrews:perf/chart-tile-throughput
Open

perf(map): raise tile concurrency and cancel superseded vector tile loads#387
NearlCrews wants to merge 2 commits into
SignalK:masterfrom
NearlCrews:perf/chart-tile-throughput

Conversation

@NearlCrews

@NearlCrews NearlCrews commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related changes to chart tile loading throughput, kept in one PR
because they pair under the same root cause (browser HTTP/1.1 per-host
connection cap):

  1. Raise OL maxTilesLoading from the default 16 to 32 so OL keeps the
    browser connection pool fully fed during zoom-out bursts.
  2. Wrap raster and vector tile loaders in an AbortController-backed factory
    that cancels superseded zoom-level requests when the user zooms again
    before the prior level finishes.

Root cause

On a zoom-out the active raster / WMTS layer typically requests 30 to 50
new tiles. The browser caps concurrent HTTP/1.1 requests per host at six.
With OL's queue capped at 16, the browser sees the requests in waves rather
than as a steady stream, and on rapid zoom the prior level's pending
requests keep occupying the pool because OL does not cancel them.

Fix

  • map.component.ts: bump maxTilesLoading: 32 in the new Map(...)
    constructor options. Why 32: covers the typical zoom-out burst while
    leaving headroom for adjacent layers without exhausting the browser pool.
  • tile-loader-abort.ts (new): two factories. Raster factory takes
    (src, options) and returns a tileLoadFunction for XYZ, WMTS,
    TileWMS, and TileJSON. Vector factory mirrors it for VectorTile.
    Each factory closes over Map<zoom, Set<AbortController>>; on every new
    loader invocation, controllers for other zoom levels are aborted before
    the new request starts. Self-cleans via .finally().
  • Wired into the five layer components (layer-tilejson-chart,
    layer-vector-chart, layer-wms-chart, layer-wmts-chart, plus the
    vector-source path in the same file).

Caveats

  • PMTiles is out of scope: its loader delegates to a library promise API
    that does not accept an AbortSignal; adding equivalent cancellation
    requires a wrapper better landed separately.
  • The maxTilesLoading win applies only on HTTP/1.1 chart hosts. HTTP/2
    sources multiplex over one TCP connection so the per-host cap does not
    apply.

Tested

  • npx tsc --noEmit -p tsconfig.app.json clean.
  • npm run build:web clean. Bundle delta negligible.

Summary by CodeRabbit

  • New Features

    • Improved vector tile loading for map layers by stopping outdated tile requests and prioritizing the most relevant zoom level.
    • Increased map tile loading capacity to help maps render more smoothly under heavier tile demand.
  • Bug Fixes

    • Reduced unnecessary network use and loading delays when users zoom or navigate the map quickly.
    • Improved stability for vector tile rendering when multiple tile requests are in progress.

@panaaj

panaaj commented Jun 1, 2026

Copy link
Copy Markdown
Member

According to Openlayers documentation tileLoadFunction is deprecated and an ImageTile source with a loader should be used instead.

@NearlCrews

Copy link
Copy Markdown
Contributor Author

Good catch, confirmed against the OL 10.7 docs: tileLoadFunction / setTileLoadFunction is deprecated on the raster UrlTile sources, so the XYZ, WMTS, TileWMS, and TileJSON wirings here should move to an ImageTile source with a loader. One scope note: VectorTile's tileLoadFunction is not deprecated (ImageTile is raster-only), so the vector loader stays as-is.

I'll split this: land the maxTilesLoading: 32 bump on its own since it's a one-line Map option independent of the loader API, then rework the raster cancellation onto the ImageTile loader in a follow-up. The abort/supersede bookkeeping ports over cleanly. If the ImageTile rework turns out heavier than expected I'll drop the cancellation rather than ship a deprecated path. Sound right?

…oads

Raise OL maxTilesLoading from the default 16 to 32 so zoom-out tile
bursts keep the browser's HTTP/1.1 connection pool fed.

Add an abortable VectorTile tileLoadFunction that cancels in-flight
loads from superseded zoom levels, freeing the connection pool for the
now-visible level.

Raster-source cancellation is intentionally left out: OL deprecates
tileLoadFunction on raster (UrlTile) sources in favor of an ImageTile
loader, which is a larger rework for a follow-up.
@NearlCrews NearlCrews changed the title perf(map): raise tile concurrency and cancel superseded zooms perf(map): raise tile concurrency and cancel superseded vector tile loads Jun 24, 2026
@NearlCrews

Copy link
Copy Markdown
Contributor Author

Trimmed to the safe parts and rebased onto master: kept the maxTilesLoading bump and the VectorTile cancellation (VectorTile.tileLoadFunction is not deprecated), and dropped the four raster-source loaders since tileLoadFunction is deprecated there. The ImageTile-based raster rework can be a follow-up if you want it.

@NearlCrews NearlCrews force-pushed the perf/chart-tile-throughput branch from 9228386 to df0f5e1 Compare June 24, 2026 01:58
@joelkoz

joelkoz commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Thanks for trimming this down — splitting off the deprecated raster tileLoadFunction wirings and keeping just the maxTilesLoading bump plus the VectorTile cancellation (which, you're right, isn't deprecated) was exactly the right call, and the rebase onto master is clean. The root-cause writeup on the HTTP/1.1 connection cap is genuinely helpful context too.

My Claude asked your Claude for this:

# Verification — PR #387 (vector tile abort + maxTilesLoading)

Context: this PR replaces OpenLayers' built-in VectorTile load path with a
custom `tileLoadFunction` (`createAbortableVectorTileLoader` in
`src/app/modules/map/ol/lib/charts/tile-loader-abort.ts`) that cancels
in-flight tile loads from superseded zoom levels, and bumps
`maxTilesLoading` from 16 to 32 in `map.component.ts`.

The current "Tested" evidence is `tsc --noEmit` + `build:web` — compile only.
There are two gaps to close: (1) a unit test proving the abort bookkeeping,
and (2) runtime proof the vector charts still render and requests cancel.

---

## Part 1 — Add a vitest spec for the abort bookkeeping

FSK runs vitest with co-located `*.spec.ts`. Add
`src/app/modules/map/ol/lib/charts/tile-loader-abort.spec.ts` proving the
per-zoom supersede logic. The pure helpers (`bucketForZoom`,
`releaseController`) are the cleanest seam — export them from the module
(keep `createAbortableVectorTileLoader` as the public entry) so they can be
tested directly without mocking the whole OL VectorTile.

Cases to cover:
- **Same-zoom reuse:** two calls at zoom Z return the *same* controller set;
  no controllers are aborted.
- **Supersede aborts others:** with controllers held under zoom Z1, a call
  for zoom Z2 aborts every Z1 controller (assert `.signal.aborted === true`)
  and removes the Z1 bucket from the map.
- **Release cleanup:** removing the last controller for a zoom deletes that
  zoom's bucket entirely; removing one of several leaves the rest intact.
- **Release is zoom-safe:** releasing a controller under a zoom that's
  already been superseded/deleted is a no-op, not a throw.

Run `ng test` (or the vitest invocation this repo uses) and confirm green.

---

## Part 2 — Runtime verification

### Setup
1. Check out the PR branch (`perf/chart-tile-throughput`) and `ng serve`.
2. Point FSK at a Signal K server with a **vector (MVT) chart source**
   served over **HTTP/1.1** (the connection-cap condition this PR targets —
   confirm in DevTools the tile responses are h1, not h2).
3. Open FSK in Chrome → DevTools → Network, filter to the tile host,
   enable "Disable cache".

### Checks (capture an artifact for each)

A. **Vector charts still render correctly.**
   Load the vector chart; pan and zoom across a few levels. Confirm features
   draw with no missing/blank tiles and no console errors.
   → Artifact: screenshot of the rendered vector chart + clean console.

B. **Superseded requests actually cancel.**
   Zoom out across 2–3 levels rapidly (faster than tiles finish). In the
   Network panel, confirm tile requests from the abandoned zoom level(s)
   show status **(canceled)**.
   → Artifact: Network-waterfall screenshot showing (canceled) rows for the
     superseded zoom, plus the in-flight count for the visible level.

C. **No regression in the steady state.**
   Zoom to a level and let it fully settle. Confirm every tile reaches
   LOADED and nothing is stuck in (canceled) or error.
   → Artifact: waterfall of a clean settle (all 200/LOADED).

D. **maxTilesLoading effect (optional).**
   On the same HTTP/1.1 source, compare time-to-visually-settle after a
   zoom-out burst on this branch vs. master, same hardware.
   → Artifact: before/after settle time (or a short screen capture).

### Edge cases to spot-check
- Cross-origin source: does plain `fetch(src)` still load (CORS /
  credentials) the same as OL's default loader did? No CORS errors for a
  remote chart host.
- Rapid pinch-zoom through intermediate levels: the final level renders and
  you don't end up with a level whose tiles were all aborted and never
  re-requested.

---

## Output
- Confirm the new spec passes (`ng test`).
- Post the runtime artifacts on PR #387, one line each on what they show.
- If any check fails, note which and the console/network detail.

@joelkoz joelkoz self-assigned this Jun 26, 2026
Add a vitest spec for the per-zoom AbortController bookkeeping behind the
superseded-tile cancellation: same-zoom bucket reuse, supersede aborting and
dropping stale-zoom controllers, and release cleanup including the zoom-safe
no-op paths. Export bucketForZoom, releaseController, and the PendingByZoom
type as the test seam, with no runtime behavior change.
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds tile-loader-abort.ts, which implements per-zoom AbortController bucket management (PendingByZoom, bucketForZoom, releaseController) and a factory createAbortableVectorTileLoader that cancels in-flight fetches for superseded zoom levels. The loader is wired into VectorChartLayerComponent as tileLoadFunction, and MapComponent sets maxTilesLoading: 32.

Changes

Abortable Vector Tile Loading

Layer / File(s) Summary
PendingByZoom state and loader factory
src/app/modules/map/ol/lib/charts/tile-loader-abort.ts, src/app/modules/map/ol/lib/charts/tile-loader-abort.spec.ts
Defines PendingByZoom, bucketForZoom (aborts controllers for other zooms, returns/creates the current zoom's bucket), releaseController (removes controller and cleans empty buckets), and createAbortableVectorTileLoader (fetch-based OL tile load callback with LOADINGLOADED/ERROR state transitions). Unit tests cover all three exported functions including edge cases.
Layer and Map wiring
src/app/modules/map/ol/lib/charts/layer-vector-chart.component.ts, src/app/modules/map/ol/lib/map.component.ts
VectorChartLayerComponent imports and passes createAbortableVectorTileLoader() as tileLoadFunction on the VectorTileSource. MapComponent.ngOnInit() adds maxTilesLoading: 32 to the OL Map constructor options.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 When zooms zoom by and tiles stack up,
Old fetches abort — no need to sup!
A bucket of controllers, neat and bright,
The map loads fast at the right zoom height.
Hop hop, no waste, the signals fly free! 🗺️

🚥 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 accurately summarizes the main changes: higher tile concurrency and aborting superseded vector tile loads.
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.

@NearlCrews

Copy link
Copy Markdown
Contributor Author

Both parts are in.

Part 1: unit spec. Added tile-loader-abort.spec.ts (vitest). bucketForZoom and releaseController were already the right seam, so I exported them plus the PendingByZoom type, with no runtime change. Nine cases:

  • same zoom returns the same bucket, and a same-zoom repeat aborts nothing
  • a new zoom aborts every controller in the superseded buckets (signal.aborted === true) and deletes those buckets, including several stale zooms in one call
  • release deletes a bucket on its last controller, keeps it when others remain, and is a no-op for an already-removed zoom or a stranger controller

Green at 9 passed. (app.component.spec.ts fails independently on the src/app/modules path alias in the vitest config, unrelated to this branch, confirmed by stashing.)

Part 2: runtime. I drove the actual createAbortableVectorTileLoader, wired exactly as layer-vector-chart.component wires it (as the VectorTileSource tileLoadFunction), in headless Chromium against real OpenFreeMap MVT tiles proxied through a plain Node server, so the browser fetches them over HTTP/1.1 with 350 ms of added latency. That is the connection-cap condition this PR targets. Then a rapid zoom out from z12 to z6, faster than the latency, to force in-flight supersession.

Results (request log, 63 tile requests):

  • Renders: the vector chart draws correctly at z12 (central London) with zero console errors.
  • Superseded loads cancel: 15 in-flight tile loads at the superseded zooms (6 at z9, 9 at z10) ended in net::ERR_ABORTED, with 0 non-abort failures.
  • Clean settle: at the final z6 all 12 visible tiles reached LOADED with 0 errors, so no level is left all-aborted-and-never-re-requested.

This is a synthetic harness exercising the real loader code path, not FSK driven by hand against a Signal K vector source. If you would rather see it against a live SK chart source, or want the render and waterfall screenshots and the harness script, say the word and I will post them.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/app/modules/map/ol/lib/charts/tile-loader-abort.spec.ts (1)

12-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add specs for createAbortableVectorTileLoader().

These tests only cover the helper map/set bookkeeping. The new behavior that actually ships here—fetch cancellation, AbortError handling, tile state transitions, and cleanup after settle—is still untested, so the main regression surface from this PR would slip through.

🤖 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 `@src/app/modules/map/ol/lib/charts/tile-loader-abort.spec.ts` around lines 12
- 120, The current spec only covers bucket bookkeeping in bucketForZoom and
releaseController, but not the actual loader behavior introduced by
createAbortableVectorTileLoader(). Add tests around
createAbortableVectorTileLoader that verify an in-flight fetch is canceled via
AbortController, AbortError is handled without failing the tile, tile state
transitions happen correctly on success/failure, and cleanup occurs after the
request settles. Use the existing helper patterns in the spec to locate and
exercise the loader entry points directly.
🤖 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 `@src/app/modules/map/ol/lib/charts/tile-loader-abort.ts`:
- Around line 52-58: Expose a teardown hook from createAbortableVectorTileLoader
so callers can abort all pending controllers held in the pending map. Update the
loader factory and the createAbortableVectorTileLoader return type to include a
cleanup/teardown function, and use it from
VectorChartLayerComponent.ngOnDestroy() to cancel any still-running tile fetches
before the layer is removed.
- Around line 80-86: The abort handling in tile-loader-abort should put
superseded tiles into a terminal state instead of leaving them stuck in LOADING.
Update the abort path around the tile load promise and the tile state logic in
tile-loader-abort so AbortError is treated as a terminal outcome and the tile is
set to ERROR via tile.setState, while keeping EMPTY reserved for intentionally
blank/disposed tiles. Use the existing tile.setState, TileState.ERROR, and abort
catch/finally flow to locate the fix.

---

Nitpick comments:
In `@src/app/modules/map/ol/lib/charts/tile-loader-abort.spec.ts`:
- Around line 12-120: The current spec only covers bucket bookkeeping in
bucketForZoom and releaseController, but not the actual loader behavior
introduced by createAbortableVectorTileLoader(). Add tests around
createAbortableVectorTileLoader that verify an in-flight fetch is canceled via
AbortController, AbortError is handled without failing the tile, tile state
transitions happen correctly on success/failure, and cleanup occurs after the
request settles. Use the existing helper patterns in the spec to locate and
exercise the loader entry points directly.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7783283-a22a-400f-9956-6672af3a1421

📥 Commits

Reviewing files that changed from the base of the PR and between 8f8875d and 4a814f8.

📒 Files selected for processing (4)
  • src/app/modules/map/ol/lib/charts/layer-vector-chart.component.ts
  • src/app/modules/map/ol/lib/charts/tile-loader-abort.spec.ts
  • src/app/modules/map/ol/lib/charts/tile-loader-abort.ts
  • src/app/modules/map/ol/lib/map.component.ts

Comment on lines +52 to +58
export function createAbortableVectorTileLoader(): (
tile: VectorTile<FeatureLike>,
src: string
) => void {
const pending: PendingByZoom = new Map();

return function loader(tile: VectorTile<FeatureLike>, src: string) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Expose a teardown hook for pending controllers.

pending is trapped inside this factory, so VectorChartLayerComponent.ngOnDestroy() has no way to abort still-running tile fetches when the layer is removed. Those requests keep consuming the shared tile budget until the network settles.

🤖 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 `@src/app/modules/map/ol/lib/charts/tile-loader-abort.ts` around lines 52 - 58,
Expose a teardown hook from createAbortableVectorTileLoader so callers can abort
all pending controllers held in the pending map. Update the loader factory and
the createAbortableVectorTileLoader return type to include a cleanup/teardown
function, and use it from VectorChartLayerComponent.ngOnDestroy() to cancel any
still-running tile fetches before the layer is removed.

Comment on lines +80 to +86
.catch((err) => {
if (err?.name !== 'AbortError') {
tile.setState(TileState.ERROR);
}
})
.finally(() => {
releaseController(pending, z, controller);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for OpenLayers TileState docs/usages in repo
rg -n "TileState|abort.*tile|AbortError|setState\\(TileState\\.(EMPTY|ERROR|LOADING|LOADED)\\)" . -S --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**'

# Inspect package versions if present
if [ -f package.json ]; then
  nl -ba package.json | sed -n '1,220p'
fi
if [ -f pnpm-lock.yaml ]; then
  rg -n "^  ol@|/ol/|name: ol|version:" pnpm-lock.yaml -S | sed -n '1,120p'
fi
if [ -f package-lock.json ]; then
  rg -n '"ol"|TileState' package-lock.json -S | sed -n '1,120p'
fi
if [ -f yarn.lock ]; then
  rg -n '(^ol@|/ol@|TileState)' yarn.lock -S | sed -n '1,120p'
fi

Repository: SignalK/freeboard-sk

Length of output: 714


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the target file region with line numbers using sed (no nl dependency)
sed -n '1,180p' src/app/modules/map/ol/lib/charts/tile-loader-abort.ts

# Inspect package metadata for the OpenLayers version if available
if [ -f package.json ]; then
  sed -n '1,240p' package.json
fi

Repository: SignalK/freeboard-sk

Length of output: 6452


🌐 Web query:

OpenLayers 10.7 TileState abort EMPTY ERROR loading tile image loader semantics

💡 Result:

In OpenLayers, TileState is an enumeration used to track the lifecycle of a tile. The states relevant to your query are defined in the ol/TileState module [1]: IDLE (0): Initial state [1]. LOADING (1): The tile is currently being fetched [1]. LOADED (2): The tile data has been successfully loaded [1]. ERROR (3): The tile loading process failed [1]. EMPTY (4): The tile is empty or has been disposed of [1][2]. Regarding tile loader semantics and state management: Tile Loading and Errors: When implementing a custom tileLoadFunction, it is critical to explicitly call tile.setState(TileState.ERROR) if the tile fails to load [3][4]. If this is not done, the tile will remain in a loading state, blocking the tile queue and preventing other requests from being processed [3][4]. Handling EMPTY State: A tile is assigned the TileState.EMPTY state when it is disposed of or is intentionally empty [2]. Recent improvements to OpenLayers ensure that an EMPTY tile remains in that state [2]. The internal setState method now returns early if a tile is already marked as EMPTY, preventing late-arriving load or error callbacks from incorrectly updating the state of a disposed tile [2]. Future Directions: OpenLayers is shifting away from the legacy UrlTile API (which uses tileLoadFunction) toward the DataTile and ImageTile sources using a loader function [5]. When using these newer loaders, if a tile request fails, you have the option to resolve the loader with empty data (e.g., an empty image) to treat the failure as an empty tile rather than an error, providing more granular control over how missing data is handled [5].

Citations:


🌐 Web query:

site:openlayers.org ol TileState EMPTY disposed intentionally empty abort loading setState ERROR

💡 Result:

In OpenLayers, TileState is an enumeration used to track the lifecycle status of a tile [1][2]. The states are: IDLE (0): The tile is initialized but not yet loading [1][2]. LOADING (1): The tile is currently being fetched [1][2]. LOADED (2): The tile has been successfully loaded [1][2]. ERROR (3): The tile failed to load [1][2]. EMPTY (4): The tile is intentionally empty (e.g., no data exists for this tile coordinate) [1][2]. When implementing a custom tile load function, you are responsible for updating the state of the tile [3][4]. It is critical to set the state to ERROR if the loading process fails, as failing to do so will leave the tile in a pending state, potentially causing the tile queue to block subsequent requests [3][4]. The EMPTY state is typically used when a tile is intentionally left blank because no data is available for that specific location or zoom level, distinguishing it from an error state where a retrieval attempt failed. Disposed tiles or those whose loading has been aborted are handled by the library's internal management, often triggering a change in the tile's state or removal from the active tile queue [5].

Citations:


Set aborted tiles to ERROR, not leave them LOADING. AbortError currently skips every terminal tile.setState(...) path, so superseded tiles can remain active in OL’s queue. EMPTY is for intentionally blank/disposed tiles.

🤖 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 `@src/app/modules/map/ol/lib/charts/tile-loader-abort.ts` around lines 80 - 86,
The abort handling in tile-loader-abort should put superseded tiles into a
terminal state instead of leaving them stuck in LOADING. Update the abort path
around the tile load promise and the tile state logic in tile-loader-abort so
AbortError is treated as a terminal outcome and the tile is set to ERROR via
tile.setState, while keeping EMPTY reserved for intentionally blank/disposed
tiles. Use the existing tile.setState, TileState.ERROR, and abort catch/finally
flow to locate the fix.

@joelkoz

joelkoz commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

This is exactly the evidence I was after — thank you for the thorough turnaround. The vitest cases nail the supersede bookkeeping, and the headless run over a real HTTP/1.1 + latency path is a clean way to prove the two things I couldn't take on faith from a green build: vector charts still render, and superseded loads actually cancel (the 15 ERR_ABORTED at z9/z10 with a clean z6 settle is precisely the behavior the PR claims). No need for the live-SK rerun on my account — the synthetic harness exercises the real loader code path, which is what matters. Nice catch confirming the app.component.spec.ts failure is a pre-existing path-alias issue, not your branch.

CodeRabbit has since flagged two things I'd treat as substantive rather than style, and I'd like both addressed before merge:

  1. Aborted tiles aren't moved to a terminal state (tile-loader-abort.ts ~80-86). The .catch only sets ERROR for non-AbortError, so a superseded tile is left sitting in LOADING. Worth confirming OL re-requests those on the next render rather than stranding them — this is the one I'd most want resolved.
  2. No teardown on unmount (~52-58). Pending controllers aren't aborted when the layer is destroyed; exposing a cleanup that VectorChartLayerComponent.ngOnDestroy() calls closes that.

Its third note (test createAbortableVectorTileLoader() itself, not just the helpers) is a nitpick — your Part-2 harness already covers that behaviorally, so promoting a slice of it into a committed test would more than satisfy it if you feel like it.

Since you're on Claude Code: CodeRabbit drops a "🤖 Prompt for AI Agents" block under each finding, plus a combined "Prompt for all review comments with AI agents" at the end of its review — those are written to paste straight into your agent, so you don't have to translate the comments by hand. Get those two items sorted and I think this is ready to land.

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.

3 participants