-
Notifications
You must be signed in to change notification settings - Fork 921
/
db.go
157 lines (127 loc) · 3.8 KB
/
db.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
155
156
157
package configstore
import (
"errors"
"reflect"
"strconv"
"time"
"github.com/botlabs-gg/yagpdb/v2/common"
"github.com/botlabs-gg/yagpdb/v2/common/pubsub"
"github.com/jinzhu/gorm"
"github.com/karlseguin/ccache"
"golang.org/x/net/context"
)
var (
ErrNotFound = errors.New("Config not found")
ErrInvalidConfig = errors.New("Invalid config")
SQL = &Postgres{}
Cached = NewCached()
storages = make(map[reflect.Type]Storage)
logger = common.GetFixedPrefixLogger("configstore")
)
func RegisterConfig(stor Storage, conf GuildConfig) {
storages[reflect.TypeOf(conf)] = stor
}
func StrID(id int64) string {
return strconv.FormatInt(id, 10)
}
func KeyGuildConfig(guildID int64, configName string) string {
return "guild_config:" + configName + ":" + StrID(guildID)
}
type GuildConfigModel struct {
GuildID int64 `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
}
func (gm *GuildConfigModel) GetUpdatedAt() time.Time {
return gm.UpdatedAt
}
func (gm *GuildConfigModel) GetGuildID() int64 {
return gm.GuildID
}
type GuildConfig interface {
GetGuildID() int64
GetUpdatedAt() time.Time
GetName() string
}
type PostFetchHandler interface {
// Called after retrieving from underlying storage, before being put in cache
// use this for any post processing etc..
PostFetch()
}
type Storage interface {
// GetGuildConfig returns a GuildConfig item from db
GetGuildConfig(ctx context.Context, guildID int64, dest GuildConfig) (err error)
// SetGuildConfig saves the GuildConfig struct
SetGuildConfig(ctx context.Context, conf GuildConfig) error
// SetIfLatest saves it only if the passedLatest time is the latest version
// SetIfLatest(ctx context.Context, conf GuildConfig) (updated bool, err error)
}
type CachedStorage struct {
cache *ccache.Cache
}
func NewCached() *CachedStorage {
return &CachedStorage{
cache: ccache.New(ccache.Configure().MaxSize(25000)),
}
}
func (c *CachedStorage) InvalidateCache(guildID int64, config string) {
c.cache.Delete(KeyGuildConfig(guildID, config))
}
func (c *CachedStorage) GetGuildConfig(ctx context.Context, guildID int64, dest GuildConfig) error {
cached := true
item, err := c.cache.Fetch(KeyGuildConfig(guildID, dest.GetName()), time.Minute*10, func() (interface{}, error) {
underlying, ok := storages[reflect.TypeOf(dest)]
if !ok {
return nil, ErrInvalidConfig
}
cached = false
err := underlying.GetGuildConfig(ctx, guildID, dest)
if err == gorm.ErrRecordNotFound {
err = ErrNotFound
}
// Call the postfetchhandler
if err == nil {
if pfh, ok := dest.(PostFetchHandler); ok {
pfh.PostFetch()
}
}
return dest, err
})
// If it was loaded from cache, we need to load it into "dest" ourselves
if err == nil && cached {
reflect.Indirect(reflect.ValueOf(dest)).Set(reflect.Indirect(reflect.ValueOf(item.Value())))
}
return err
}
func InitDatabases() {
pubsub.AddHandler("invalidate_guild_config_cache", HandleInvalidateCacheEvt, "")
}
func HandleInvalidateCacheEvt(event *pubsub.Event) {
conf, ok := event.Data.(*string)
if !ok {
logger.Error("Invalid invalidate guild config cache event")
return
}
tg, _ := strconv.ParseInt(event.TargetGuild, 10, 64)
Cached.InvalidateCache(tg, *conf)
}
// InvalidateGuildCache is a helper that both instantly invalides the local application cache
// As well as sending the pusub event
func InvalidateGuildCache(guildID interface{}, conf GuildConfig) {
var gID int64
switch t := guildID.(type) {
case int64:
gID = t
case string:
gID, _ = strconv.ParseInt(t, 10, 64)
case GuildConfig:
gID = t.GetGuildID()
default:
panic("Invalid guildID passed to InvalidateGuildCache")
}
Cached.InvalidateCache(gID, conf.GetName())
err := pubsub.Publish("invalidate_guild_config_cache", gID, conf.GetName())
if err != nil {
logger.WithError(err).Error("FAILED INVALIDATING CACHE")
}
}