...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package fixchain
16
17 import (
18 "fmt"
19 "io"
20 "log"
21 "net/http"
22 "sync"
23 "sync/atomic"
24 "time"
25 )
26
27 type lockedCache struct {
28 m map[string][]byte
29 sync.RWMutex
30 }
31
32 func (c *lockedCache) get(str string) ([]byte, bool) {
33 c.RLock()
34 defer c.RUnlock()
35 b, ok := c.m[str]
36 return b, ok
37 }
38
39 func (c *lockedCache) set(str string, b []byte) {
40 c.Lock()
41 defer c.Unlock()
42 c.m[str] = b
43 }
44
45 func newLockedCache() *lockedCache {
46 return &lockedCache{m: make(map[string][]byte)}
47 }
48
49 type urlCache struct {
50 client *http.Client
51 cache *lockedCache
52
53 hit uint32
54 miss uint32
55 errors uint32
56 badStatus uint32
57 readFail uint32
58 }
59
60 func (u *urlCache) getURL(url string) ([]byte, error) {
61 r, ok := u.cache.get(url)
62 if ok {
63 atomic.AddUint32(&u.hit, 1)
64 return r, nil
65 }
66 c, err := u.client.Get(url)
67 if err != nil {
68 atomic.AddUint32(&u.errors, 1)
69 return nil, err
70 }
71 defer c.Body.Close()
72
73 if c.StatusCode != 200 {
74 atomic.AddUint32(&u.badStatus, 1)
75 return nil, fmt.Errorf("can't deal with status %d", c.StatusCode)
76 }
77 r, err = io.ReadAll(c.Body)
78 if err != nil {
79 atomic.AddUint32(&u.readFail, 1)
80 return nil, err
81 }
82 atomic.AddUint32(&u.miss, 1)
83 u.cache.set(url, r)
84 return r, nil
85 }
86
87 func newURLCache(c *http.Client, logStats bool) *urlCache {
88 u := &urlCache{cache: newLockedCache(), client: c}
89
90 if logStats {
91 t := time.NewTicker(time.Second)
92 go func() {
93 for range t.C {
94 log.Printf("url cache: %d hits, %d misses, %d errors, "+
95 "%d bad status, %d read fail, %d cached", u.hit,
96 u.miss, u.errors, u.badStatus, u.readFail,
97 len(u.cache.m))
98 }
99 }()
100 }
101
102 return u
103 }
104
View as plain text