@@ -7,22 +7,29 @@ package pgwire
7
7
8
8
import (
9
9
"context"
10
+ "time"
10
11
11
12
"github.com/cockroachdb/cockroach/pkg/security/username"
12
13
"github.com/cockroachdb/cockroach/pkg/sql"
14
+ "github.com/cockroachdb/cockroach/pkg/util/cache"
13
15
"github.com/cockroachdb/cockroach/pkg/util/log"
14
16
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
15
17
"github.com/cockroachdb/cockroach/pkg/util/syncutil/singleflight"
18
+ "github.com/cockroachdb/cockroach/pkg/util/timeutil"
16
19
)
17
20
18
21
// lastLoginUpdater handles updating the last login time for SQL users with
19
- // deduplication via singleflight to reduce concurrent updates.
22
+ // deduplication via singleflight to reduce concurrent updates. It also uses
23
+ // an LRU cache so that a user never is updated more than once every 10 minutes.
20
24
type lastLoginUpdater struct {
21
25
// group ensures that there is at most one last login time update
22
26
// in-flight at any given time.
23
27
group * singleflight.Group
24
28
// execCfg is the executor configuration used for running update operations.
25
29
execCfg * sql.ExecutorConfig
30
+ // lastUpdateCache tracks the last time each user's login time was updated
31
+ // to avoid redundant database updates.
32
+ lastUpdateCache * cache.UnorderedCache
26
33
27
34
mu struct {
28
35
syncutil.Mutex
@@ -32,11 +39,39 @@ type lastLoginUpdater struct {
32
39
}
33
40
}
34
41
42
+ const (
43
+ // lastLoginCacheSize is the maximum number of users to track in the LRU
44
+ // cache.
45
+ lastLoginCacheSize = 100
46
+ // lastLoginEvictionTime is how long to keep entries in the cache before
47
+ // evicting them.
48
+ lastLoginEvictionTime = 10 * time .Minute
49
+ )
50
+
35
51
// newLastLoginUpdater creates a new lastLoginUpdater instance.
36
52
func newLastLoginUpdater (execCfg * sql.ExecutorConfig ) * lastLoginUpdater {
53
+ // Create cache with LRU eviction, limiting to lastLoginCacheSize users and entries older than lastLoginEvictionTime.
54
+ cacheConfig := cache.Config {
55
+ Policy : cache .CacheLRU ,
56
+ ShouldEvict : func (size int , key , value interface {}) bool {
57
+ // Evict if we have more than lastLoginCacheSize entries or if the entry
58
+ // is older than lastLoginEvictionTime.
59
+ if size > lastLoginCacheSize {
60
+ return true
61
+ }
62
+ lastUpdate , ok := value .(time.Time )
63
+ if ! ok {
64
+ // Evict invalid entries also.
65
+ return true
66
+ }
67
+ return timeutil .Since (lastUpdate ) > lastLoginEvictionTime
68
+ },
69
+ }
70
+
37
71
u := & lastLoginUpdater {
38
- group : singleflight .NewGroup ("update last login time" , "" ),
39
- execCfg : execCfg ,
72
+ group : singleflight .NewGroup ("update last login time" , "" ),
73
+ execCfg : execCfg ,
74
+ lastUpdateCache : cache .NewUnorderedCache (cacheConfig ),
40
75
}
41
76
u .mu .pendingUsers = make (map [username.SQLUsername ]struct {})
42
77
return u
@@ -51,6 +86,16 @@ func (u *lastLoginUpdater) updateLastLoginTime(ctx context.Context, dbUser usern
51
86
return
52
87
}
53
88
89
+ // Check if we recently updated this user's login time.
90
+ if lastUpdate , ok := u .lastUpdateCache .Get (dbUser ); ok {
91
+ if lastUpdateTime , ok := lastUpdate .(time.Time ); ok {
92
+ // Only update if it's been more than lastLoginEvictionTime since the last update.
93
+ if timeutil .Since (lastUpdateTime ) < lastLoginEvictionTime {
94
+ return
95
+ }
96
+ }
97
+ }
98
+
54
99
// Use singleflight to ensure at most one last login time update batch
55
100
// is in-flight at any given time.
56
101
future , leader := u .group .DoChan (ctx , "UpdateLastLoginTime" ,
@@ -96,10 +141,21 @@ func (u *lastLoginUpdater) processPendingUpdates(ctx context.Context) error {
96
141
u .mu .pendingUsers = make (map [username.SQLUsername ]struct {})
97
142
u .mu .Unlock ()
98
143
144
+ if len (users ) == 0 {
145
+ return nil
146
+ }
147
+
99
148
// Update last login time for all pending users in a single query.
100
149
err := sql .UpdateLastLoginTime (ctx , u .execCfg , users )
101
150
if err != nil {
102
151
return err
103
152
}
153
+
154
+ // Update the cache with the current time for all successfully updated users.
155
+ now := timeutil .Now ()
156
+ for _ , user := range users {
157
+ u .lastUpdateCache .Add (user , now )
158
+ }
159
+
104
160
return nil
105
161
}
0 commit comments