/
cache.go
154 lines (126 loc) · 4.69 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
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package tournament
import (
"context"
"sync"
"github.com/domino14/liwords/pkg/entity"
lru "github.com/hashicorp/golang-lru"
"github.com/rs/zerolog/log"
pb "github.com/domino14/liwords/rpc/api/proto/tournament_service"
)
type backingStore interface {
Get(ctx context.Context, id string) (*entity.Tournament, error)
GetBySlug(ctx context.Context, id string) (*entity.Tournament, error)
Set(context.Context, *entity.Tournament) error
Create(context.Context, *entity.Tournament) error
GetRecentGames(ctx context.Context, tourneyID string, numGames int, offset int) (*pb.RecentGamesResponse, error)
Disconnect()
SetTournamentEventChan(c chan<- *entity.EventWrapper)
TournamentEventChan() chan<- *entity.EventWrapper
GetRecentClubSessions(ctx context.Context, clubID string, numSessions int, offset int) (*pb.ClubSessionsResponse, error)
ListAllIDs(context.Context) ([]string, error)
AddRegistrants(ctx context.Context, tid string, userIDs []string, division string) error
RemoveRegistrants(ctx context.Context, tid string, userIDs []string, division string) error
RemoveRegistrantsForTournament(ctx context.Context, tid string) error
ActiveTournamentsFor(ctx context.Context, userID string) ([][2]string, error)
}
const (
// Increase this if we ever think we might be holding more than
// 50 tournaments at a time.
CacheCap = 50
)
// Cache will reside in-memory, and will be per-node.
type Cache struct {
sync.Mutex
cache *lru.Cache
backing backingStore
}
func NewCache(backing backingStore) *Cache {
lrucache, err := lru.New(CacheCap)
if err != nil {
panic(err)
}
return &Cache{
backing: backing,
cache: lrucache,
}
}
// Get gets a tournament from the cache. It loads it into the cache if it's not there.
func (c *Cache) Get(ctx context.Context, id string) (*entity.Tournament, error) {
tm, ok := c.cache.Get(id)
if ok && tm != nil {
return tm.(*entity.Tournament), nil
}
// Recheck after locking, to ensure it is still not there.
c.Lock()
defer c.Unlock()
tm, ok = c.cache.Get(id)
if ok && tm != nil {
return tm.(*entity.Tournament), nil
}
log.Info().Str("tournamentid", id).Msg("not-in-cache")
uncachedTournament, err := c.backing.Get(ctx, id)
if err == nil {
c.cache.Add(id, uncachedTournament)
}
return uncachedTournament, err
}
func (c *Cache) GetBySlug(ctx context.Context, id string) (*entity.Tournament, error) {
return c.backing.GetBySlug(ctx, id)
}
// Set sets a tournament in the cache, AND in the backing store. This ensures if the
// node crashes the tournament doesn't just vanish.
func (c *Cache) Set(ctx context.Context, tm *entity.Tournament) error {
return c.setOrCreate(ctx, tm, false)
}
// Create creates the tournament in the cache as well as the store.
func (c *Cache) Create(ctx context.Context, tm *entity.Tournament) error {
return c.setOrCreate(ctx, tm, true)
}
func (c *Cache) setOrCreate(ctx context.Context, tm *entity.Tournament, isNew bool) error {
var err error
if isNew {
err = c.backing.Create(ctx, tm)
} else {
err = c.backing.Set(ctx, tm)
}
if err != nil {
return err
}
c.cache.Add(tm.UUID, tm)
return nil
}
// Unload unloads the tournament from the cache
func (c *Cache) Unload(ctx context.Context, id string) {
c.cache.Remove(id)
}
func (c *Cache) Disconnect() {
c.backing.Disconnect()
}
// SetTournamentEventChan sets the tournament event channel to the passed in channel.
func (c *Cache) SetTournamentEventChan(ch chan<- *entity.EventWrapper) {
c.backing.SetTournamentEventChan(ch)
}
func (c *Cache) TournamentEventChan() chan<- *entity.EventWrapper {
return c.backing.TournamentEventChan()
}
func (c *Cache) GetRecentGames(ctx context.Context, tourneyID string, numGames int, offset int) (*pb.RecentGamesResponse, error) {
return c.backing.GetRecentGames(ctx, tourneyID, numGames, offset)
}
func (c *Cache) GetRecentClubSessions(ctx context.Context, clubID string, numSessions int, offset int) (*pb.ClubSessionsResponse, error) {
return c.backing.GetRecentClubSessions(ctx, clubID, numSessions, offset)
}
func (c *Cache) ListAllIDs(ctx context.Context) ([]string, error) {
return c.backing.ListAllIDs(ctx)
}
func (c *Cache) AddRegistrants(ctx context.Context, tid string, userIDs []string, division string) error {
return c.backing.AddRegistrants(ctx, tid, userIDs, division)
}
func (c *Cache) RemoveRegistrants(ctx context.Context, tid string, userIDs []string, division string) error {
return c.backing.RemoveRegistrants(ctx, tid, userIDs, division)
}
func (c *Cache) RemoveRegistrantsForTournament(ctx context.Context, tid string) error {
return c.backing.RemoveRegistrantsForTournament(ctx, tid)
}
func (c *Cache) ActiveTournamentsFor(ctx context.Context, userID string) ([][2]string, error) {
return c.backing.ActiveTournamentsFor(ctx, userID)
}