merged from gramsrv upstream
This commit is contained in:
parent
79c64ee916
commit
21a0856587
651 changed files with 54774 additions and 4590 deletions
135
internal/store/redisstore/active_channel_ids_page.go
Normal file
135
internal/store/redisstore/active_channel_ids_page.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package redisstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultActiveChannelIDsPageTTL = 24 * time.Hour
|
||||
activeChannelIDsPageSchemaV1 = 1
|
||||
activeChannelIDsPageMaxBytes = 64 << 10
|
||||
)
|
||||
|
||||
type ActiveChannelIDsPageCache struct {
|
||||
c *redis.Client
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func NewActiveChannelIDsPageCache(c *redis.Client, ttl time.Duration) *ActiveChannelIDsPageCache {
|
||||
if ttl <= 0 {
|
||||
ttl = DefaultActiveChannelIDsPageTTL
|
||||
}
|
||||
return &ActiveChannelIDsPageCache{c: c, ttl: ttl}
|
||||
}
|
||||
|
||||
type activeChannelIDsPageEnvelope struct {
|
||||
Schema int `json:"schema"`
|
||||
Key store.ActiveChannelIDsPageKey `json:"key"`
|
||||
ChannelIDs []int64 `json:"channel_ids"`
|
||||
}
|
||||
|
||||
func activeChannelIDsPageRedisKey(key store.ActiveChannelIDsPageKey) string {
|
||||
return fmt.Sprintf(
|
||||
"channel:active-ids:page:v1:%d:%d:%d:%d",
|
||||
key.UserID, key.Generation, key.AfterChannelID, key.Limit,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *ActiveChannelIDsPageCache) GetActiveChannelIDsPage(
|
||||
ctx context.Context,
|
||||
key store.ActiveChannelIDsPageKey,
|
||||
) ([]int64, bool, error) {
|
||||
if s == nil || s.c == nil {
|
||||
return nil, false, fmt.Errorf("active channel IDs Redis cache unavailable")
|
||||
}
|
||||
if err := validateActiveChannelIDsPageKey(key); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
redisKey := activeChannelIDsPageRedisKey(key)
|
||||
raw, err := s.c.Get(ctx, redisKey).Bytes()
|
||||
if err == redis.Nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("redis get active channel IDs page: %w", err)
|
||||
}
|
||||
if len(raw) == 0 || len(raw) > activeChannelIDsPageMaxBytes {
|
||||
_ = s.c.Del(ctx, redisKey).Err()
|
||||
return nil, false, fmt.Errorf("invalid active channel IDs page size %d", len(raw))
|
||||
}
|
||||
var envelope activeChannelIDsPageEnvelope
|
||||
if err := json.Unmarshal(raw, &envelope); err != nil {
|
||||
_ = s.c.Del(ctx, redisKey).Err()
|
||||
return nil, false, fmt.Errorf("decode active channel IDs page: %w", err)
|
||||
}
|
||||
if envelope.Schema != activeChannelIDsPageSchemaV1 || envelope.Key != key {
|
||||
_ = s.c.Del(ctx, redisKey).Err()
|
||||
return nil, false, fmt.Errorf("active channel IDs page identity/schema mismatch")
|
||||
}
|
||||
if err := validateActiveChannelIDsPage(key, envelope.ChannelIDs); err != nil {
|
||||
_ = s.c.Del(ctx, redisKey).Err()
|
||||
return nil, false, err
|
||||
}
|
||||
return append([]int64(nil), envelope.ChannelIDs...), true, nil
|
||||
}
|
||||
|
||||
func (s *ActiveChannelIDsPageCache) PutActiveChannelIDsPage(
|
||||
ctx context.Context,
|
||||
key store.ActiveChannelIDsPageKey,
|
||||
channelIDs []int64,
|
||||
) error {
|
||||
if s == nil || s.c == nil {
|
||||
return fmt.Errorf("active channel IDs Redis cache unavailable")
|
||||
}
|
||||
if err := validateActiveChannelIDsPageKey(key); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateActiveChannelIDsPage(key, channelIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := json.Marshal(activeChannelIDsPageEnvelope{
|
||||
Schema: activeChannelIDsPageSchemaV1, Key: key, ChannelIDs: channelIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode active channel IDs page: %w", err)
|
||||
}
|
||||
if len(raw) > activeChannelIDsPageMaxBytes {
|
||||
return fmt.Errorf("active channel IDs page exceeds %d bytes: %d", activeChannelIDsPageMaxBytes, len(raw))
|
||||
}
|
||||
if err := s.c.Set(ctx, activeChannelIDsPageRedisKey(key), raw, s.ttl).Err(); err != nil {
|
||||
return fmt.Errorf("redis set active channel IDs page: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateActiveChannelIDsPageKey(key store.ActiveChannelIDsPageKey) error {
|
||||
if key.UserID == 0 || key.Generation == 0 || key.AfterChannelID < 0 ||
|
||||
key.Limit <= 0 || key.Limit > domain.MaxSynchronousChannelDialogFanout {
|
||||
return fmt.Errorf("invalid active channel IDs page key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateActiveChannelIDsPage(key store.ActiveChannelIDsPageKey, channelIDs []int64) error {
|
||||
if len(channelIDs) > key.Limit {
|
||||
return fmt.Errorf("active channel IDs page has %d rows, limit %d", len(channelIDs), key.Limit)
|
||||
}
|
||||
previous := key.AfterChannelID
|
||||
for _, channelID := range channelIDs {
|
||||
if channelID <= previous {
|
||||
return fmt.Errorf("active channel IDs page is not strictly ordered after %d", previous)
|
||||
}
|
||||
previous = channelID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ store.ActiveChannelIDsPageCache = (*ActiveChannelIDsPageCache)(nil)
|
||||
Loading…
Add table
Add a link
Reference in a new issue