Repository navigation
perf(resolver): allocation-free resolver selection in ParallelBestResolver - #2150
Merged
Merged
Conversation
…lelBestResolver The default upstream strategy rebuilt a weightedrand.Chooser on every cache-miss request — twice, via pickRandom. Each build allocated a choices slice, sorted it, and allocated a totals slice, for ~10 allocs (328 B) per request in the common 2-upstream case. Replace the Chooser with single-pass weighted reservoir sampling (k=1): each resolver is kept with probability weight/runningTotal, leaving every resolver selected proportional to its weight once the loop completes. Same weighting (recent errors reduce a resolver's weight, recovering over an hour), same distribution, but zero allocations and one O(n) pass. Also skip context.WithCancel for the random strategy, which queries a single resolver per attempt and so has no losing race to abort. This drops the github.com/mroth/weightedrand/v2 dependency. benchstat (n=6), selection cost per request: pickRandom/upstreams=2 717ns 328B 10 allocs -> 205ns 0B 0 allocs weightedRandom/upstreams=2 451ns 200B 6 allocs -> 125ns 0B 0 allocs pickRandom/upstreams=5 1336ns 544B 12 allocs -> 585ns 0B 0 allocs weightedRandom/upstreams=5 755ns 280B 6 allocs -> 370ns 0B 0 allocs
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2150 +/- ##
==========================================
+ Coverage 87.86% 87.90% +0.04%
==========================================
Files 124 124
Lines 9813 9816 +3
==========================================
+ Hits 8622 8629 +7
+ Misses 926 924 -2
+ Partials 265 263 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
0xERR0R
enabled auto-merge (squash)
June 30, 2026 07:47
dadezzz
pushed a commit
to dadezzz/infra_docker-compose
that referenced
this pull request
Jul 2, 2026
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/0xerr0r/blocky](https://github.com/0xERR0R/blocky) | minor | `v0.32.1` → `v0.33.0` | --- ### Release Notes <details> <summary>0xERR0R/blocky (ghcr.io/0xerr0r/blocky)</summary> ### [`v0.33.0`](https://github.com/0xERR0R/blocky/releases/tag/v0.33.0) [Compare Source](0xERR0R/blocky@v0.32.1...v0.33.0) #### Changelog ##### Features - [`552eda5`](0xERR0R/blocky@552eda5): feat(cli): add 'blocky stats' command ([#​2138](0xERR0R/blocky#2138)) ([@​0xERR0R](https://github.com/0xERR0R)) - [`e9deb53`](0xERR0R/blocky@e9deb53): feat(queryLog): add dnstap as a query log destination ([#​2144](0xERR0R/blocky#2144)) ([@​rtmongold](https://github.com/rtmongold)) ##### Bug fixes - [`a191ad2`](0xERR0R/blocky@a191ad2): fix(dnssec): propagate Indeterminate (not Bogus) for unreachable chain of trust ([#​2120](0xERR0R/blocky#2120)) ([#​2139](0xERR0R/blocky#2139)) ([@​0xERR0R](https://github.com/0xERR0R)) - [`4b524e8`](0xERR0R/blocky@4b524e8): fix(ecs): apply useAsClient above cache and client-name lookup ([#​2140](0xERR0R/blocky#2140)) ([#​2141](0xERR0R/blocky#2141)) ([@​0xERR0R](https://github.com/0xERR0R)) ##### Build and dependencies - [`afea452`](0xERR0R/blocky@afea452): build(deps): bump github.com/breml/rootcerts from 0.3.5 to 0.3.6 ([#​2133](0xERR0R/blocky#2133)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`247a3c1`](0xERR0R/blocky@247a3c1): build(deps): bump github.com/jedib0t/go-pretty/v6 from 6.6.7 to 6.8.1 ([#​2145](0xERR0R/blocky#2145)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`2aa728d`](0xERR0R/blocky@2aa728d): build(deps): bump github.com/oapi-codegen/runtime from 1.4.1 to 1.4.2 ([#​2134](0xERR0R/blocky#2134)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`3f3f7d9`](0xERR0R/blocky@3f3f7d9): build(deps): bump github.com/onsi/ginkgo/v2 from 2.31.0 to 2.32.0 ([#​2135](0xERR0R/blocky#2135)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`2d0e6f2`](0xERR0R/blocky@2d0e6f2): build(deps): bump github.com/onsi/gomega from 1.42.0 to 1.42.1 ([#​2137](0xERR0R/blocky#2137)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`61d9a98`](0xERR0R/blocky@61d9a98): build(deps): bump github.com/testcontainers/testcontainers-go/modules/mariadb from 0.42.0 to 0.43.0 ([#​2131](0xERR0R/blocky#2131)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`cf865a6`](0xERR0R/blocky@cf865a6): build(deps): bump github.com/testcontainers/testcontainers-go/modules/postgres from 0.42.0 to 0.43.0 ([#​2130](0xERR0R/blocky#2130)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`e3c30dc`](0xERR0R/blocky@e3c30dc): build(deps): bump github.com/testcontainers/testcontainers-go/modules/redis from 0.42.0 to 0.43.0 ([#​2129](0xERR0R/blocky#2129)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`a58f1c8`](0xERR0R/blocky@a58f1c8): build(deps): bump google.golang.org/protobuf from 1.36.10 to 1.36.11 ([#​2148](0xERR0R/blocky#2148)) ([@​dependabot](https://github.com/dependabot)\[bot]) - [`6df83cd`](0xERR0R/blocky@6df83cd): build(deps): bump gorm.io/gorm from 1.31.1 to 1.31.2 ([#​2147](0xERR0R/blocky#2147)) ([@​dependabot](https://github.com/dependabot)\[bot]) ##### Misc - [`6da7ce1`](0xERR0R/blocky@6da7ce1): perf(blocking): Tier 2 — lock-free grouped cache, cheapest-first lookup, client-name indexing ([#​2146](0xERR0R/blocky#2146)) ([@​0xERR0R](https://github.com/0xERR0R)) - [`302ca65`](0xERR0R/blocky@302ca65): perf(log): cut per-request logger allocations on the resolver hot path ([#​2142](0xERR0R/blocky#2142)) ([@​0xERR0R](https://github.com/0xERR0R)) - [`223df0c`](0xERR0R/blocky@223df0c): perf(queryLog): skip per-request LogEntry build when off or ignored ([#​2149](0xERR0R/blocky#2149)) ([@​0xERR0R](https://github.com/0xERR0R)) - [`b73422e`](0xERR0R/blocky@b73422e): perf(resolver): allocation-free resolver selection in ParallelBestResolver ([#​2150](0xERR0R/blocky#2150)) ([@​0xERR0R](https://github.com/0xERR0R)) *** #### ❤️ Support Blocky Blocky is free and open source, developed in my spare time — no telemetry, no ads, no hidden filtering. If it's useful to you, please consider supporting its continued development: [GitHub Sponsors](https://github.com/sponsors/0xERR0R) · [thanks.dev](https://thanks.dev/u/gh/0xERR0R) · [Liberapay](https://liberapay.com/spx01) · [Ko-fi](https://ko-fi.com/0xerr0r) · [PayPal](https://paypal.me/spx01) Thank you to everyone supporting Blocky! 🙏 </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDYuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI0Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
ParallelBestResolveris the default upstream strategy. On every cache-miss request it rebuilt aweightedrand.Chooser— and viapickRandom, it did so twice per request. Each build allocated achoicesslice, sorted it, and allocated atotalsslice: ~10 allocations / 328 B per request in the common 2-upstream case.This replaces the
Chooserwith single-pass weighted reservoir sampling (k=1): each resolver is kept with probabilityweight / runningTotal, which leaves every resolver selected with probability proportional to its weight once the loop completes. Same weighting semantics (a resolver that errored recently gets a reduced weight, recovering to full weight an hour after its last error), same statistical distribution, but zero allocations in one O(n) pass.Additionally:
context.WithCancelfor therandomstrategy, which queries a single resolver per attempt and therefore has no losing race to abort.github.com/mroth/weightedrand/v2dependency (no longer used anywhere).This addresses Tier 4a of the hot-path performance findings (upstream / cache-miss path).
Benchmarks
benchstat(n=6), per-request resolver-selection cost:pickRandom/upstreams=2(default parallel_best)weightedRandom/upstreams=2(random strategy)pickRandom/upstreams=5weightedRandom/upstreams=5Tests
BenchmarkParallelBestResolverSelectionadded (per the tier's "add a bench if missing" guidance).go test ./resolver/(full package) andgo test -raceon the parallel/random paths pass.make lintclean.Not included
requestResponsechannel) is deliberately skipped: a losing goroutine still sends into the buffered channel afterResolvereturns, so recycling it would risk a stale response landing in a concurrent request's channel. Making it safe would require draining the slow loser before returning, negating the "fastest wins" benefit.