/
coalesced_resolver.go
52 lines (43 loc) · 1.34 KB
/
coalesced_resolver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package coalesced
import (
"context"
"net/url"
"github.com/honeycombio/beeline-go"
"golang.org/x/sync/singleflight"
"github.com/mccutchen/urlresolver"
)
// Resolver is a urlresolver.Interface implementation that coalesces concurrent
// requests to upstream URLs.
type Resolver struct {
group *singleflight.Group
resolver urlresolver.Interface
}
// New creates a new coalesced Resolver.
func New(resolver urlresolver.Interface) *Resolver {
return &Resolver{
group: &singleflight.Group{},
resolver: resolver,
}
}
// Resolve resolves a URL if it is not already cached.
func (c *Resolver) Resolve(ctx context.Context, givenURL string) (urlresolver.Result, error) {
// A bit wasteful to canonicalize the URL here since the wrapped resolver
// will do the same thing, but it should slightly improve our chances of
// coalescing requests.
canonicalURL, err := canonicalize(givenURL)
if err != nil {
return urlresolver.Result{}, err
}
result, err, shared := c.group.Do(canonicalURL, func() (interface{}, error) {
return c.resolver.Resolve(ctx, canonicalURL)
})
beeline.AddField(ctx, "resolver.request_coalesced", shared)
return result.(urlresolver.Result), err
}
func canonicalize(givenURL string) (string, error) {
parsedURL, err := url.Parse(givenURL)
if err != nil {
return "", err
}
return urlresolver.Canonicalize(parsedURL), nil
}