fleet/server/datastore/redis/ratelimit_store.go
Scott Gress 59f96651b6
Update to Go 1.24.1 (#27506)
For #26713 

# Details

This PR updates Fleet and its related tools and binaries to use Go
version 1.24.1.

Scanning through the changelog, I didn't see anything relevant to Fleet
that requires action. The only possible breaking change I spotted was:

> As [announced](https://tip.golang.org/doc/go1.23#linux) in the Go 1.23
release notes, Go 1.24 requires Linux kernel version 3.2 or later.

Linux kernel 3.2 was released in January of 2012, so I think we can
commit to dropping support for earlier kernel versions.

The new [tools directive](https://tip.golang.org/doc/go1.24#tools) is
interesting as it means we can move away from using `tools.go` files,
but it's not a required update.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [x] Manual QA for all new/changed functionality
- For Orbit and Fleet Desktop changes:
- [X] Make sure fleetd is compatible with the latest released version of
Fleet
   - [x] Orbit runs on macOS  , Linux   and Windows. 
- [x] Manual QA must be performed in the three main OSs, macOS ,
Windows and Linux .
2025-03-31 11:14:09 -05:00

122 lines
2.7 KiB
Go

package redis
import (
"strings"
"time"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/gomodule/redigo/redis"
)
type ThrottledStore struct {
Pool fleet.RedisPool
KeyPrefix string
}
const (
getWithTimeScript = `
local tbl = redis.call('TIME')
local val = redis.call('GET', KEYS[1])
table.insert(tbl, val)
return tbl
`
compareAndSwapWithTTLScript = `
local v = redis.call('get', KEYS[1])
if v == false then
return redis.error_reply("key does not exist")
end
if v ~= ARGV[1] then
return 0
end
redis.call('SET', KEYS[1], ARGV[2], 'EX', ARGV[3])
return 1
`
compareAndSwapNoKeyError = "key does not exist"
)
func (s *ThrottledStore) GetWithTime(key string) (int64, time.Time, error) {
var t time.Time
key = s.KeyPrefix + key
conn := s.Pool.Get()
defer conn.Close()
if err := BindConn(s.Pool, conn, key); err != nil {
return 0, t, err
}
// must come after BindConn due to redisc restrictions
conn = ConfigureDoer(s.Pool, conn)
script := redis.NewScript(1, getWithTimeScript)
res, err := redis.Values(script.Do(conn, key))
if err != nil {
return 0, t, err
}
if len(res) < 3 {
res = append(res, nil)
}
var secs, us, val int64
val = -1 // initialize val to -1, will stay untouched if res[2] is nil
if _, err := redis.Scan(res, &secs, &us, &val); err != nil {
return 0, t, err
}
t = time.Unix(secs, us*int64(time.Microsecond))
return val, t, nil
}
func (s *ThrottledStore) SetIfNotExistsWithTTL(key string, value int64, ttl time.Duration) (bool, error) {
key = s.KeyPrefix + key
conn := ConfigureDoer(s.Pool, s.Pool.Get())
defer conn.Close()
ttlSeconds := int(ttl.Seconds())
// An `EX 0` will fail, make sure that we set expiry for a minimum of one second
if ttlSeconds < 1 {
ttlSeconds = 1
}
_, err := redis.String(conn.Do("SET", key, value, "EX", ttlSeconds, "NX"))
if err != nil {
if err == redis.ErrNil {
// not set due to NX condition not met
return false, nil
}
return false, err
}
return true, nil
}
func (s *ThrottledStore) CompareAndSwapWithTTL(key string, old, isNew int64, ttl time.Duration) (bool, error) {
key = s.KeyPrefix + key
conn := s.Pool.Get()
defer conn.Close()
if err := BindConn(s.Pool, conn, key); err != nil {
return false, err
}
// must come after BindConn due to redisc restrictions
conn = ConfigureDoer(s.Pool, conn)
ttlSeconds := int(ttl.Seconds())
// An `EX 0` will fail, make sure that we set expiry for a minimum of one second
if ttlSeconds < 1 {
ttlSeconds = 1
}
script := redis.NewScript(1, compareAndSwapWithTTLScript)
swapped, err := redis.Bool(script.Do(conn, key, old, isNew, ttlSeconds))
if err != nil {
if strings.Contains(err.Error(), compareAndSwapNoKeyError) {
return false, nil
}
return false, err
}
return swapped, nil
}