131 lines
3.3 KiB
Go
131 lines
3.3 KiB
Go
// redka 内嵌 Redis 兼容存储实现(v0.2.x API)
|
||
// 使用 github.com/nalgeon/redka — 基于 SQLite 的 Redis 替代方案 // Logan.Li
|
||
// 零外部依赖,数据存在本地文件中,非常适合独立二进制部署
|
||
package redis
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"time"
|
||
|
||
"github.com/nalgeon/redka"
|
||
_ "github.com/mattn/go-sqlite3" // redka.Open 需要 database/sql 驱动名 sqlite3 // Logan.Li
|
||
)
|
||
|
||
// redkaCache 基于 Redka v0.2.x 的缓存实现 // Logan.Li
|
||
type redkaCache struct {
|
||
db *redka.DB
|
||
}
|
||
|
||
func newRedka(cfg Config) (Cache, error) {
|
||
// redka.db 与独立二进制同级存放 // Logan.Li
|
||
dataDir := cfg.DataDir
|
||
if dataDir == "" {
|
||
dataDir = "."
|
||
}
|
||
if err := os.MkdirAll(dataDir, 0o755); err != nil {
|
||
return nil, fmt.Errorf("redka: 创建数据目录失败: %w", err)
|
||
}
|
||
dbPath := filepath.Join(dataDir, "redka.db")
|
||
db, err := redka.Open(dbPath, nil)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &redkaCache{db: db}, nil
|
||
}
|
||
|
||
func (r *redkaCache) Capabilities() Capabilities {
|
||
return Capabilities{SortedSet: false, PubSub: false}
|
||
}
|
||
|
||
func (r *redkaCache) Get(ctx context.Context, key string) (string, error) {
|
||
// Redka v0.2.x: Str().Get() 返回 core.Value // Logan.Li
|
||
val, err := r.db.Str().Get(key)
|
||
if err == redka.ErrNotFound {
|
||
return "", nil
|
||
}
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return val.String(), nil
|
||
}
|
||
|
||
func (r *redkaCache) Set(ctx context.Context, key string, value string, expiration time.Duration) error {
|
||
// Redka v0.2.x: Str().Set() 返回 error // Logan.Li
|
||
if err := r.db.Str().Set(key, value); err != nil {
|
||
return err
|
||
}
|
||
if expiration > 0 {
|
||
_, err := r.db.Key().Expire(key, expiration)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (r *redkaCache) Del(ctx context.Context, keys ...string) error {
|
||
for _, key := range keys {
|
||
if _, err := r.db.Key().Delete(key); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (r *redkaCache) Exists(ctx context.Context, keys ...string) (int64, error) {
|
||
var count int64
|
||
for _, key := range keys {
|
||
ok, err := r.db.Key().Exists(key)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
if ok {
|
||
count++
|
||
}
|
||
}
|
||
return count, nil
|
||
}
|
||
|
||
func (r *redkaCache) Expire(ctx context.Context, key string, expiration time.Duration) error {
|
||
ok, err := r.db.Key().Expire(key, expiration)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !ok {
|
||
return redka.ErrNotFound
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (r *redkaCache) Ping(ctx context.Context) error {
|
||
// Redka 始终可用(内嵌数据库)// Logan.Li
|
||
return nil
|
||
}
|
||
|
||
func (r *redkaCache) Close() error {
|
||
return r.db.Close()
|
||
}
|
||
|
||
var errSortedSetNotSupported = fmt.Errorf("sorted set operations are not supported by the redka backend; use go-redis (driver=redis) instead")
|
||
|
||
func (r *redkaCache) ZAdd(ctx context.Context, key string, member SortedSetMember) error {
|
||
return errSortedSetNotSupported
|
||
}
|
||
|
||
func (r *redkaCache) ZRangeByScore(ctx context.Context, key string, min, max string) ([]SortedSetMember, error) {
|
||
return nil, errSortedSetNotSupported
|
||
}
|
||
|
||
func (r *redkaCache) ZRemRangeByScore(ctx context.Context, key string, min, max string) error {
|
||
return errSortedSetNotSupported
|
||
}
|
||
|
||
var errPubSubNotSupported = fmt.Errorf("Pub/Sub is not supported by the redka backend; use go-redis (driver=redis)")
|
||
|
||
func (r *redkaCache) Publish(ctx context.Context, channel string, message string) error {
|
||
// local/redka 无 Pub/Sub,流水线日志走轮询 Get,此处 no-op // Logan.Li
|
||
return nil
|
||
}
|