mirror of
https://github.com/charlienet/go-mixed.git
synced 2025-07-17 16:12:42 +08:00
101 lines
1.9 KiB
Go
101 lines
1.9 KiB
Go
package bloom
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
_ "embed"
|
|
|
|
"github.com/charlienet/go-mixed/redis"
|
|
)
|
|
|
|
//go:embed bloom.lua
|
|
var redis_bloom_function string
|
|
|
|
var once sync.Once
|
|
|
|
var ErrTooLargeOffset = errors.New("超出最大偏移量")
|
|
|
|
var _ bitStore = &redisBitSet{}
|
|
|
|
// 使用Redis存储位图
|
|
type redisBitSet struct {
|
|
store redis.Client
|
|
key string
|
|
bits uint
|
|
}
|
|
|
|
func newRedisStore(store redis.Client, key string, bits uint) *redisBitSet {
|
|
once.Do(func() { store.LoadFunction(redis_bloom_function) })
|
|
|
|
return &redisBitSet{
|
|
store: store,
|
|
key: key,
|
|
bits: bits,
|
|
}
|
|
}
|
|
|
|
func (s *redisBitSet) Set(ctx context.Context, offsets ...uint) error {
|
|
args, err := s.buildOffsetArgs(offsets)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = s.store.FCall(ctx, "set_bit", []string{s.key}, args...).Result()
|
|
|
|
//底层使用的是go-redis,redis.Nil表示操作的key不存在
|
|
//需要针对key不存在的情况特殊判断
|
|
if err == redis.Nil {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *redisBitSet) Test(offsets ...uint) (bool, error) {
|
|
args, err := s.buildOffsetArgs(offsets)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
|
defer cancel()
|
|
|
|
resp, err := s.store.FCall(ctx, "test_bit", []string{s.key}, args...).Result()
|
|
|
|
// key 不存在,表示还未存放任何数据
|
|
if err == redis.Nil {
|
|
return false, nil
|
|
} else if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
exists, ok := resp.(int64)
|
|
if !ok {
|
|
return false, nil
|
|
}
|
|
|
|
return exists == 1, nil
|
|
}
|
|
|
|
func (s *redisBitSet) Clear() {
|
|
|
|
}
|
|
|
|
func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]any, error) {
|
|
args := make([]any, 0, len(offsets))
|
|
for _, offset := range offsets {
|
|
if offset >= r.bits {
|
|
return nil, ErrTooLargeOffset
|
|
}
|
|
|
|
args = append(args, strconv.FormatUint(uint64(offset), 10))
|
|
}
|
|
return args, nil
|
|
}
|