Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5f9744b

Browse files
committed
pr comments
1 parent 201347f commit 5f9744b

File tree

6 files changed

+228
-284
lines changed

6 files changed

+228
-284
lines changed

coderd/cryptokeys/dbkeycache.go

Lines changed: 78 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cryptokeys
22

33
import (
44
"context"
5-
"database/sql"
65
"sync"
76
"time"
87

@@ -23,9 +22,10 @@ type DBCache struct {
2322
clock quartz.Clock
2423

2524
// The following are initialized by NewDBCache.
26-
cacheMu sync.RWMutex
27-
cache map[int32]database.CryptoKey
25+
keysMu sync.RWMutex
26+
keys map[int32]database.CryptoKey
2827
latestKey database.CryptoKey
28+
fetched chan struct{}
2929
}
3030

3131
type DBCacheOption func(*DBCache)
@@ -39,131 +39,118 @@ func WithDBCacheClock(clock quartz.Clock) DBCacheOption {
3939
// NewDBCache creates a new DBCache. It starts a background
4040
// process that periodically refreshes the cache. The context should
4141
// be canceled to stop the background process.
42-
func NewDBCache(ctx context.Context, logger slog.Logger, db database.Store, feature database.CryptoKeyFeature, opts ...func(*DBCache)) (*DBCache, error) {
42+
func NewDBCache(ctx context.Context, logger slog.Logger, db database.Store, feature database.CryptoKeyFeature, opts ...func(*DBCache)) *DBCache {
4343
d := &DBCache{
4444
db: db,
4545
feature: feature,
4646
clock: quartz.NewReal(),
4747
logger: logger,
48+
fetched: make(chan struct{}),
4849
}
50+
4951
for _, opt := range opts {
5052
opt(d)
5153
}
5254

53-
cache, latest, err := d.newCache(ctx)
54-
if err != nil {
55-
return nil, xerrors.Errorf("new cache: %w", err)
56-
}
57-
d.cache, d.latestKey = cache, latest
58-
59-
go d.refresh(ctx)
60-
return d, nil
55+
go d.clear(ctx)
56+
return d
6157
}
6258

63-
// Version returns the CryptoKey with the given sequence number, provided that
64-
// it is neither deleted nor has breached its deletion date.
65-
func (d *DBCache) Version(ctx context.Context, sequence int32) (codersdk.CryptoKey, error) {
66-
now := d.clock.Now().UTC()
67-
d.cacheMu.RLock()
68-
key, ok := d.cache[sequence]
69-
d.cacheMu.RUnlock()
59+
// Verifying returns the CryptoKey with the given sequence number, provided that
60+
// it is neither deleted nor has breached its deletion date. It should only be
61+
// used for verifying or decrypting payloads. To sign/encrypt call Signing.
62+
func (d *DBCache) Verifying(ctx context.Context, sequence int32) (codersdk.CryptoKey, error) {
63+
now := d.clock.Now()
64+
d.keysMu.RLock()
65+
key, ok := d.keys[sequence]
66+
d.keysMu.RUnlock()
7067
if ok {
7168
return checkKey(key, now)
7269
}
7370

74-
d.cacheMu.Lock()
75-
defer d.cacheMu.Unlock()
71+
d.keysMu.Lock()
72+
defer d.keysMu.Unlock()
7673

77-
key, ok = d.cache[sequence]
74+
key, ok = d.keys[sequence]
7875
if ok {
7976
return checkKey(key, now)
8077
}
8178

82-
key, err := d.db.GetCryptoKeyByFeatureAndSequence(ctx, database.GetCryptoKeyByFeatureAndSequenceParams{
83-
Feature: d.feature,
84-
Sequence: sequence,
85-
})
86-
if xerrors.Is(err, sql.ErrNoRows) {
87-
return codersdk.CryptoKey{}, ErrKeyNotFound
88-
}
79+
cache, latest, err := d.fetch(ctx)
8980
if err != nil {
90-
return codersdk.CryptoKey{}, err
91-
}
92-
93-
if !key.CanVerify(now) {
94-
return codersdk.CryptoKey{}, ErrKeyInvalid
81+
return codersdk.CryptoKey{}, xerrors.Errorf("new cache: %w", err)
9582
}
83+
d.keys, d.latestKey = cache, latest
9684

97-
// If this key is valid for signing then mark it as the latest key.
98-
if key.CanSign(now) && key.Sequence > d.latestKey.Sequence {
99-
d.latestKey = key
85+
key, ok = d.keys[sequence]
86+
if !ok {
87+
return codersdk.CryptoKey{}, ErrKeyNotFound
10088
}
10189

102-
d.cache[sequence] = key
103-
104-
return db2sdk.CryptoKey(key), nil
90+
return checkKey(key, now)
10591
}
10692

107-
// Latest returns the latest valid key for signing. A valid key is one that is
93+
// Signing returns the latest valid key for signing. A valid key is one that is
10894
// both past its start time and before its deletion time.
109-
func (d *DBCache) Latest(ctx context.Context) (codersdk.CryptoKey, error) {
110-
d.cacheMu.RLock()
95+
func (d *DBCache) Signing(ctx context.Context) (codersdk.CryptoKey, error) {
96+
d.keysMu.RLock()
11197
latest := d.latestKey
112-
d.cacheMu.RUnlock()
98+
d.keysMu.RUnlock()
11399

114-
now := d.clock.Now().UTC()
100+
now := d.clock.Now()
115101
if latest.CanSign(now) {
116102
return db2sdk.CryptoKey(latest), nil
117103
}
118104

119-
d.cacheMu.Lock()
120-
defer d.cacheMu.Unlock()
105+
d.keysMu.Lock()
106+
defer d.keysMu.Unlock()
121107

122-
if latest.CanSign(now) {
123-
return db2sdk.CryptoKey(latest), nil
108+
if d.latestKey.CanSign(now) {
109+
return db2sdk.CryptoKey(d.latestKey), nil
124110
}
125111

126112
// Refetch all keys for this feature so we can find the latest valid key.
127-
cache, latest, err := d.newCache(ctx)
113+
cache, latest, err := d.fetch(ctx)
128114
if err != nil {
129-
return codersdk.CryptoKey{}, xerrors.Errorf("new cache: %w", err)
115+
return codersdk.CryptoKey{}, xerrors.Errorf("fetch: %w", err)
130116
}
117+
d.keys, d.latestKey = cache, latest
131118

132-
if len(cache) == 0 {
133-
return codersdk.CryptoKey{}, ErrKeyNotFound
134-
}
135-
136-
if !latest.CanSign(now) {
137-
return codersdk.CryptoKey{}, ErrKeyInvalid
138-
}
139-
140-
d.cache, d.latestKey = cache, latest
141-
142-
return db2sdk.CryptoKey(latest), nil
119+
return db2sdk.CryptoKey(d.latestKey), nil
143120
}
144121

145-
func (d *DBCache) refresh(ctx context.Context) {
146-
d.clock.TickerFunc(ctx, time.Minute*10, func() error {
147-
cache, latest, err := d.newCache(ctx)
148-
if err != nil {
149-
d.logger.Error(ctx, "failed to refresh cache", slog.Error(err))
150-
return nil
122+
func (d *DBCache) clear(ctx context.Context) {
123+
for {
124+
fired := make(chan struct{})
125+
timer := d.clock.AfterFunc(time.Minute*10, func() {
126+
defer close(fired)
127+
128+
// There's a small window where the timer fires as we're fetching
129+
// keys that could result in us immediately invalidating the cache that we just populated.
130+
d.keysMu.Lock()
131+
defer d.keysMu.Unlock()
132+
d.keys = nil
133+
d.latestKey = database.CryptoKey{}
134+
})
135+
136+
select {
137+
case <-ctx.Done():
138+
return
139+
case <-d.fetched:
140+
timer.Stop()
141+
case <-fired:
151142
}
152-
d.cacheMu.Lock()
153-
defer d.cacheMu.Unlock()
154-
155-
d.cache, d.latestKey = cache, latest
156-
return nil
157-
})
143+
}
158144
}
159145

160-
// newCache fetches all keys for the given feature and determines the latest key.
161-
func (d *DBCache) newCache(ctx context.Context) (map[int32]database.CryptoKey, database.CryptoKey, error) {
162-
now := d.clock.Now().UTC()
146+
// fetch fetches all keys for the given feature and determines the latest key.
147+
func (d *DBCache) fetch(ctx context.Context) (map[int32]database.CryptoKey, database.CryptoKey, error) {
148+
now := d.clock.Now()
163149
keys, err := d.db.GetCryptoKeysByFeature(ctx, d.feature)
164150
if err != nil {
165151
return nil, database.CryptoKey{}, xerrors.Errorf("get crypto keys by feature: %w", err)
166152
}
153+
167154
cache := make(map[int32]database.CryptoKey)
168155
var latest database.CryptoKey
169156
for _, key := range keys {
@@ -173,6 +160,20 @@ func (d *DBCache) newCache(ctx context.Context) (map[int32]database.CryptoKey, d
173160
}
174161
}
175162

163+
if len(cache) == 0 {
164+
return nil, database.CryptoKey{}, ErrKeyNotFound
165+
}
166+
167+
if !latest.CanSign(now) {
168+
return nil, database.CryptoKey{}, ErrKeyInvalid
169+
}
170+
171+
select {
172+
case <-ctx.Done():
173+
return nil, database.CryptoKey{}, ctx.Err()
174+
case d.fetched <- struct{}{}:
175+
}
176+
176177
return cache, latest, nil
177178
}
178179

0 commit comments

Comments
 (0)