This repository has been archived by the owner on Apr 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathcache.go
101 lines (80 loc) · 2.42 KB
/
cache.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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package cache
import (
"hash/crc32"
"math"
"net/http"
"sync"
"time"
)
const cacheBucketsSize = 256
type HTTPCache struct {
cacheKeyTemplate string
entries [cacheBucketsSize]map[string][]*HTTPCacheEntry
entriesLock [cacheBucketsSize]*sync.RWMutex
}
func NewHTTPCache(cacheKeyTemplate string) *HTTPCache {
entriesLocks := [cacheBucketsSize]*sync.RWMutex{}
entries := [cacheBucketsSize]map[string][]*HTTPCacheEntry{}
for i := 0; i < int(cacheBucketsSize); i++ {
entriesLocks[i] = new(sync.RWMutex)
entries[i] = make(map[string][]*HTTPCacheEntry)
}
return &HTTPCache{
cacheKeyTemplate: cacheKeyTemplate,
entries: entries,
entriesLock: entriesLocks,
}
}
func (cache *HTTPCache) Get(request *http.Request) (*HTTPCacheEntry, bool) {
key := getKey(cache.cacheKeyTemplate, request)
b := cache.getBucketIndexForKey(key)
cache.entriesLock[b].RLock()
defer cache.entriesLock[b].RUnlock()
previousEntries, exists := cache.entries[b][key]
if !exists {
return nil, false
}
for _, entry := range previousEntries {
if entry.Fresh() && matchesVary(request, entry) {
return entry, true
}
}
return nil, false
}
func (cache *HTTPCache) Put(request *http.Request, entry *HTTPCacheEntry) {
key := entry.Key()
bucket := cache.getBucketIndexForKey(key)
cache.entriesLock[bucket].Lock()
defer cache.entriesLock[bucket].Unlock()
cache.scheduleCleanEntry(entry)
for i, previousEntry := range cache.entries[bucket][key] {
if matchesVary(entry.Request, previousEntry) {
go previousEntry.Clean()
cache.entries[bucket][key][i] = entry
return
}
}
cache.entries[bucket][key] = append(cache.entries[bucket][key], entry)
}
func (cache *HTTPCache) scheduleCleanEntry(entry *HTTPCacheEntry) {
go func(entry *HTTPCacheEntry) {
time.Sleep(entry.expiration.Sub(time.Now().UTC()))
cache.cleanEntry(entry)
}(entry)
}
func (cache *HTTPCache) cleanEntry(entry *HTTPCacheEntry) {
key := entry.Key()
bucket := cache.getBucketIndexForKey(key)
cache.entriesLock[bucket].Lock()
defer cache.entriesLock[bucket].Unlock()
for i, otherEntry := range cache.entries[bucket][key] {
if entry == otherEntry {
cache.entries[bucket][key] = append(cache.entries[bucket][key][:i], cache.entries[bucket][key][i+1:]...)
entry.Clean()
return
}
}
}
func (cache *HTTPCache) getBucketIndexForKey(key string) uint32 {
return uint32(math.Mod(float64(crc32.ChecksumIEEE([]byte(key))), float64(cacheBucketsSize)))
}