Skip to content

Commit e442223

Browse files
committed
add lru package for game cache; fix data races
1 parent 53ff407 commit e442223

File tree

5 files changed

+41
-34
lines changed

5 files changed

+41
-34
lines changed

Diff for: Dockerfile-dev

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
FROM golang:alpine
22

33
RUN apk add --no-cache git
4+
RUN apk add --no-cache git make build-base
45

56
COPY go.sum /opt/go.sum
67
COPY go.mod /opt/go.mod

Diff for: docker-compose.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ services:
1515
REGISTRATION_CODE: foobar
1616
MAILGUN_KEY: ${MAILGUN_KEY:-default}
1717
REDIS_URL: "redis://redis:6379"
18+
GORACE: history_size=7
1819
volumes:
1920
- .:/opt/program:rw
20-
command: sh -c "go run cmd/liwords-api/*.go"
21+
command: sh -c "go run -race cmd/liwords-api/*.go"
2122
depends_on: ["db", "proxy", "nats", "socket", "redis"]
2223
networks:
2324
- aeronet
@@ -56,7 +57,7 @@ services:
5657
NATS_URL: nats://nats:4222
5758
volumes:
5859
- ../liwords-socket:/opt/program:rw
59-
command: sh -c "go run cmd/socketsrv/*.go"
60+
command: sh -c "go run -race cmd/socketsrv/*.go"
6061
networks:
6162
- aeronet
6263
ports:

Diff for: go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ go 1.14
44

55
require (
66
github.com/dgrijalva/jwt-go v3.2.0+incompatible
7-
github.com/domino14/macondo v0.4.5-0.20200923032030-a394644e70e2
7+
github.com/domino14/macondo v0.4.5-0.20200926023632-90e54ff6b544
88
github.com/golang/protobuf v1.4.2
99
github.com/gomodule/redigo v1.8.2
10+
github.com/hashicorp/golang-lru v0.5.4
1011
github.com/jinzhu/gorm v1.9.16
1112
github.com/justinas/alice v1.2.0
1213
github.com/kr/text v0.2.0 // indirect

Diff for: go.sum

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6RO
2424
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
2525
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
2626
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
27-
github.com/domino14/macondo v0.4.5-0.20200923032030-a394644e70e2 h1:h6vf/XBAB7DSM0NNsH3+O2vB0iXLX0fr3W4a+Npra4A=
28-
github.com/domino14/macondo v0.4.5-0.20200923032030-a394644e70e2/go.mod h1:GAib5Wo/e78Llg4a/10xfCgo0Tz25NPbqU/sQSr0bwo=
27+
github.com/domino14/macondo v0.4.5-0.20200926023632-90e54ff6b544 h1:k4DmJ2pIH1zd+nLn7+v4DEx/6YDGIPfofL0qsA59K6o=
28+
github.com/domino14/macondo v0.4.5-0.20200926023632-90e54ff6b544/go.mod h1:GAib5Wo/e78Llg4a/10xfCgo0Tz25NPbqU/sQSr0bwo=
2929
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
3030
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
3131
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
@@ -65,6 +65,8 @@ github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
6565
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
6666
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
6767
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
68+
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
69+
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
6870
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
6971
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
7072
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=

Diff for: pkg/stores/game/cache.go

+31-29
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package game
22

33
import (
44
"context"
5-
"sync"
65
"time"
76

87
"github.com/domino14/liwords/pkg/entity"
98
pb "github.com/domino14/liwords/rpc/api/proto/realtime"
9+
lru "github.com/hashicorp/golang-lru"
1010
"github.com/rs/zerolog/log"
1111
)
1212

@@ -21,12 +21,17 @@ type backingStore interface {
2121
Disconnect()
2222
}
2323

24+
const (
25+
// Assume every game takes up roughly 50KB in memory
26+
// This is roughly 200 MB and allows for 4000 simultaneous games.
27+
// We will want to increase this as we grow (as well as the size of our container)
28+
CacheCap = 4000
29+
)
30+
2431
// Cache will reside in-memory, and will be per-node. If we add more nodes
2532
// we will need to make sure only the right nodes respond to game requests.
2633
type Cache struct {
27-
sync.Mutex
28-
29-
games map[string]*entity.Game
34+
cache *lru.Cache
3035
activeGames []*pb.GameMeta
3136

3237
activeGamesTTL time.Duration
@@ -36,9 +41,17 @@ type Cache struct {
3641
}
3742

3843
func NewCache(backing backingStore) *Cache {
44+
45+
lrucache, err := lru.New(CacheCap)
46+
if err != nil {
47+
panic(err)
48+
}
49+
3950
return &Cache{
4051
backing: backing,
41-
games: make(map[string]*entity.Game),
52+
cache: lrucache,
53+
54+
// games: make(map[string]*entity.Game),
4255
// Have a non-trivial TTL for the cache of active games.
4356
// XXX: This might act poorly if the following happens within the TTL:
4457
// - active games gets cached
@@ -54,38 +67,29 @@ func NewCache(backing backingStore) *Cache {
5467
}
5568
}
5669

57-
// Exists lets us know whether the game is in the cache
58-
func (c *Cache) Exists(ctx context.Context, id string) bool {
59-
_, ok := c.games[id]
60-
return ok
61-
}
62-
6370
// Unload unloads the game from the cache
6471
func (c *Cache) Unload(ctx context.Context, id string) {
65-
delete(c.games, id)
72+
c.cache.Remove(id)
6673
}
6774

6875
// SetGameEventChan sets the game event channel to the passed in channel.
6976
func (c *Cache) SetGameEventChan(ch chan<- *entity.EventWrapper) {
7077
c.backing.SetGameEventChan(ch)
7178
}
7279

73-
// Get gets a game from the cache. It doesn't try to get it from the backing
74-
// store, if it can't find it in the cache.
80+
// Get gets a game from the cache.. it loads it into the cache if it's not there.
7581
func (c *Cache) Get(ctx context.Context, id string) (*entity.Game, error) {
76-
g, ok := c.games[id]
77-
if !ok {
78-
79-
g, err := c.backing.Get(ctx, id)
80-
if err != nil {
81-
return nil, err
82-
}
83-
c.Lock()
84-
defer c.Unlock()
85-
c.games[id] = g
86-
return g, nil
82+
g, ok := c.cache.Get(id)
83+
if ok && g != nil {
84+
return g.(*entity.Game), nil
8785
}
88-
return g, nil
86+
log.Info().Str("gameid", id).Msg("not-in-cache")
87+
uncachedGame, err := c.backing.Get(ctx, id)
88+
if err == nil {
89+
c.cache.Add(id, uncachedGame)
90+
}
91+
return uncachedGame, err
92+
8993
}
9094

9195
// Set sets a game in the cache, AND in the backing store. This ensures if the
@@ -113,9 +117,7 @@ func (c *Cache) setOrCreate(ctx context.Context, game *entity.Game, isNew bool)
113117
if err != nil {
114118
return err
115119
}
116-
c.Lock()
117-
defer c.Unlock()
118-
c.games[gameID] = game
120+
c.cache.Add(gameID, game)
119121
return nil
120122
}
121123

0 commit comments

Comments
 (0)