Resolver Balancing Strategies in MasterDnsVPN: Round Robin, Least Loss, and Lowest Latency
MasterDnsVPN implements three DNS resolver balancing strategies—Round Robin, Least Loss, and Lowest Latency—controlled by a strategy ID passed to the Balancer component in internal/client/balancer.go to optimize upstream DNS traffic distribution.
The masterking32/MasterDnsVPN repository provides a production-ready DNS VPN client that intelligently distributes UDP packets across multiple upstream resolvers. By selecting the appropriate resolver balancing strategy, you can optimize DNS query distribution for raw performance, packet loss avoidance, or latency-sensitive applications.
Round Robin Strategy
The Round Robin strategy (BalancingRoundRobin) cycles through active resolvers in a deterministic order without tracking performance metrics. It uses an atomic counter (rrCounter) to maintain position across concurrent requests.
When selecting a resolver, the balancer calculates the index using rrCounter % len(activeIDs) inside selectRoundRobinLocked. This provides lock-free, evenly distributed traffic across all healthy upstream connections regardless of performance characteristics.
// Round robin selection (simplified from line 1640)
func (b *Balancer) selectRoundRobinLocked(count int) []Connection {
n := len(b.activeIDs)
start := roundRobinStartIndex(b.rrCounter.Add(uint64(count))-uint64(count), n)
// Returns next available resolver in sequence
}
Least Loss Strategy
The Least Loss strategy (BalancingLeastLoss) prioritizes upstream resolvers that demonstrate the lowest packet loss ratios. The balancer computes a loss score using the formula (lost × 1000) / sent via the lossScoreLocked method.
This strategy requires a minimum sample size of 5 sent packets before scoring activates; otherwise, it assigns a neutral penalty of 200. Resolvers with zero loss receive a perfect score of 0, making them preferred targets.
// Loss score calculation (line 2033)
func (b *Balancer) lossScoreLocked(idx int) uint64 {
sent, _, lost, _, _ := b.stats[idx].snapshot()
if sent < 5 { return 200 }
if lost == 0 { return 0 }
return (lost * 1000) / sent
}
Lowest Latency Strategy
The Lowest Latency strategy (BalancingLowestLatency) routes queries to the resolver with the smallest average round-trip time (RTT). The latency score equals sumRTT / count for the last N successful replies, calculated in latencyScoreLocked at line 2047.
The strategy enforces a minimum threshold of 5 successful replies before calculating averages. Resolvers with insufficient samples receive a penalty of 999000 microseconds, effectively removing them from selection until adequate data exists.
// Latency score calculation (line 2047)
func (b *Balancer) latencyScoreLocked(idx int) uint64 {
_, _, _, sum, count := b.stats[idx].snapshot()
if count < 5 { return 999000 }
return sum / count // average RTT in microseconds
}
How the Balancer Evaluates Resolvers
The selection entry point is GetBestConnection, which checks the configured strategy and delegates to the appropriate scorer function. For statistical strategies (Least Loss and Lowest Latency), strategyScorerLocked (line 1836) returns both a scorer function and a signal flag indicating whether sufficient statistics exist.
// Strategy routing (line 1836)
func (b *Balancer) strategyScorerLocked() (func(int) uint64, bool) {
switch b.strategy {
case BalancingLeastLoss:
return b.lossScoreLocked, b.hasLossSignalLocked()
case BalancingLowestLatency:
return b.lossScoreLocked, b.hasLatencySignalLocked()
// ...
}
}
The bestScoredConnectionLocked method then iterates through active connections, invokes the scorer, and returns the resolver with the minimum calculated value.
Configuring the Balancing Strategy
In internal/client/client.go, the client initialization reads ResolverBalancingStrategy from the JSON configuration structure defined in internal/config/client.go. The constructor NewBalancer (line 125) accepts the strategy constant as its first argument.
// Example instantiations
balRR := NewBalancer(BalancingRoundRobin, logger) // Cyclic distribution
balLL := NewBalancer(BalancingLeastLoss, logger) // Loss-aware
balLT := NewBalancer(BalancingLowestLatency, logger) // Latency-optimized
// Usage
conn, ok := balLL.GetBestConnection()
if !ok {
// Handle no active resolvers
}
If the configuration omits an explicit strategy, the system defaults to round-robin behavior.
Summary
- Round Robin provides deterministic, cyclic distribution across resolvers without metric overhead, ideal for uniform infrastructure.
- Least Loss calculates packet loss ratios after 5 samples, preferring reliable connections in unstable network environments.
- Lowest Latency averages RTT measurements after 5 replies, optimizing for speed-sensitive DNS resolution.
- All strategies are implemented in
internal/client/balancer.go, with configuration managed throughinternal/config/client.go.
Frequently Asked Questions
How do I enable the Lowest Latency strategy in MasterDnsVPN?
Set the ResolverBalancingStrategy field in your client configuration JSON to the BalancingLowestLatency constant, then pass this value when calling NewBalancer in internal/client/client.go. The balancer automatically begins tracking RTT samples and will prioritize the fastest resolver after collecting at least 5 latency measurements.
What is the minimum sample size before Least Loss scoring activates?
The balancer requires 5 sent packets before calculating loss scores. Until this threshold is met, the lossScoreLocked function returns a neutral penalty of 200, preventing premature optimization based on insufficient data.
Does Round Robin track performance metrics?
No. The Round Robin strategy operates statelessly using only an atomic counter (rrCounter) to cycle through activeIDs. It does not collect latency, loss, or throughput statistics, resulting in minimal CPU and memory overhead compared to the statistical strategies.
Which strategy should I use for unreliable network connections?
Use Least Loss (BalancingLeastLoss) when operating over unstable networks. By calculating (lost × 1000) / sent, it actively avoids resolvers demonstrating packet loss, automatically routing traffic through the most reliable available upstream connection as network conditions fluctuate.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →