fleet/server/service/redis_key_value/redis_key_value.go
Lucas Manuel Rodriguez 8d664bd456
Make software batch endpoint asynchronous (#22258)
#22069

API changes: https://github.com/fleetdm/fleet/pull/22259

QAd by applying 10 pieces of software on a team, which took 3+ minutes
in total (which, before these changes was timing out at 100s.)

With this approach, a GitOps CI run timing out might leave the
background process running (which will eventually be applied to the
database). The team discussed and agreed that we can fix this edge case
later.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Committing-Changes.md#changes-files)
for more information.
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
2024-09-20 11:55:47 -03:00

58 lines
1.9 KiB
Go

// Package redis_key_value implements a most basic SET & GET key/value store
// where both the key and the value are strings.
package redis_key_value
import (
"context"
"errors"
"time"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/datastore/redis"
"github.com/fleetdm/fleet/v4/server/fleet"
redigo "github.com/gomodule/redigo/redis"
)
// RedisKeyValue is a basic key/value store with SET and GET operations
// Items are removed via expiration (defined in the SET operation).
type RedisKeyValue struct {
pool fleet.RedisPool
testPrefix string // for tests, the key prefix to use to avoid conflicts
}
// New creates a new RedisKeyValue store.
func New(pool fleet.RedisPool) *RedisKeyValue {
return &RedisKeyValue{pool: pool}
}
// prefix is used to not collide with other key domains (like live queries or calendar locks).
const prefix = "key_value_"
// Set creates or overrides the given key with the given value.
// Argument expireTime is used to set the expiration of the item
// (when updating, the expiration of the item is updated).
func (r *RedisKeyValue) Set(ctx context.Context, key string, value string, expireTime time.Duration) error {
conn := redis.ConfigureDoer(r.pool, r.pool.Get())
defer conn.Close()
if _, err := redigo.String(conn.Do("SET", r.testPrefix+prefix+key, value, "PX", expireTime.Milliseconds())); err != nil {
return ctxerr.Wrap(ctx, err, "redis failed to set")
}
return nil
}
// Get returns the value for a given key.
// It returns (nil, nil) if the key doesn't exist.
func (r *RedisKeyValue) Get(ctx context.Context, key string) (*string, error) {
conn := redis.ConfigureDoer(r.pool, r.pool.Get())
defer conn.Close()
res, err := redigo.String(conn.Do("GET", r.testPrefix+prefix+key))
if errors.Is(err, redigo.ErrNil) {
return nil, nil
}
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "redis failed to get")
}
return &res, nil
}