Skip to content

docs: spec for coalescing tile fetches through deck.gl getTileData#556

Draft
kylebarron wants to merge 6 commits into
mainfrom
kyle/getTileData-coalesce
Draft

docs: spec for coalescing tile fetches through deck.gl getTileData#556
kylebarron wants to merge 6 commits into
mainfrom
kyle/getTileData-coalesce

Conversation

@kylebarron
Copy link
Copy Markdown
Member

Design for #273: a TileBatcher in deck.gl-raster that coalesces deck.gl's per-tile getTileData calls into one getMultiTileData (-> geotiff.fetchTiles) call per zoom level, plus a chunkd request-scheduler middleware in geotiff so maxRequests bounds actual concurrent HTTP requests. No public fetchTiles API change; no loaders.gl dependency in geotiff.

Design for #273: a TileBatcher in deck.gl-raster that coalesces deck.gl's
per-tile getTileData calls into one getMultiTileData (-> geotiff.fetchTiles)
call per zoom level, plus a chunkd request-scheduler middleware in geotiff so
maxRequests bounds actual concurrent HTTP requests. No public fetchTiles API
change; no loaders.gl dependency in geotiff.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the docs label May 18, 2026
kylebarron and others added 3 commits May 18, 2026 15:32
A minimal slot-acquisition contract — `acquire(signal?): Promise<() => void>`
where an abort while queued drops the request without firing it — plus:

- `limitFetch(fetch, limiter)`: wraps a Source.fetch to hold a slot for its
  duration, forwarding the call's signal to acquire().
- `Semaphore`: a FIFO concrete impl.
- `defaultLimiterForOrigin(url)`: returns a per-origin shared Semaphore
  (`maxRequests = 6`) cached across calls, so multiple sources targeting
  the same host (multiple GeoTIFF.fromUrl, plus future Zarr etc.) share
  one cap — the relevant constraint is the browser's HTTP/1.1 per-origin
  connection limit, which is global to the page, not per-layer.

No loaders.gl / @chunkd dependency; geotiff stays self-contained.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When supplied, the data source's fetch is wrapped via limitFetch so every
tile-data and mask HTTP request is gated by the limiter. Header/metadata
reads are not gated — small, mostly at open time, served from the header
cache.

GeoTIFF.fromUrl defaults the limiter to defaultLimiterForOrigin(url) — so
two `fromUrl` calls (or any other source-level caller) targeting the same
origin automatically share one cap. Pass an explicit limiter to override;
pass `null` to disable gating.

fetchTile / fetchTiles / coalesceRanges are untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The cap that matters is the browser's HTTP/1.1 concurrent-connections-
per-origin limit (~6 on Chrome), which is *global to the page* — not
per-layer. The earlier design had RasterTileLayer create a layer-scoped
RequestScheduler, which can't enforce that cap across multiple layers
sharing an origin (two COGLayers on one S3 bucket would each get 6
slots → 12 in flight → browser queues, and stale-after-pan requests
block fresh ones).

Pivot:

- Decouple concurrency limiting from multi-tile coalescing. They were
  always two concerns; the spec now treats them as independent pieces
  that can ship and be reasoned about separately.
- Move the limiter to the source layer (geotiff). GeoTIFF.fromUrl
  defaults its concurrencyLimiter to defaultLimiterForOrigin(url), a
  shared per-origin Semaphore (max=6). Two `fromUrl` calls (or any other
  source-level caller, future Zarr included) targeting the same origin
  automatically share one cap, zero setup.
- Drop the chunkd SourceMiddleware framing; gate just the data source's
  .fetch via limitFetch. No SourceView, no chunkd-vs-cogeotiff Source-
  type cast.
- Drop the loaders.gl RequestScheduler adapter from deck.gl-raster. The
  layer no longer owns or threads a limiter.
- Add `signal` to ConcurrencyLimiter.acquire so queued aborts drop
  rather than fire stale requests after a pan.

The TileBatcher work (the deck.gl-raster half) is unchanged in spirit
but no longer creates a limiter; it just runs the batcher and sets the
inner TileLayer's maxRequests: 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kylebarron kylebarron force-pushed the kyle/getTileData-coalesce branch from 0477ae3 to 6e6b133 Compare May 18, 2026 19:35
kylebarron and others added 2 commits May 18, 2026 15:38
Like fetchTiles, but a single missing tile or a single tile's decode
failure becomes a { error } entry in its slot rather than throwing. A
network-level failure inside a coalesced range still rejects the whole
call (those bytes are gone for every tile in the merged range). Composes
the existing coalesced byte-fetch + per-tile assembleTile pieces; adds a
collect-not-throw variant of fetchCogBytesMultiple. New method on
GeoTIFF + Overview. SettledTile type re-exported from the package.

For the deck.gl batched-fetch path so one bad tile can't blank a viewport.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Buffers per-item fetch() calls and dispatches one batched call per
group key on a setTimeout(_, 0); per-item Error distribution; composite
"abort-when-all" signal; finalize() rejects what's buffered. Pure
coalescer — no limiter ownership, no scheduler — that's the source
layer's concern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant