forked from DataDog/dd-trace-go
/
fetcher.go
86 lines (71 loc) · 2.67 KB
/
fetcher.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
// This file is pulled from datadog-agent/pkg/util/cachedfetch changing the logger and using strings only
// Package cachedfetch provides a read-through cache for fetched values.
package cachedfetch
import (
"context"
"sync"
"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
)
// Fetcher supports fetching a value, such as from a cloud service API. An
// attempt is made to fetch the value on each call to Fetch, but if that
// attempt fails then a cached value from the last successful attempt is
// returned, if such a value exists. This helps the agent to "ride out"
// temporary failures in cloud APIs while still fetching fresh data when those
// APIs are functioning properly. Cached values do not expire.
//
// Callers should instantiate one fetcher per piece of data required.
type Fetcher struct {
// function that attempts to fetch the value
Attempt func(context.Context) (string, error)
// the name of the thing being fetched, used in the default log message. At
// least one of Name and LogFailure must be non-nil.
Name string
// function to log a fetch failure, given the error and the last successful
// value. This function is not called if there is no last successful value.
// If left at its zero state, a default log message will be generated, using
// Name.
LogFailure func(error, interface{})
// previous successfully fetched value
lastValue interface{}
// mutex to protect access to lastValue
sync.Mutex
}
// Fetch attempts to fetch the value, returning the result or the last successful
// value, or an error if no attempt has ever been successful. No special handling
// is included for the Context: both context.Cancelled and context.DeadlineExceeded
// are handled like any other error by returning the cached value.
//
// This can be called from multiple goroutines, in which case it will call Attempt
// concurrently.
func (f *Fetcher) Fetch(ctx context.Context) (string, error) {
value, err := f.Attempt(ctx)
if err == nil {
f.Lock()
f.lastValue = value
f.Unlock()
return value, nil
}
f.Lock()
lastValue := f.lastValue
f.Unlock()
if lastValue == nil {
// attempt was never successful
return value, err
}
if f.LogFailure == nil {
log.Debug("Unable to get %s; returning cached value instead", f.Name)
} else {
f.LogFailure(err, lastValue)
}
return lastValue.(string), nil
}
// Reset resets the cached value (used for testing)
func (f *Fetcher) Reset() {
f.Lock()
f.lastValue = nil
f.Unlock()
}