@@ -13,10 +13,14 @@ import (
1313)
1414
1515const cacheKeyPrefix = "anon-device"
16+ const anonymousDeviceExpiration = 30 * 24 * time .Hour
17+
18+ var ErrDeviceLimitReached = fmt .Errorf ("device limit reached" )
1619
1720type AnonDBStore struct {
18- sqlStore db.DB
19- log log.Logger
21+ sqlStore db.DB
22+ log log.Logger
23+ deviceLimit int64
2024}
2125
2226type Device struct {
@@ -45,8 +49,8 @@ type AnonStore interface {
4549 DeleteDevicesOlderThan (ctx context.Context , olderThan time.Time ) error
4650}
4751
48- func ProvideAnonDBStore (sqlStore db.DB ) * AnonDBStore {
49- return & AnonDBStore {sqlStore : sqlStore , log : log .New ("anonstore" )}
52+ func ProvideAnonDBStore (sqlStore db.DB , deviceLimit int64 ) * AnonDBStore {
53+ return & AnonDBStore {sqlStore : sqlStore , log : log .New ("anonstore" ), deviceLimit : deviceLimit }
5054}
5155
5256func (s * AnonDBStore ) ListDevices (ctx context.Context , from * time.Time , to * time.Time ) ([]* Device , error ) {
@@ -65,9 +69,54 @@ func (s *AnonDBStore) ListDevices(ctx context.Context, from *time.Time, to *time
6569 return devices , err
6670}
6771
72+ // updateDevice updates a device if it exists and has been updated between the given times.
73+ func (s * AnonDBStore ) updateDevice (ctx context.Context , device * Device ) error {
74+ const query = `UPDATE anon_device SET
75+ client_ip = ?,
76+ user_agent = ?,
77+ updated_at = ?
78+ WHERE device_id = ? AND updated_at BETWEEN ? AND ?`
79+
80+ args := []interface {}{device .ClientIP , device .UserAgent , device .UpdatedAt .UTC (), device .DeviceID ,
81+ device .UpdatedAt .UTC ().Add (- anonymousDeviceExpiration ), device .UpdatedAt .UTC ().Add (time .Minute ),
82+ }
83+ err := s .sqlStore .WithDbSession (ctx , func (dbSession * sqlstore.DBSession ) error {
84+ args = append ([]interface {}{query }, args ... )
85+ result , err := dbSession .Exec (args ... )
86+ if err != nil {
87+ return err
88+ }
89+
90+ rowsAffected , err := result .RowsAffected ()
91+ if err != nil {
92+ return err
93+ }
94+
95+ if rowsAffected == 0 {
96+ return ErrDeviceLimitReached
97+ }
98+
99+ return nil
100+ })
101+
102+ return err
103+ }
104+
68105func (s * AnonDBStore ) CreateOrUpdateDevice (ctx context.Context , device * Device ) error {
69106 var query string
70107
108+ // if device limit is reached, only update devices
109+ if s .deviceLimit > 0 {
110+ count , err := s .CountDevices (ctx , time .Now ().UTC ().Add (- anonymousDeviceExpiration ), time .Now ().UTC ().Add (time .Minute ))
111+ if err != nil {
112+ return err
113+ }
114+
115+ if count >= s .deviceLimit {
116+ return s .updateDevice (ctx , device )
117+ }
118+ }
119+
71120 args := []any {device .DeviceID , device .ClientIP , device .UserAgent ,
72121 device .CreatedAt .UTC (), device .UpdatedAt .UTC ()}
73122 switch s .sqlStore .GetDBType () {
0 commit comments