Skip to content

Commit f49196c

Browse files
committed
xTransport: avoid updating the host->IP map in multiple goroutines
When a goroutine is updating an IP, keep serving the previous IP to other goroutines.
1 parent 8264b43 commit f49196c

1 file changed

Lines changed: 28 additions & 9 deletions

File tree

dnscrypt-proxy/xtransport.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ const (
4040
)
4141

4242
type CachedIPItem struct {
43-
ip net.IP
44-
expiration *time.Time
43+
ip net.IP
44+
expiration *time.Time
45+
updating_until *time.Time
4546
}
4647

4748
type CachedIPs struct {
@@ -105,7 +106,7 @@ func ParseIP(ipStr string) net.IP {
105106
// If ttl < 0, never expire
106107
// Otherwise, ttl is set to max(ttl, MinResolverIPTTL)
107108
func (xTransport *XTransport) saveCachedIP(host string, ip net.IP, ttl time.Duration) {
108-
item := &CachedIPItem{ip: ip, expiration: nil}
109+
item := &CachedIPItem{ip: ip, expiration: nil, updating_until: nil}
109110
if ttl >= 0 {
110111
if ttl < MinResolverIPTTL {
111112
ttl = MinResolverIPTTL
@@ -118,8 +119,21 @@ func (xTransport *XTransport) saveCachedIP(host string, ip net.IP, ttl time.Dura
118119
xTransport.cachedIPs.Unlock()
119120
}
120121

121-
func (xTransport *XTransport) loadCachedIP(host string) (ip net.IP, expired bool) {
122-
ip, expired = nil, false
122+
// Mark an entry as being updated
123+
func (xTransport *XTransport) markUpdatingCachedIP(host string) {
124+
xTransport.cachedIPs.Lock()
125+
item, ok := xTransport.cachedIPs.cache[host]
126+
if ok {
127+
now := time.Now()
128+
until := now.Add(xTransport.timeout)
129+
item.updating_until = &until
130+
xTransport.cachedIPs.cache[host] = item
131+
}
132+
xTransport.cachedIPs.Unlock()
133+
}
134+
135+
func (xTransport *XTransport) loadCachedIP(host string) (ip net.IP, expired bool, updating bool) {
136+
ip, expired, updating = nil, false, false
123137
xTransport.cachedIPs.RLock()
124138
item, ok := xTransport.cachedIPs.cache[host]
125139
xTransport.cachedIPs.RUnlock()
@@ -130,6 +144,9 @@ func (xTransport *XTransport) loadCachedIP(host string) (ip net.IP, expired bool
130144
expiration := item.expiration
131145
if expiration != nil && time.Until(*expiration) < 0 {
132146
expired = true
147+
if item.updating_until != nil && time.Until(*item.updating_until) > 0 {
148+
updating = true
149+
}
133150
}
134151
return
135152
}
@@ -153,7 +170,7 @@ func (xTransport *XTransport) rebuildTransport() {
153170
ipOnly := host
154171
// resolveAndUpdateCache() is always called in `Fetch()` before the `Dial()`
155172
// method is used, so that a cached entry must be present at this point.
156-
cachedIP, _ := xTransport.loadCachedIP(host)
173+
cachedIP, _, _ := xTransport.loadCachedIP(host)
157174
if cachedIP != nil {
158175
if ipv4 := cachedIP.To4(); ipv4 != nil {
159176
ipOnly = ipv4.String()
@@ -263,7 +280,7 @@ func (xTransport *XTransport) rebuildTransport() {
263280
dlog.Debugf("Dialing for H3: [%v]", addrStr)
264281
host, port := ExtractHostAndPort(addrStr, stamps.DefaultPort)
265282
ipOnly := host
266-
cachedIP, _ := xTransport.loadCachedIP(host)
283+
cachedIP, _, _ := xTransport.loadCachedIP(host)
267284
network := "udp4"
268285
if cachedIP != nil {
269286
if ipv4 := cachedIP.To4(); ipv4 != nil {
@@ -402,10 +419,12 @@ func (xTransport *XTransport) resolveAndUpdateCache(host string) error {
402419
if ParseIP(host) != nil {
403420
return nil
404421
}
405-
cachedIP, expired := xTransport.loadCachedIP(host)
406-
if cachedIP != nil && !expired {
422+
cachedIP, expired, updating := xTransport.loadCachedIP(host)
423+
if cachedIP != nil && (!expired || updating) {
407424
return nil
408425
}
426+
xTransport.markUpdatingCachedIP(host)
427+
409428
var foundIP net.IP
410429
var ttl time.Duration
411430
var err error

0 commit comments

Comments
 (0)