-
Notifications
You must be signed in to change notification settings - Fork 0
/
keystone_cache.go
139 lines (127 loc) · 4.42 KB
/
keystone_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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package identity
import (
"container/list"
"sync"
"time"
"github.com/gophercloud/gophercloud"
"github.com/notque/netflow-api/pkg/util"
)
// Cache type used for the name caches
type cache struct {
// "Inherit from" sync.RWMutex, so the cache can be locked during access/update
sync.RWMutex
// The actual cache - a simple map with no expiry
// (the total number of items will only be in the 10000s, ~100 bytes per item, so ~1Mb per cache)
m map[string]string
}
var providerClient *gophercloud.ProviderClient
var domainNameCache *cache
var projectNameCache *cache
var userNameCache *cache
var userIDCache *cache
var roleNameCache *cache
var groupNameCache *cache
// Token cache
type keystoneTokenCache struct {
// "Inherit from" sync.RWMutex, so the cache can be locked during access/update
sync.RWMutex
// tMap: Cached tokens (keystoneToken struct) accessible by the token ID from the request
tMap map[string]*keystoneToken // map tokenID to token struct
// eList: A sorted list of token expiry times, so we don't have to scan the whole list
// every time we check to see what's expired
eList *list.List // sorted list of expiration times
// eMap: If we know a token is expired at time T, we use this map to look up the tokenID
// so we can then remove the token from tMap.
eMap map[time.Time][]string // map expiration time to list of tokenIDs
}
var tokenCache *keystoneTokenCache
func init() {
domainNameCache = &cache{m: make(map[string]string)}
projectNameCache = &cache{m: make(map[string]string)}
userNameCache = &cache{m: make(map[string]string)}
userIDCache = &cache{m: make(map[string]string)}
roleNameCache = &cache{m: make(map[string]string)}
groupNameCache = &cache{m: make(map[string]string)}
tokenCache = &keystoneTokenCache{
tMap: make(map[string]*keystoneToken),
eMap: make(map[time.Time][]string),
eList: list.New(),
}
}
func updateCache(cache *cache, key string, value string) {
cache.Lock()
cache.m[key] = value
cache.Unlock()
}
func getFromCache(cache *cache, key string) (string, bool) {
cache.RLock()
value, exists := cache.m[key]
cache.RUnlock()
return value, exists
}
func addTokenToCache(cache *keystoneTokenCache, id string, token *keystoneToken) {
expiryTime, err := time.Parse("2006-01-02T15:04:05.999999Z", token.ExpiresAt)
if err != nil {
util.LogWarning("Not adding token to cache because time '%s' could not be parsed", token.ExpiresAt)
return
}
cache.Lock()
cache.eList.PushBack(expiryTime)
// If the expiryTime is earlier than the last item in the list,
// move it to the correct place so that we keep the list sorted
lastItem := cache.eList.Back()
if cache.eList.Back() != nil && expiryTime.Before(lastItem.Value.(time.Time)) {
for e := cache.eList.Back(); e != nil; e = e.Prev() {
if expiryTime.After((e.Value).(time.Time)) {
cache.eList.MoveAfter(cache.eList.Back(), e)
}
}
}
if cache.eMap[expiryTime] == nil {
cache.eMap[expiryTime] = []string{id}
} else {
cache.eMap[expiryTime] = append(cache.eMap[expiryTime], id)
}
cache.tMap[id] = token
cacheSize := len(cache.tMap)
cache.Unlock()
util.LogDebug("Added token to cache. Current cache size: %d", cacheSize)
}
func getCachedToken(cache *keystoneTokenCache, id string) *keystoneToken {
// First, remove expired tokens from cache
now := time.Now()
elemsToRemove := []*list.Element{}
cache.RLock()
for e := cache.eList.Front(); e != nil; e = e.Next() {
expiryTime := (e.Value).(time.Time)
if now.Before(expiryTime) {
break // list is sorted, so we can stop once we get to an unexpired token
}
// We can't remove from the list during the for loop, so remember which ones to delete
elemsToRemove = append(elemsToRemove, e)
}
cache.RUnlock()
cache.Lock()
for _, elem := range elemsToRemove {
cache.eList.Remove(elem) // Remove the cached expiry time from the sorted list
time := (elem.Value).(time.Time)
tokenIDs := cache.eMap[time]
delete(cache.eMap, time) // Remove the cached expiry time from the time:tokenIDs map
for _, tokenID := range tokenIDs {
delete(cache.tMap, tokenID) // Remove all the cached tokens
}
}
cacheSize := len(cache.tMap)
cache.Unlock()
if len(elemsToRemove) > 0 {
util.LogDebug("Removed expired token(s) from cache. Current cache size: %d", cacheSize)
}
// Now look for the token in question
cache.RLock()
token := cache.tMap[id]
cache.RUnlock()
if token != nil {
util.LogDebug("Got token from cache. Current cache size: %d", cacheSize)
}
return token
}