Skip to content

perf: de-parallelize upscale ancestor lookup to avoid thread-pool starvation - #264

Open
shimoncohen wants to merge 1 commit into
masterfrom
fix-deparallelize-upscale-tile-lookup
Open

perf: de-parallelize upscale ancestor lookup to avoid thread-pool starvation#264
shimoncohen wants to merge 1 commit into
masterfrom
fix-deparallelize-upscale-tile-lookup

Conversation

@shimoncohen

Copy link
Copy Markdown
Collaborator

Problem

InternalGetLastExistingTile (Data.cs) fetched all ancestor coords for upscale via an unbounded Parallel.ForEachAsync and then blocked on .Result. When invoked from inside the service-level Parallel.ForEach merge loop (see PR #260), this nests parallelism, and the sync-over-async .Result blocks an outer worker thread while inner Task.Run items compete for the same thread pool → thread-pool starvation under load. The inner GetTile calls also serialize on the source connection, so the concurrency bought little.

This addresses @asafmas-rnd's "check that Parallel doesn't call other parallel's" comment on #260 — the inner parallel is pre-existing (from #119, 2023); #260's outer parallel is what turns it into a nest.

Fix

Sequential, highest-zoom-first loop that short-circuits on the first existing tile. coordsList is already ordered high→low zoom, so the first hit is the closest ancestor — identical result, fewer GetTile calls, no nesting, no sync-over-async.

Verification

  • Build clean
  • 1118 unit tests pass
  • Behavior identical (returns closest existing ancestor)
  • Diff −28/+9, dropped now-unused System.Collections.Concurrent

🤖 Generated with Claude Code

…rvation

InternalGetLastExistingTile fetched all ancestor coords via an unbounded
Parallel.ForEachAsync and blocked on .Result. When called from inside the
service-level Parallel.ForEach merge loop this nests parallelism and, via the
sync-over-async .Result, blocks an outer worker thread while inner Task.Run
items compete for the same thread pool -> starvation under load. The inner
GetTile calls also serialize on the source connection, so the concurrency
bought little.

Replace with a sequential highest-zoom-first loop that short-circuits on the
first existing tile. Same result (closest ancestor), fewer GetTile calls, no
nesting, no sync-over-async.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant