-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce a caching DNS resolver (#540)
There is no cache aging. If a resolution is successful, then we do cache the result. This is meant as a mechanism to make measurements faster and with less data points. For this reason it's disabled by default. Consider for example the case where `http://www.example.com` redirects you to `https://www.example.com`. You really don't want to perform the name resolution again in this case. As a side note, depending on the resolver we're using, we may not need the caching mechanism. Nonetheless, using the cache always when measuring also reduces the number of duplicate entries we produce where only the first entry has actually been resolved. This was developed as part of #509
- Loading branch information
1 parent
7c5a269
commit 7027883
Showing
4 changed files
with
104 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package resolver | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
) | ||
|
||
// CacheResolver is a resolver that caches successful replies. | ||
type CacheResolver struct { | ||
Resolver | ||
mu sync.Mutex | ||
cache map[string][]string | ||
} | ||
|
||
// LookupHost implements Resolver.LookupHost | ||
func (r *CacheResolver) LookupHost( | ||
ctx context.Context, hostname string) ([]string, error) { | ||
if entry := r.Get(hostname); entry != nil { | ||
return entry, nil | ||
} | ||
entry, err := r.Resolver.LookupHost(ctx, hostname) | ||
if err != nil { | ||
return nil, err | ||
} | ||
r.Set(hostname, entry) | ||
return entry, nil | ||
} | ||
|
||
// Get gets the currently configured entry for domain, or nil | ||
func (r *CacheResolver) Get(domain string) []string { | ||
r.mu.Lock() | ||
defer r.mu.Unlock() | ||
return r.cache[domain] | ||
} | ||
|
||
// Set allows to pre-populate the cache | ||
func (r *CacheResolver) Set(domain string, addresses []string) { | ||
r.mu.Lock() | ||
if r.cache == nil { | ||
r.cache = make(map[string][]string) | ||
} | ||
r.cache[domain] = addresses | ||
r.mu.Unlock() | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package resolver_test | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"testing" | ||
|
||
"github.com/ooni/probe-engine/netx/resolver" | ||
) | ||
|
||
func TestUnitCacheFailure(t *testing.T) { | ||
expected := errors.New("mocked error") | ||
var r resolver.Resolver = resolver.FakeResolver{ | ||
Err: expected, | ||
} | ||
r = &resolver.CacheResolver{Resolver: r} | ||
addrs, err := r.LookupHost(context.Background(), "www.google.com") | ||
if !errors.Is(err, expected) { | ||
t.Fatal("not the error we expected") | ||
} | ||
if addrs != nil { | ||
t.Fatal("expected nil addrs here") | ||
} | ||
} | ||
|
||
func TestUnitCacheHitSuccess(t *testing.T) { | ||
var r resolver.Resolver = resolver.FakeResolver{ | ||
Err: errors.New("mocked error"), | ||
} | ||
cache := &resolver.CacheResolver{Resolver: r} | ||
cache.Set("dns.google.com", []string{"8.8.8.8"}) | ||
addrs, err := cache.LookupHost(context.Background(), "dns.google.com") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if len(addrs) != 1 || addrs[0] != "8.8.8.8" { | ||
t.Fatal("not the result we expected") | ||
} | ||
} | ||
|
||
func TestUnitCacheMissSuccess(t *testing.T) { | ||
var r resolver.Resolver = resolver.FakeResolver{ | ||
Result: []string{"8.8.8.8"}, | ||
} | ||
r = &resolver.CacheResolver{Resolver: r} | ||
addrs, err := r.LookupHost(context.Background(), "dns.google.com") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if len(addrs) != 1 || addrs[0] != "8.8.8.8" { | ||
t.Fatal("not the result we expected") | ||
} | ||
} |