added garbage collector

This commit is contained in:
Ali Yasir Naç
2025-04-29 10:14:14 +03:00
parent 494ff4e035
commit e81b66dbf4
4 changed files with 75 additions and 5 deletions

View File

@@ -62,6 +62,7 @@ Password: "root",
Access: "full", Access: "full",
Scope: "all", Scope: "all",
DefaultTable: "fiber_storage", DefaultTable: "fiber_storage",
GCInterval: time.Second * 10,
}) })
``` ```
@@ -92,6 +93,9 @@ Scope string
// The default table used to store key-value records // The default table used to store key-value records
DefaultTable string DefaultTable string
// Optional. Default is 10 * time.Second
GCInterval time.Duration
} }
``` ```
@@ -108,5 +112,6 @@ Password: "root",
Access: "full", Access: "full",
Scope: "all", Scope: "all",
DefaultTable: "fiber_storage", DefaultTable: "fiber_storage",
GCInterval: time.Second * 10,
} }
``` ```

View File

@@ -3,6 +3,7 @@ package surrealdb
import ( import (
"log" "log"
"strings" "strings"
"time"
) )
// Config holds the configuration required to initialize and connect to a SurrealDB instance. // Config holds the configuration required to initialize and connect to a SurrealDB instance.
@@ -41,6 +42,9 @@ type Config struct {
// The default table used to store key-value records // The default table used to store key-value records
DefaultTable string DefaultTable string
// Optional. Default is 10 * time.Second
GCInterval time.Duration
} }
var ConfigDefault = Config{ var ConfigDefault = Config{
@@ -52,6 +56,7 @@ var ConfigDefault = Config{
Access: "full", Access: "full",
Scope: "all", Scope: "all",
DefaultTable: "fiber_storage", DefaultTable: "fiber_storage",
GCInterval: time.Second * 10,
} }
func configDefault(config ...Config) Config { func configDefault(config ...Config) Config {
@@ -100,5 +105,9 @@ func configDefault(config ...Config) Config {
log.Printf("Warning: ConnectionString %s doesn't start with ws://, wss://, http:// or https://", cfg.ConnectionString) log.Printf("Warning: ConnectionString %s doesn't start with ws://, wss://, http:// or https://", cfg.ConnectionString)
} }
if int(cfg.GCInterval.Seconds()) <= 0 {
cfg.GCInterval = ConfigDefault.GCInterval
}
return cfg return cfg
} }

View File

@@ -10,8 +10,10 @@ import (
// Storage interface that is implemented by storage providers // Storage interface that is implemented by storage providers
type Storage struct { type Storage struct {
db *surrealdb.DB db *surrealdb.DB
table string table string
stopGC chan struct{}
interval time.Duration
} }
// model represents a key-value storage record used in SurrealDB. // model represents a key-value storage record used in SurrealDB.
@@ -54,10 +56,15 @@ func New(config ...Config) *Storage {
panic(err) panic(err)
} }
return &Storage{ storage := &Storage{
db: db, db: db,
table: cfg.DefaultTable, table: cfg.DefaultTable,
stopGC: make(chan struct{}),
interval: cfg.GCInterval,
} }
go storage.gc()
return storage
} }
func (s *Storage) Get(key string) ([]byte, error) { func (s *Storage) Get(key string) ([]byte, error) {
@@ -113,6 +120,7 @@ func (s *Storage) Reset() error {
} }
func (s *Storage) Close() error { func (s *Storage) Close() error {
close(s.stopGC)
return s.db.Close() return s.db.Close()
} }
@@ -139,3 +147,30 @@ func (s *Storage) List() ([]byte, error) {
return json.Marshal(data) return json.Marshal(data)
} }
func (s *Storage) gc() {
ticker := time.NewTicker(s.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
s.cleanupExpired()
case <-s.stopGC:
return
}
}
}
func (s *Storage) cleanupExpired() {
records, err := surrealdb.Select[[]model, models.Table](s.db, models.Table(s.table))
if err != nil {
return
}
now := time.Now().Unix()
for _, item := range *records {
if item.Exp > 0 && now > item.Exp {
_ = s.Delete(item.Key)
}
}
}

View File

@@ -182,6 +182,27 @@ func Test_Surrealdb_ListSkipsExpired(t *testing.T) {
require.NotContains(t, result, "expired") require.NotContains(t, result, "expired")
} }
func Test_Surrealdb_GarbageCollector_RemovesExpiredKeys(t *testing.T) {
testStore, err := newTestStore(t)
require.NoError(t, err)
defer testStore.Close()
err = testStore.Set("temp_key", []byte("temp_value"), 1*time.Second)
require.NoError(t, err)
val, err := testStore.Get("temp_key")
require.NoError(t, err)
require.NotNil(t, val)
time.Sleep(3 * time.Second)
require.Eventually(t, func() bool {
val, err = testStore.Get("temp_key")
require.NoError(t, err)
return val == nil
}, 3*time.Second, 300*time.Millisecond)
}
func Benchmark_SurrealDB_Set(b *testing.B) { func Benchmark_SurrealDB_Set(b *testing.B) {
testStore, err := newTestStore(b) testStore, err := newTestStore(b)
require.NoError(b, err) require.NoError(b, err)