4
4
"container/heap"
5
5
"slices"
6
6
"sync"
7
+ "sync/atomic"
7
8
"time"
8
9
9
10
"github.com/negrel/assert"
@@ -13,7 +14,7 @@ import (
13
14
"github.com/rs/zerolog"
14
15
)
15
16
16
- // Service define an in memory session storage.
17
+ // Service define an in- memory session storage.
17
18
type Service interface {
18
19
// InsertSession stores given session in memory. If number of visitor session
19
20
// exceed configured max session per visitor, this function returns false and
@@ -26,7 +27,7 @@ type Service interface {
26
27
// IdentifySession updates stored session visitor id. Updated session and
27
28
// boolean found flag are returned.
28
29
IdentifySession (deviceId uint64 , pageUri uri.Uri , visitorId string ) (event.Session , bool )
29
- // WaitForSession retrieves stored session and returns it. If session is not
30
+ // WaitSession retrieves stored session and returns it. If session is not
30
31
// found, it waits until it is created or timeout.
31
32
// Returned boolean flag is false if wait timed out and returned an empty
32
33
// session.
@@ -46,12 +47,12 @@ func (e *sessionEntry) hasWaiter() bool {
46
47
return e .wait != nil
47
48
}
48
49
49
- func (e * sessionEntry ) isExpired () bool {
50
- return uint32 (time . Now () .Unix ()) >= e .expiry
50
+ func (e * sessionEntry ) isExpired (now time. Time ) bool {
51
+ return uint32 (now .Unix ()) >= e .expiry
51
52
}
52
53
53
- func (e * sessionEntry ) isValid () bool {
54
- return ! e .hasWaiter () && ! e .isExpired ()
54
+ func (e * sessionEntry ) isValid (now time. Time ) bool {
55
+ return ! e .hasWaiter () && ! e .isExpired (now )
55
56
}
56
57
57
58
// deviceData holds sessions entries and gc metadata associated to a single
@@ -74,6 +75,9 @@ type service struct {
74
75
75
76
// GC priority queue.
76
77
gcQueue gcQueue
78
+
79
+ // Internal clock.
80
+ now atomic.Pointer [time.Time ]
77
81
}
78
82
79
83
// ProvideService is a wire provider for in memory session storage.
@@ -100,6 +104,7 @@ func ProvideService(
100
104
}
101
105
heap .Init (& service .gcQueue )
102
106
107
+ go service .clock ()
103
108
go service .gcLoop ()
104
109
105
110
logger .Info ().Msg ("in memory session storage configured" )
@@ -145,7 +150,7 @@ func (s *service) getSession(deviceId uint64, latestPath string) (*sessionEntry,
145
150
func (s * service ) getValidSessionEntry (deviceId uint64 , latestPath string ) * sessionEntry {
146
151
assert .Locked (& s .mu )
147
152
session , device , i := s .getSession (deviceId , latestPath )
148
- if session == nil || ! session .isValid () {
153
+ if session == nil || ! session .isValid (* s . now . Load () ) {
149
154
return nil
150
155
}
151
156
@@ -314,7 +319,7 @@ func (s *service) WaitSession(deviceId uint64, pageUri uri.Uri, timeout time.Dur
314
319
currentSession , deviceData , sessionIndex := s .getSession (deviceId , pageUri .Path ())
315
320
316
321
// Valid session.
317
- if currentSession != nil && currentSession .isValid () {
322
+ if currentSession != nil && currentSession .isValid (* s . now . Load () ) {
318
323
s .mu .Unlock ()
319
324
return currentSession .Session , true
320
325
} else if timeout == time .Duration (0 ) { // Entry not found and timeout is 0s.
@@ -377,18 +382,21 @@ func (s *service) WaitSession(deviceId uint64, pageUri uri.Uri, timeout time.Dur
377
382
378
383
// session garbage collector loop.
379
384
func (s * service ) gcLoop () {
385
+ tick := time .NewTicker (s .cfg .gcInterval )
386
+
380
387
for {
388
+ now := <- tick .C
389
+
390
+ s .now .Store (& now )
381
391
s .metrics .gcCycle .Inc ()
382
392
383
393
// Wait until there is job in gcQueue.
384
394
s .mu .Lock ()
385
395
if len (s .gcQueue ) == 0 {
386
396
s .mu .Unlock ()
387
- time .Sleep (s .cfg .gcInterval )
388
397
continue
389
398
}
390
399
391
- now := time .Now ()
392
400
nowTs := uint32 (now .Unix ())
393
401
394
402
// Peek job.
@@ -397,7 +405,6 @@ func (s *service) gcLoop() {
397
405
// Job hasn't expired yet.
398
406
if job .pExpiry > nowTs {
399
407
s .mu .Unlock ()
400
- time .Sleep (s .cfg .gcInterval )
401
408
continue
402
409
}
403
410
@@ -443,5 +450,5 @@ func (s *service) gcLoop() {
443
450
}
444
451
445
452
func (s * service ) newExpiry () uint32 {
446
- return uint32 (time . Now ().Add (s .cfg .sessionInactiveTtl ).Unix ())
453
+ return uint32 (s . now . Load ().Add (s .cfg .sessionInactiveTtl ).Unix ())
447
454
}
0 commit comments