aboutsummaryrefslogtreecommitdiffstats
path: root/library/go/yandex/tvm/tvmtool/internal/cache/cache.go
blob: 9ec9665682011fe0634541069310ded985c2bc54 (plain) (blame)
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
package cache

import (
	"sync"
	"time"

	"github.com/ydb-platform/ydb/library/go/yandex/tvm"
)

const (
	Hit Status = iota
	Miss
	GonnaMissy
)

type (
	Status int

	Cache struct {
		ttl     time.Duration
		maxTTL  time.Duration
		tickets map[tvm.ClientID]entry
		aliases map[string]tvm.ClientID
		lock    sync.RWMutex
	}

	entry struct {
		value *string
		born  time.Time
	}
)

func New(ttl, maxTTL time.Duration) *Cache {
	return &Cache{
		ttl:     ttl,
		maxTTL:  maxTTL,
		tickets: make(map[tvm.ClientID]entry, 1),
		aliases: make(map[string]tvm.ClientID, 1),
	}
}

func (c *Cache) Gc() {
	now := time.Now()

	c.lock.Lock()
	defer c.lock.Unlock()
	for clientID, ticket := range c.tickets {
		if ticket.born.Add(c.maxTTL).After(now) {
			continue
		}

		delete(c.tickets, clientID)
		for alias, aClientID := range c.aliases {
			if clientID == aClientID {
				delete(c.aliases, alias)
			}
		}
	}
}

func (c *Cache) ClientIDs() []tvm.ClientID {
	c.lock.RLock()
	defer c.lock.RUnlock()

	clientIDs := make([]tvm.ClientID, 0, len(c.tickets))
	for clientID := range c.tickets {
		clientIDs = append(clientIDs, clientID)
	}
	return clientIDs
}

func (c *Cache) Aliases() []string {
	c.lock.RLock()
	defer c.lock.RUnlock()

	aliases := make([]string, 0, len(c.aliases))
	for alias := range c.aliases {
		aliases = append(aliases, alias)
	}
	return aliases
}

func (c *Cache) Load(clientID tvm.ClientID) (*string, Status) {
	c.lock.RLock()
	e, ok := c.tickets[clientID]
	c.lock.RUnlock()
	if !ok {
		return nil, Miss
	}

	now := time.Now()
	exp := e.born.Add(c.ttl)
	if exp.After(now) {
		return e.value, Hit
	}

	exp = e.born.Add(c.maxTTL)
	if exp.After(now) {
		return e.value, GonnaMissy
	}

	c.lock.Lock()
	delete(c.tickets, clientID)
	c.lock.Unlock()
	return nil, Miss
}

func (c *Cache) LoadByAlias(alias string) (*string, Status) {
	c.lock.RLock()
	clientID, ok := c.aliases[alias]
	c.lock.RUnlock()
	if !ok {
		return nil, Miss
	}

	return c.Load(clientID)
}

func (c *Cache) Store(clientID tvm.ClientID, alias string, value *string) {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.aliases[alias] = clientID
	c.tickets[clientID] = entry{
		value: value,
		born:  time.Now(),
	}
}