Skip to content

Commit 4f1538d

Browse files
committed
feat(cache): enhance cache strategy of transaction
1 parent 23c1efa commit 4f1538d

File tree

11 files changed

+588
-195
lines changed

11 files changed

+588
-195
lines changed

template/cache.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,41 @@ import (
55
"database/sql/driver"
66
"fmt"
77
"strings"
8+
"sync"
9+
"sync/atomic"
10+
"time"
11+
12+
"github.com/motoki317/sc"
13+
"github.com/traP-jp/isuc/domains"
814
)
915

16+
type cacheWithInfo struct {
17+
*sc.Cache[string, *cacheRows]
18+
query string
19+
info domains.CachePlanSelectQuery
20+
uniqueOnly bool // if true, query is like "SELECT * FROM table WHERE pk = ?"
21+
lastUpdate atomic.Int64 // time.Time.UnixNano()
22+
lastUpdateByKey syncMap[int64]
23+
}
24+
25+
func (c *cacheWithInfo) updateTx() {
26+
c.lastUpdate.Store(time.Now().UnixNano())
27+
}
28+
29+
func (c *cacheWithInfo) updateByKeyTx(key string) {
30+
c.lastUpdateByKey.Store(key, time.Now().UnixNano())
31+
}
32+
33+
func (c *cacheWithInfo) isNewerThan(key string, t int64) bool {
34+
if c.lastUpdate.Load() > t {
35+
return true
36+
}
37+
if update, ok := c.lastUpdateByKey.Load(key); ok && update > t {
38+
return true
39+
}
40+
return false
41+
}
42+
1043
type (
1144
queryKey struct{}
1245
stmtKey struct{}
@@ -18,7 +51,7 @@ type (
1851
func ExportMetrics() string {
1952
res := ""
2053
for query, cache := range caches {
21-
stats := cache.cache.Stats()
54+
stats := cache.Stats()
2255
progress := "["
2356
for i := 0; i < 20; i++ {
2457
if i < int(stats.HitRatio()*20) {
@@ -43,7 +76,7 @@ type CacheStats struct {
4376
func ExportCacheStats() map[string]CacheStats {
4477
res := make(map[string]CacheStats)
4578
for query, cache := range caches {
46-
stats := cache.cache.Stats()
79+
stats := cache.Stats()
4780
res[query] = CacheStats{
4881
Query: query,
4982
HitRatio: stats.HitRatio(),
@@ -56,7 +89,7 @@ func ExportCacheStats() map[string]CacheStats {
5689

5790
func PurgeAllCaches() {
5891
for _, cache := range caches {
59-
cache.cache.Purge()
92+
cache.Purge()
6093
}
6194
}
6295

@@ -109,3 +142,20 @@ func replaceFn(ctx context.Context, key string) (*cacheRows, error) {
109142
}
110143
return cacheRows.clone(), nil
111144
}
145+
146+
type syncMap[T any] struct {
147+
m sync.Map
148+
}
149+
150+
func (m *syncMap[T]) Load(key string) (T, bool) {
151+
var zero T
152+
v, ok := m.m.Load(key)
153+
if !ok {
154+
return zero, false
155+
}
156+
return v.(T), true
157+
}
158+
159+
func (m *syncMap[T]) Store(key string, value T) {
160+
m.m.Store(key, value)
161+
}

template/cache.tmpl

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,41 @@ import (
55
"database/sql/driver"
66
"fmt"
77
"strings"
8+
"sync"
9+
"sync/atomic"
10+
"time"
11+
12+
"github.com/motoki317/sc"
13+
"github.com/traP-jp/isuc/domains"
814
)
915

16+
type cacheWithInfo struct {
17+
*sc.Cache[string, *cacheRows]
18+
query string
19+
info domains.CachePlanSelectQuery
20+
uniqueOnly bool // if true, query is like "SELECT * FROM table WHERE pk = ?"
21+
lastUpdate atomic.Int64 // time.Time.UnixNano()
22+
lastUpdateByKey syncMap[int64]
23+
}
24+
25+
func (c *cacheWithInfo) updateTx() {
26+
c.lastUpdate.Store(time.Now().UnixNano())
27+
}
28+
29+
func (c *cacheWithInfo) updateByKeyTx(key string) {
30+
c.lastUpdateByKey.Store(key, time.Now().UnixNano())
31+
}
32+
33+
func (c *cacheWithInfo) isNewerThan(key string, t int64) bool {
34+
if c.lastUpdate.Load() > t {
35+
return true
36+
}
37+
if update, ok := c.lastUpdateByKey.Load(key); ok && update > t {
38+
return true
39+
}
40+
return false
41+
}
42+
1043
type (
1144
queryKey struct{}
1245
stmtKey struct{}
@@ -18,7 +51,7 @@ type (
1851
func ExportMetrics() string {
1952
res := ""
2053
for query, cache := range caches {
21-
stats := cache.cache.Stats()
54+
stats := cache.Stats()
2255
progress := "["
2356
for i := 0; i < 20; i++ {
2457
if i < int(stats.HitRatio()*20) {
@@ -43,7 +76,7 @@ type CacheStats struct {
4376
func ExportCacheStats() map[string]CacheStats {
4477
res := make(map[string]CacheStats)
4578
for query, cache := range caches {
46-
stats := cache.cache.Stats()
79+
stats := cache.Stats()
4780
res[query] = CacheStats{
4881
Query: query,
4982
HitRatio: stats.HitRatio(),
@@ -56,7 +89,7 @@ func ExportCacheStats() map[string]CacheStats {
5689

5790
func PurgeAllCaches() {
5891
for _, cache := range caches {
59-
cache.cache.Purge()
92+
cache.Purge()
6093
}
6194
}
6295

@@ -109,3 +142,20 @@ func replaceFn(ctx context.Context, key string) (*cacheRows, error) {
109142
}
110143
return cacheRows.clone(), nil
111144
}
145+
146+
type syncMap[T any] struct {
147+
m sync.Map
148+
}
149+
150+
func (m *syncMap[T]) Load(key string) (T, bool) {
151+
var zero T
152+
v, ok := m.m.Load(key)
153+
if !ok {
154+
return zero, false
155+
}
156+
return v.(T), true
157+
}
158+
159+
func (m *syncMap[T]) Store(key string, value T) {
160+
m.m.Store(key, value)
161+
}

template/driver.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,18 @@ func init() {
5151

5252
conditions := query.Select.Conditions
5353
if isSingleUniqueCondition(conditions, query.Select.Table) {
54-
caches[normalized] = cacheWithInfo{
54+
caches[normalized] = &cacheWithInfo{
55+
Cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
5556
query: normalized,
5657
info: *query.Select,
57-
cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
5858
uniqueOnly: true,
5959
}
6060
continue
6161
}
62-
caches[query.Query] = cacheWithInfo{
62+
caches[query.Query] = &cacheWithInfo{
63+
Cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
6364
query: query.Query,
6465
info: *query.Select,
65-
cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
6666
uniqueOnly: false,
6767
}
6868

@@ -105,7 +105,8 @@ var (
105105
type cacheConn struct {
106106
inner driver.Conn
107107
tx bool
108-
cleanUp []func()
108+
txStart int64 // time.Time.UnixNano()
109+
cleanUp cleanUpTask
109110
}
110111

111112
func (c *cacheConn) Prepare(rawQuery string) (driver.Stmt, error) {
@@ -148,23 +149,26 @@ func (c *cacheConn) Begin() (driver.Tx, error) {
148149
return nil, err
149150
}
150151
c.tx = true
152+
c.txStart = time.Now().UnixNano()
151153
return &cacheTx{conn: c, inner: inner}, nil
152154
}
153155

154156
func (c *cacheConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
157+
var inner driver.Tx
158+
var err error
155159
if i, ok := c.inner.(driver.ConnBeginTx); ok {
156-
inner, err := i.BeginTx(ctx, opts)
160+
inner, err = i.BeginTx(ctx, opts)
161+
if err != nil {
162+
return nil, err
163+
}
164+
} else {
165+
inner, err = c.inner.Begin()
157166
if err != nil {
158167
return nil, err
159168
}
160-
c.tx = true
161-
return &cacheTx{conn: c, inner: inner}, nil
162-
}
163-
inner, err := c.inner.Begin()
164-
if err != nil {
165-
return nil, err
166169
}
167170
c.tx = true
171+
c.txStart = time.Now().UnixNano()
168172
return &cacheTx{conn: c, inner: inner}, nil
169173
}
170174

@@ -185,18 +189,21 @@ type cacheTx struct {
185189
func (t *cacheTx) Commit() error {
186190
t.conn.tx = false
187191
defer func() {
188-
for _, c := range t.conn.cleanUp {
189-
c()
192+
for _, c := range t.conn.cleanUp.purge {
193+
c.Purge()
194+
}
195+
for _, forget := range t.conn.cleanUp.forget {
196+
forget.cache.Forget(forget.key)
190197
}
191-
t.conn.cleanUp = t.conn.cleanUp[:0]
198+
t.conn.cleanUp.reset()
192199
}()
193200
return t.inner.Commit()
194201
}
195202

196203
func (t *cacheTx) Rollback() error {
197204
t.conn.tx = false
198205
// no need to clean up
199-
t.conn.cleanUp = nil
206+
t.conn.cleanUp.reset()
200207
return t.inner.Rollback()
201208
}
202209

template/driver.tmpl

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,18 @@ func init() {
5050

5151
conditions := query.Select.Conditions
5252
if isSingleUniqueCondition(conditions, query.Select.Table) {
53-
caches[normalized] = cacheWithInfo{
53+
caches[normalized] = &cacheWithInfo{
54+
Cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
5455
query: normalized,
5556
info: *query.Select,
56-
cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
5757
uniqueOnly: true,
5858
}
5959
continue
6060
}
61-
caches[query.Query] = cacheWithInfo{
61+
caches[query.Query] = &cacheWithInfo{
62+
Cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
6263
query: query.Query,
6364
info: *query.Select,
64-
cache: sc.NewMust(replaceFn, 10*time.Minute, 10*time.Minute),
6565
uniqueOnly: false,
6666
}
6767

@@ -104,7 +104,8 @@ var (
104104
type cacheConn struct {
105105
inner driver.Conn
106106
tx bool
107-
cleanUp []func()
107+
txStart int64 // time.Time.UnixNano()
108+
cleanUp cleanUpTask
108109
}
109110

110111
func (c *cacheConn) Prepare(rawQuery string) (driver.Stmt, error) {
@@ -147,23 +148,26 @@ func (c *cacheConn) Begin() (driver.Tx, error) {
147148
return nil, err
148149
}
149150
c.tx = true
151+
c.txStart = time.Now().UnixNano()
150152
return &cacheTx{conn: c, inner: inner}, nil
151153
}
152154

153155
func (c *cacheConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
156+
var inner driver.Tx
157+
var err error
154158
if i, ok := c.inner.(driver.ConnBeginTx); ok {
155-
inner, err := i.BeginTx(ctx, opts)
159+
inner, err = i.BeginTx(ctx, opts)
160+
if err != nil {
161+
return nil, err
162+
}
163+
} else {
164+
inner, err = c.inner.Begin()
156165
if err != nil {
157166
return nil, err
158167
}
159-
c.tx = true
160-
return &cacheTx{conn: c, inner: inner}, nil
161-
}
162-
inner, err := c.inner.Begin()
163-
if err != nil {
164-
return nil, err
165168
}
166169
c.tx = true
170+
c.txStart = time.Now().UnixNano()
167171
return &cacheTx{conn: c, inner: inner}, nil
168172
}
169173

@@ -184,18 +188,21 @@ type cacheTx struct {
184188
func (t *cacheTx) Commit() error {
185189
t.conn.tx = false
186190
defer func() {
187-
for _, c := range t.conn.cleanUp {
188-
c()
191+
for _, c := range t.conn.cleanUp.purge {
192+
c.Purge()
193+
}
194+
for _, forget := range t.conn.cleanUp.forget {
195+
forget.cache.Forget(forget.key)
189196
}
190-
t.conn.cleanUp = t.conn.cleanUp[:0]
197+
t.conn.cleanUp.reset()
191198
}()
192199
return t.inner.Commit()
193200
}
194201

195202
func (t *cacheTx) Rollback() error {
196203
t.conn.tx = false
197204
// no need to clean up
198-
t.conn.cleanUp = nil
205+
t.conn.cleanUp.reset()
199206
return t.inner.Rollback()
200207
}
201208

0 commit comments

Comments
 (0)