mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 13:09:58 +00:00
TUN-1350: Enhance error messages with cloudflarestatus.com link, if relevant
This commit is contained in:
27
vendor/github.com/cloudflare/golibs/LICENSE-BSD-CloudFlare
generated
vendored
Normal file
27
vendor/github.com/cloudflare/golibs/LICENSE-BSD-CloudFlare
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2013 CloudFlare, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of the CloudFlare, Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
3
vendor/github.com/cloudflare/golibs/lrucache/.gitignore
generated
vendored
Normal file
3
vendor/github.com/cloudflare/golibs/lrucache/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
cover.out~
|
||||
benchmark/benchmark
|
||||
|
41
vendor/github.com/cloudflare/golibs/lrucache/Makefile
generated
vendored
Normal file
41
vendor/github.com/cloudflare/golibs/lrucache/Makefile
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
RACE+=--race
|
||||
|
||||
PKGNAME=github.com/cloudflare/golibs/lrucache
|
||||
SKIPCOVER=list.go|list_extension.go|priorityqueue.go
|
||||
|
||||
.PHONY: all test bench cover clean
|
||||
|
||||
all:
|
||||
@echo "Targets:"
|
||||
@echo " test: run tests with race detector"
|
||||
@echo " cover: print test coverage"
|
||||
@echo " bench: run basic benchmarks"
|
||||
|
||||
test:
|
||||
@go test $(RACE) -bench=. -v $(PKGNAME)
|
||||
|
||||
COVEROUT=cover.out
|
||||
cover:
|
||||
@go test -coverprofile=$(COVEROUT) -v $(PKGNAME)
|
||||
@cat $(COVEROUT) | egrep -v '$(SKIPCOVER)' > $(COVEROUT)~
|
||||
@go tool cover -func=$(COVEROUT)~|sed 's|^.*/\([^/]*/[^/]*/[^/]*\)$$|\1|g'
|
||||
|
||||
bench:
|
||||
@echo "[*] Scalability of cache/lrucache"
|
||||
@echo "[ ] Operations in shared cache using one core"
|
||||
@GOMAXPROCS=1 go test -run=- -bench='.*LRUCache.*' $(PKGNAME) \
|
||||
| egrep -v "^PASS|^ok"
|
||||
|
||||
@echo "[*] Scalability of cache/multilru"
|
||||
@echo "[ ] Operations in four caches using four cores "
|
||||
@GOMAXPROCS=4 go test -run=- -bench='.*MultiLRU.*' $(PKGNAME) \
|
||||
| egrep -v "^PASS|^ok"
|
||||
|
||||
|
||||
@(cd benchmark; go build $(PKGNAME)/benchmark)
|
||||
@./benchmark/benchmark
|
||||
|
||||
clean:
|
||||
rm -rf $(COVEROUT) $(COVEROUT)~ benchmark/benchmark
|
40
vendor/github.com/cloudflare/golibs/lrucache/README.md
generated
vendored
Normal file
40
vendor/github.com/cloudflare/golibs/lrucache/README.md
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
LRU Cache
|
||||
---------
|
||||
|
||||
A `golang` implementation of last recently used cache data structure.
|
||||
|
||||
To install:
|
||||
|
||||
go get github.com/cloudflare/golibs/lrucache
|
||||
|
||||
To test:
|
||||
|
||||
cd $GOPATH/src/github.com/cloudflare/golibs/lrucache
|
||||
make test
|
||||
|
||||
For coverage:
|
||||
|
||||
make cover
|
||||
|
||||
Basic benchmarks:
|
||||
|
||||
$ make bench # As tested on my two core i5
|
||||
[*] Scalability of cache/lrucache
|
||||
[ ] Operations in shared cache using one core
|
||||
BenchmarkConcurrentGetLRUCache 5000000 450 ns/op
|
||||
BenchmarkConcurrentSetLRUCache 2000000 821 ns/op
|
||||
BenchmarkConcurrentSetNXLRUCache 5000000 664 ns/op
|
||||
|
||||
[*] Scalability of cache/multilru
|
||||
[ ] Operations in four caches using four cores
|
||||
BenchmarkConcurrentGetMultiLRU-4 5000000 475 ns/op
|
||||
BenchmarkConcurrentSetMultiLRU-4 2000000 809 ns/op
|
||||
BenchmarkConcurrentSetNXMultiLRU-4 5000000 643 ns/op
|
||||
|
||||
[*] Capacity=4096 Keys=30000 KeySpace=15625
|
||||
vitess LRUCache MultiLRUCache-4
|
||||
create 1.709us 1.626374ms 343.54us
|
||||
Get (miss) 144.266083ms 132.470397ms 177.277193ms
|
||||
SetNX #1 338.637977ms 380.733302ms 411.709204ms
|
||||
Get (hit) 195.896066ms 173.252112ms 234.109494ms
|
||||
SetNX #2 349.785951ms 367.255624ms 419.129127ms
|
69
vendor/github.com/cloudflare/golibs/lrucache/cache.go
generated
vendored
Normal file
69
vendor/github.com/cloudflare/golibs/lrucache/cache.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
// Package lrucache implements a last recently used cache data structure.
|
||||
//
|
||||
// This code tries to avoid dynamic memory allocations - all required
|
||||
// memory is allocated on creation. Access to the data structure is
|
||||
// O(1). Modification O(log(n)) if expiry is used, O(1)
|
||||
// otherwise.
|
||||
//
|
||||
// This package exports three things:
|
||||
// LRUCache: is the main implementation. It supports multithreading by
|
||||
// using guarding mutex lock.
|
||||
//
|
||||
// MultiLRUCache: is a sharded implementation. It supports the same
|
||||
// API as LRUCache and uses it internally, but is not limited to
|
||||
// a single CPU as every shard is separately locked. Use this
|
||||
// data structure instead of LRUCache if you have have lock
|
||||
// contention issues.
|
||||
//
|
||||
// Cache interface: Both implementations fulfill it.
|
||||
package lrucache
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cache interface is fulfilled by the LRUCache and MultiLRUCache
|
||||
// implementations.
|
||||
type Cache interface {
|
||||
// Methods not needing to know current time.
|
||||
//
|
||||
// Get a key from the cache, possibly stale. Update its LRU
|
||||
// score.
|
||||
Get(key string) (value interface{}, ok bool)
|
||||
// Get a key from the cache, possibly stale. Don't modify its LRU score. O(1)
|
||||
GetQuiet(key string) (value interface{}, ok bool)
|
||||
// Get and remove a key from the cache.
|
||||
Del(key string) (value interface{}, ok bool)
|
||||
// Evict all items from the cache.
|
||||
Clear() int
|
||||
// Number of entries used in the LRU
|
||||
Len() int
|
||||
// Get the total capacity of the LRU
|
||||
Capacity() int
|
||||
|
||||
// Methods use time.Now() when neccessary to determine expiry.
|
||||
//
|
||||
// Add an item to the cache overwriting existing one if it
|
||||
// exists.
|
||||
Set(key string, value interface{}, expire time.Time)
|
||||
// Get a key from the cache, make sure it's not stale. Update
|
||||
// its LRU score.
|
||||
GetNotStale(key string) (value interface{}, ok bool)
|
||||
// Evict all the expired items.
|
||||
Expire() int
|
||||
|
||||
// Methods allowing to explicitly specify time used to
|
||||
// determine if items are expired.
|
||||
//
|
||||
// Add an item to the cache overwriting existing one if it
|
||||
// exists. Allows specifing current time required to expire an
|
||||
// item when no more slots are used.
|
||||
SetNow(key string, value interface{}, expire time.Time, now time.Time)
|
||||
// Get a key from the cache, make sure it's not stale. Update
|
||||
// its LRU score.
|
||||
GetNotStaleNow(key string, now time.Time) (value interface{}, ok bool)
|
||||
// Evict items that expire before Now.
|
||||
ExpireNow(now time.Time) int
|
||||
}
|
238
vendor/github.com/cloudflare/golibs/lrucache/list.go
generated
vendored
Normal file
238
vendor/github.com/cloudflare/golibs/lrucache/list.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/* This file is a slightly modified file from the go package sources
|
||||
and is released on the following license:
|
||||
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Package list implements a doubly linked list.
|
||||
//
|
||||
// To iterate over a list (where l is a *List):
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.Value
|
||||
// }
|
||||
//
|
||||
|
||||
package lrucache
|
||||
|
||||
// Element is an element of a linked list.
|
||||
type element struct {
|
||||
// Next and previous pointers in the doubly-linked list of elements.
|
||||
// To simplify the implementation, internally a list l is implemented
|
||||
// as a ring, such that &l.root is both the next element of the last
|
||||
// list element (l.Back()) and the previous element of the first list
|
||||
// element (l.Front()).
|
||||
next, prev *element
|
||||
|
||||
// The list to which this element belongs.
|
||||
list *list
|
||||
|
||||
// The value stored with this element.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Next returns the next list element or nil.
|
||||
func (e *element) Next() *element {
|
||||
if p := e.next; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prev returns the previous list element or nil.
|
||||
func (e *element) Prev() *element {
|
||||
if p := e.prev; e.list != nil && p != &e.list.root {
|
||||
return p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List represents a doubly linked list.
|
||||
// The zero value for List is an empty list ready to use.
|
||||
type list struct {
|
||||
root element // sentinel list element, only &root, root.prev, and root.next are used
|
||||
len int // current list length excluding (this) sentinel element
|
||||
}
|
||||
|
||||
// Init initializes or clears list l.
|
||||
func (l *list) Init() *list {
|
||||
l.root.next = &l.root
|
||||
l.root.prev = &l.root
|
||||
l.len = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// New returns an initialized list.
|
||||
// func New() *list { return new(list).Init() }
|
||||
|
||||
// Len returns the number of elements of list l.
|
||||
// The complexity is O(1).
|
||||
func (l *list) Len() int { return l.len }
|
||||
|
||||
// Front returns the first element of list l or nil
|
||||
func (l *list) Front() *element {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.next
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
func (l *list) Back() *element {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.prev
|
||||
}
|
||||
|
||||
// insert inserts e after at, increments l.len, and returns e.
|
||||
func (l *list) insert(e, at *element) *element {
|
||||
n := at.next
|
||||
at.next = e
|
||||
e.prev = at
|
||||
e.next = n
|
||||
n.prev = e
|
||||
e.list = l
|
||||
l.len++
|
||||
return e
|
||||
}
|
||||
|
||||
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
|
||||
func (l *list) insertValue(v interface{}, at *element) *element {
|
||||
return l.insert(&element{Value: v}, at)
|
||||
}
|
||||
|
||||
// remove removes e from its list, decrements l.len, and returns e.
|
||||
func (l *list) remove(e *element) *element {
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.next = nil // avoid memory leaks
|
||||
e.prev = nil // avoid memory leaks
|
||||
e.list = nil
|
||||
l.len--
|
||||
return e
|
||||
}
|
||||
|
||||
// Remove removes e from l if e is an element of list l.
|
||||
// It returns the element value e.Value.
|
||||
func (l *list) Remove(e *element) interface{} {
|
||||
if e.list == l {
|
||||
// if e.list == l, l must have been initialized when e was inserted
|
||||
// in l or l == nil (e is a zero Element) and l.remove will crash
|
||||
l.remove(e)
|
||||
}
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// PushFront inserts a new element e with value v at the front of list l and returns e.
|
||||
func (l *list) PushFront(v interface{}) *element {
|
||||
return l.insertValue(v, &l.root)
|
||||
}
|
||||
|
||||
// PushBack inserts a new element e with value v at the back of list l and returns e.
|
||||
func (l *list) PushBack(v interface{}) *element {
|
||||
return l.insertValue(v, l.root.prev)
|
||||
}
|
||||
|
||||
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
func (l *list) InsertBefore(v interface{}, mark *element) *element {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
return l.insertValue(v, mark.prev)
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
func (l *list) InsertAfter(v interface{}, mark *element) *element {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
return l.insertValue(v, mark)
|
||||
}
|
||||
|
||||
// MoveToFront moves element e to the front of list l.
|
||||
// If e is not an element of l, the list is not modified.
|
||||
func (l *list) MoveToFront(e *element) {
|
||||
if e.list != l || l.root.next == e {
|
||||
return
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.insert(l.remove(e), &l.root)
|
||||
}
|
||||
|
||||
// MoveToBack moves element e to the back of list l.
|
||||
// If e is not an element of l, the list is not modified.
|
||||
func (l *list) MoveToBack(e *element) {
|
||||
if e.list != l || l.root.prev == e {
|
||||
return
|
||||
}
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.insert(l.remove(e), l.root.prev)
|
||||
}
|
||||
|
||||
// MoveBefore moves element e to its new position before mark.
|
||||
// If e is not an element of l, or e == mark, the list is not modified.
|
||||
func (l *list) MoveBefore(e, mark *element) {
|
||||
if e.list != l || e == mark {
|
||||
return
|
||||
}
|
||||
l.insert(l.remove(e), mark.prev)
|
||||
}
|
||||
|
||||
// MoveAfter moves element e to its new position after mark.
|
||||
// If e is not an element of l, or e == mark, the list is not modified.
|
||||
func (l *list) MoveAfter(e, mark *element) {
|
||||
if e.list != l || e == mark {
|
||||
return
|
||||
}
|
||||
l.insert(l.remove(e), mark)
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list l.
|
||||
// The lists l and other may be the same.
|
||||
func (l *list) PushBackList(other *list) {
|
||||
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
|
||||
l.insertValue(e.Value, l.root.prev)
|
||||
}
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list l.
|
||||
// The lists l and other may be the same.
|
||||
func (l *list) PushFrontList(other *list) {
|
||||
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
|
||||
l.insertValue(e.Value, &l.root)
|
||||
}
|
||||
}
|
25
vendor/github.com/cloudflare/golibs/lrucache/list_extension.go
generated
vendored
Normal file
25
vendor/github.com/cloudflare/golibs/lrucache/list_extension.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
// Extensions to "container/list" that allowing reuse of Elements.
|
||||
|
||||
package lrucache
|
||||
|
||||
func (l *list) PushElementFront(e *element) *element {
|
||||
return l.insert(e, &l.root)
|
||||
}
|
||||
|
||||
func (l *list) PushElementBack(e *element) *element {
|
||||
return l.insert(e, l.root.prev)
|
||||
}
|
||||
|
||||
func (l *list) PopElementFront() *element {
|
||||
el := l.Front()
|
||||
l.Remove(el)
|
||||
return el
|
||||
}
|
||||
|
||||
func (l *list) PopFront() interface{} {
|
||||
el := l.Front()
|
||||
l.Remove(el)
|
||||
return el.Value
|
||||
}
|
316
vendor/github.com/cloudflare/golibs/lrucache/lrucache.go
generated
vendored
Normal file
316
vendor/github.com/cloudflare/golibs/lrucache/lrucache.go
generated
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package lrucache
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Every element in the cache is linked to three data structures:
|
||||
// Table map, PriorityQueue heap ordered by expiry and a LruList list
|
||||
// ordered by decreasing popularity.
|
||||
type entry struct {
|
||||
element element // list element. value is a pointer to this entry
|
||||
key string // key is a key!
|
||||
value interface{} //
|
||||
expire time.Time // time when the item is expired. it's okay to be stale.
|
||||
index int // index for priority queue needs. -1 if entry is free
|
||||
}
|
||||
|
||||
// LRUCache data structure. Never dereference it or copy it by
|
||||
// value. Always use it through a pointer.
|
||||
type LRUCache struct {
|
||||
lock sync.Mutex
|
||||
table map[string]*entry // all entries in table must be in lruList
|
||||
priorityQueue priorityQueue // some elements from table may be in priorityQueue
|
||||
lruList list // every entry is either used and resides in lruList
|
||||
freeList list // or free and is linked to freeList
|
||||
|
||||
ExpireGracePeriod time.Duration // time after an expired entry is purged from cache (unless pushed out of LRU)
|
||||
}
|
||||
|
||||
// Initialize the LRU cache instance. O(capacity)
|
||||
func (b *LRUCache) Init(capacity uint) {
|
||||
b.table = make(map[string]*entry, capacity)
|
||||
b.priorityQueue = make([]*entry, 0, capacity)
|
||||
b.lruList.Init()
|
||||
b.freeList.Init()
|
||||
heap.Init(&b.priorityQueue)
|
||||
|
||||
// Reserve all the entries in one giant continous block of memory
|
||||
arrayOfEntries := make([]entry, capacity)
|
||||
for i := uint(0); i < capacity; i++ {
|
||||
e := &arrayOfEntries[i]
|
||||
e.element.Value = e
|
||||
e.index = -1
|
||||
b.freeList.PushElementBack(&e.element)
|
||||
}
|
||||
}
|
||||
|
||||
// Create new LRU cache instance. Allocate all the needed memory. O(capacity)
|
||||
func NewLRUCache(capacity uint) *LRUCache {
|
||||
b := &LRUCache{}
|
||||
b.Init(capacity)
|
||||
return b
|
||||
}
|
||||
|
||||
// Give me the entry with lowest expiry field if it's before now.
|
||||
func (b *LRUCache) expiredEntry(now time.Time) *entry {
|
||||
if len(b.priorityQueue) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if now.IsZero() {
|
||||
// Fill it only when actually used.
|
||||
now = time.Now()
|
||||
}
|
||||
|
||||
if e := b.priorityQueue[0]; e.expire.Before(now) {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Give me the least used entry.
|
||||
func (b *LRUCache) leastUsedEntry() *entry {
|
||||
return b.lruList.Back().Value.(*entry)
|
||||
}
|
||||
|
||||
func (b *LRUCache) freeSomeEntry(now time.Time) (e *entry, used bool) {
|
||||
if b.freeList.Len() > 0 {
|
||||
return b.freeList.Front().Value.(*entry), false
|
||||
}
|
||||
|
||||
e = b.expiredEntry(now)
|
||||
if e != nil {
|
||||
return e, true
|
||||
}
|
||||
|
||||
if b.lruList.Len() == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return b.leastUsedEntry(), true
|
||||
}
|
||||
|
||||
// Move entry from used/lru list to a free list. Clear the entry as well.
|
||||
func (b *LRUCache) removeEntry(e *entry) {
|
||||
if e.element.list != &b.lruList {
|
||||
panic("list lruList")
|
||||
}
|
||||
|
||||
if e.index != -1 {
|
||||
heap.Remove(&b.priorityQueue, e.index)
|
||||
}
|
||||
b.lruList.Remove(&e.element)
|
||||
b.freeList.PushElementFront(&e.element)
|
||||
delete(b.table, e.key)
|
||||
e.key = ""
|
||||
e.value = nil
|
||||
}
|
||||
|
||||
func (b *LRUCache) insertEntry(e *entry) {
|
||||
if e.element.list != &b.freeList {
|
||||
panic("list freeList")
|
||||
}
|
||||
|
||||
if !e.expire.IsZero() {
|
||||
heap.Push(&b.priorityQueue, e)
|
||||
}
|
||||
b.freeList.Remove(&e.element)
|
||||
b.lruList.PushElementFront(&e.element)
|
||||
b.table[e.key] = e
|
||||
}
|
||||
|
||||
func (b *LRUCache) touchEntry(e *entry) {
|
||||
b.lruList.MoveToFront(&e.element)
|
||||
}
|
||||
|
||||
// Add an item to the cache overwriting existing one if it
|
||||
// exists. Allows specifing current time required to expire an item
|
||||
// when no more slots are used. O(log(n)) if expiry is set, O(1) when
|
||||
// clear.
|
||||
func (b *LRUCache) SetNow(key string, value interface{}, expire time.Time, now time.Time) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
var used bool
|
||||
|
||||
e := b.table[key]
|
||||
if e != nil {
|
||||
used = true
|
||||
} else {
|
||||
e, used = b.freeSomeEntry(now)
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if used {
|
||||
b.removeEntry(e)
|
||||
}
|
||||
|
||||
e.key = key
|
||||
e.value = value
|
||||
e.expire = expire
|
||||
b.insertEntry(e)
|
||||
}
|
||||
|
||||
// Add an item to the cache overwriting existing one if it
|
||||
// exists. O(log(n)) if expiry is set, O(1) when clear.
|
||||
func (b *LRUCache) Set(key string, value interface{}, expire time.Time) {
|
||||
b.SetNow(key, value, expire, time.Time{})
|
||||
}
|
||||
|
||||
// Get a key from the cache, possibly stale. Update its LRU score. O(1)
|
||||
func (b *LRUCache) Get(key string) (v interface{}, ok bool) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
e := b.table[key]
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b.touchEntry(e)
|
||||
return e.value, true
|
||||
}
|
||||
|
||||
// Get a key from the cache, possibly stale. Don't modify its LRU score. O(1)
|
||||
func (b *LRUCache) GetQuiet(key string) (v interface{}, ok bool) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
e := b.table[key]
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return e.value, true
|
||||
}
|
||||
|
||||
// Get a key from the cache, make sure it's not stale. Update its
|
||||
// LRU score. O(log(n)) if the item is expired.
|
||||
func (b *LRUCache) GetNotStale(key string) (value interface{}, ok bool) {
|
||||
return b.GetNotStaleNow(key, time.Now())
|
||||
}
|
||||
|
||||
// Get a key from the cache, make sure it's not stale. Update its
|
||||
// LRU score. O(log(n)) if the item is expired.
|
||||
func (b *LRUCache) GetNotStaleNow(key string, now time.Time) (value interface{}, ok bool) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
e := b.table[key]
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if e.expire.Before(now) {
|
||||
// Remove entries expired for more than a graceful period
|
||||
if b.ExpireGracePeriod == 0 || e.expire.Sub(now) > b.ExpireGracePeriod {
|
||||
b.removeEntry(e)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b.touchEntry(e)
|
||||
return e.value, true
|
||||
}
|
||||
|
||||
// Get a key from the cache, possibly stale. Update its LRU
|
||||
// score. O(1) always.
|
||||
func (b *LRUCache) GetStale(key string) (value interface{}, ok, expired bool) {
|
||||
return b.GetStaleNow(key, time.Now())
|
||||
}
|
||||
|
||||
// Get a key from the cache, possibly stale. Update its LRU
|
||||
// score. O(1) always.
|
||||
func (b *LRUCache) GetStaleNow(key string, now time.Time) (value interface{}, ok, expired bool) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
e := b.table[key]
|
||||
if e == nil {
|
||||
return nil, false, false
|
||||
}
|
||||
|
||||
b.touchEntry(e)
|
||||
return e.value, true, e.expire.Before(now)
|
||||
}
|
||||
|
||||
// Get and remove a key from the cache. O(log(n)) if the item is using expiry, O(1) otherwise.
|
||||
func (b *LRUCache) Del(key string) (v interface{}, ok bool) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
e := b.table[key]
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
value := e.value
|
||||
b.removeEntry(e)
|
||||
return value, true
|
||||
}
|
||||
|
||||
// Evict all items from the cache. O(n*log(n))
|
||||
func (b *LRUCache) Clear() int {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
// First, remove entries that have expiry set
|
||||
l := len(b.priorityQueue)
|
||||
for i := 0; i < l; i++ {
|
||||
// This could be reduced to O(n).
|
||||
b.removeEntry(b.priorityQueue[0])
|
||||
}
|
||||
|
||||
// Second, remove all remaining entries
|
||||
r := b.lruList.Len()
|
||||
for i := 0; i < r; i++ {
|
||||
b.removeEntry(b.leastUsedEntry())
|
||||
}
|
||||
return l + r
|
||||
}
|
||||
|
||||
// Evict all the expired items. O(n*log(n))
|
||||
func (b *LRUCache) Expire() int {
|
||||
return b.ExpireNow(time.Now())
|
||||
}
|
||||
|
||||
// Evict items that expire before `now`. O(n*log(n))
|
||||
func (b *LRUCache) ExpireNow(now time.Time) int {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
i := 0
|
||||
for {
|
||||
e := b.expiredEntry(now)
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
b.removeEntry(e)
|
||||
i += 1
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Number of entries used in the LRU
|
||||
func (b *LRUCache) Len() int {
|
||||
// yes. this stupid thing requires locking
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
return b.lruList.Len()
|
||||
}
|
||||
|
||||
// Get the total capacity of the LRU
|
||||
func (b *LRUCache) Capacity() int {
|
||||
// yes. this stupid thing requires locking
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
return b.lruList.Len() + b.freeList.Len()
|
||||
}
|
118
vendor/github.com/cloudflare/golibs/lrucache/multilru.go
generated
vendored
Normal file
118
vendor/github.com/cloudflare/golibs/lrucache/multilru.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package lrucache
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MultiLRUCache data structure. Never dereference it or copy it by
|
||||
// value. Always use it through a pointer.
|
||||
type MultiLRUCache struct {
|
||||
buckets uint
|
||||
cache []*LRUCache
|
||||
}
|
||||
|
||||
// Using this constructor is almost always wrong. Use NewMultiLRUCache instead.
|
||||
func (m *MultiLRUCache) Init(buckets, bucket_capacity uint) {
|
||||
m.buckets = buckets
|
||||
m.cache = make([]*LRUCache, buckets)
|
||||
for i := uint(0); i < buckets; i++ {
|
||||
m.cache[i] = NewLRUCache(bucket_capacity)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the stale expiry grace period for each cache in the multicache instance.
|
||||
func (m *MultiLRUCache) SetExpireGracePeriod(p time.Duration) {
|
||||
for _, c := range m.cache {
|
||||
c.ExpireGracePeriod = p
|
||||
}
|
||||
}
|
||||
|
||||
func NewMultiLRUCache(buckets, bucket_capacity uint) *MultiLRUCache {
|
||||
m := &MultiLRUCache{}
|
||||
m.Init(buckets, bucket_capacity)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) bucketNo(key string) uint {
|
||||
// Arbitrary choice. Any fast hash will do.
|
||||
return uint(crc32.ChecksumIEEE([]byte(key))) % m.buckets
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Set(key string, value interface{}, expire time.Time) {
|
||||
m.cache[m.bucketNo(key)].Set(key, value, expire)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) SetNow(key string, value interface{}, expire time.Time, now time.Time) {
|
||||
m.cache[m.bucketNo(key)].SetNow(key, value, expire, now)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Get(key string) (value interface{}, ok bool) {
|
||||
return m.cache[m.bucketNo(key)].Get(key)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) GetQuiet(key string) (value interface{}, ok bool) {
|
||||
return m.cache[m.bucketNo(key)].Get(key)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) GetNotStale(key string) (value interface{}, ok bool) {
|
||||
return m.cache[m.bucketNo(key)].GetNotStale(key)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) GetNotStaleNow(key string, now time.Time) (value interface{}, ok bool) {
|
||||
return m.cache[m.bucketNo(key)].GetNotStaleNow(key, now)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) GetStale(key string) (value interface{}, ok, expired bool) {
|
||||
return m.cache[m.bucketNo(key)].GetStale(key)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) GetStaleNow(key string, now time.Time) (value interface{}, ok, expired bool) {
|
||||
return m.cache[m.bucketNo(key)].GetStaleNow(key, now)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Del(key string) (value interface{}, ok bool) {
|
||||
return m.cache[m.bucketNo(key)].Del(key)
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Clear() int {
|
||||
var s int
|
||||
for _, c := range m.cache {
|
||||
s += c.Clear()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Len() int {
|
||||
var s int
|
||||
for _, c := range m.cache {
|
||||
s += c.Len()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Capacity() int {
|
||||
var s int
|
||||
for _, c := range m.cache {
|
||||
s += c.Capacity()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) Expire() int {
|
||||
var s int
|
||||
for _, c := range m.cache {
|
||||
s += c.Expire()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (m *MultiLRUCache) ExpireNow(now time.Time) int {
|
||||
var s int
|
||||
for _, c := range m.cache {
|
||||
s += c.ExpireNow(now)
|
||||
}
|
||||
return s
|
||||
}
|
37
vendor/github.com/cloudflare/golibs/lrucache/priorityqueue.go
generated
vendored
Normal file
37
vendor/github.com/cloudflare/golibs/lrucache/priorityqueue.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
// This code is based on golang example from "container/heap" package.
|
||||
|
||||
package lrucache
|
||||
|
||||
type priorityQueue []*entry
|
||||
|
||||
func (pq priorityQueue) Len() int {
|
||||
return len(pq)
|
||||
}
|
||||
|
||||
func (pq priorityQueue) Less(i, j int) bool {
|
||||
return pq[i].expire.Before(pq[j].expire)
|
||||
}
|
||||
|
||||
func (pq priorityQueue) Swap(i, j int) {
|
||||
pq[i], pq[j] = pq[j], pq[i]
|
||||
pq[i].index = i
|
||||
pq[j].index = j
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) Push(e interface{}) {
|
||||
n := len(*pq)
|
||||
item := e.(*entry)
|
||||
item.index = n
|
||||
*pq = append(*pq, item)
|
||||
}
|
||||
|
||||
func (pq *priorityQueue) Pop() interface{} {
|
||||
old := *pq
|
||||
n := len(old)
|
||||
item := old[n-1]
|
||||
item.index = -1
|
||||
*pq = old[0 : n-1]
|
||||
return item
|
||||
}
|
Reference in New Issue
Block a user