From 00d8c160a67379fc667a837fb75108ed64e99c6a Mon Sep 17 00:00:00 2001 From: xgzlucario <912156837@qq.com> Date: Wed, 24 Jul 2024 02:46:06 +0800 Subject: [PATCH] feat: add lastAccessd for dict object --- Makefile | 5 +---- command_test.go | 20 ++++++++++++------ internal/dict/dict.go | 43 +++++++++++++++++++++++++++++--------- internal/dict/dict_test.go | 11 ++++++++++ internal/dict/object.go | 13 +++++------- main.go | 22 +++++++++---------- 6 files changed, 75 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 698484f..1a322c3 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,4 @@ build: go build -o rotom -ldflags "-s -w -X main.buildTime=$(shell date +%y%m%d_%H%M%S%z)" build-docker: - docker build --build-arg BUILD_TIME=$(shell date +%y%m%d_%H%M%S%z) -t rotom . - -# tmp command -# rsync -av --exclude='.git' rotom/ 2:~/xgz/rotom \ No newline at end of file + docker build --build-arg BUILD_TIME=$(shell date +%y%m%d_%H%M%S%z) -t rotom . \ No newline at end of file diff --git a/command_test.go b/command_test.go index d9cc29a..ec2928a 100644 --- a/command_test.go +++ b/command_test.go @@ -20,12 +20,7 @@ func startup() { AppendFileName: "appendonly-test.aof", } os.Remove(config.AppendFileName) - if err := InitDB(config); err != nil { - log.Panic().Msgf("init db error: %v", err) - } - if err := initServer(config); err != nil { - log.Panic().Msgf("init server error: %v", err) - } + config4Server(config) server.aeLoop.AddRead(server.fd, AcceptHandler, nil) server.aeLoop.AddTimeEvent(AE_NORMAL, 500, CheckOutOfMemory, nil) server.aeLoop.AeMain() @@ -220,3 +215,16 @@ func TestCommand(t *testing.T) { rdb.Close() }) } + +func TestConfig(t *testing.T) { + assert := assert.New(t) + + cfg, _ := LoadConfig("config.json") + assert.Equal(cfg.Port, 6379) + + _, err := LoadConfig("not-exist.json") + assert.NotNil(err) + + _, err = LoadConfig("go.mod") + assert.NotNil(err) +} diff --git a/internal/dict/dict.go b/internal/dict/dict.go index 1c9a9a5..5a98e9e 100644 --- a/internal/dict/dict.go +++ b/internal/dict/dict.go @@ -1,11 +1,28 @@ package dict import ( + "sync/atomic" "time" "github.com/cockroachdb/swiss" ) +var ( + _sec atomic.Uint32 + _nsec atomic.Int64 +) + +func init() { + // init backend ticker + tk := time.NewTicker(time.Microsecond) + go func() { + for t := range tk.C { + _sec.Store(uint32(t.Unix())) + _nsec.Store(t.UnixNano()) + } + }() +} + // Dict is the hashmap for Rotom. type Dict struct { data *swiss.Map[string, *Object] @@ -25,11 +42,15 @@ func (dict *Dict) Get(key string) (*Object, bool) { return nil, false } - // if object.hasTTL { - // ttl, ok := dict.expire.Get(key) - // if ttl > 0 || !ok { // - // } - // } + if object.hasTTL { + nsecTTL, ok := dict.expire.Get(key) + if !ok || nsecTTL < _nsec.Load() { + // expired + dict.data.Delete(key) + dict.expire.Delete(key) + return nil, false + } + } switch object.typ { case TypeZipMapC, TypeZipSetC: @@ -37,16 +58,17 @@ func (dict *Dict) Get(key string) (*Object, bool) { object.typ -= 1 } - object.updateLRU() + // update access time + object.lastAccessd = _sec.Load() return object, true } func (dict *Dict) Set(key string, typ Type, data any) { dict.data.Put(key, &Object{ - typ: typ, - lru: uint32(time.Now().Unix()), - data: data, + typ: typ, + lastAccessd: _sec.Load(), + data: data, }) } @@ -58,10 +80,11 @@ func (dict *Dict) Remove(key string) bool { } func (dict *Dict) SetTTL(key string, expiration int64) bool { - _, ok := dict.data.Get(key) + object, ok := dict.data.Get(key) if !ok { return false } + object.hasTTL = true dict.expire.Put(key, expiration) return true } diff --git a/internal/dict/dict_test.go b/internal/dict/dict_test.go index f203f2f..d0f9c95 100644 --- a/internal/dict/dict_test.go +++ b/internal/dict/dict_test.go @@ -17,6 +17,17 @@ func TestDict(t *testing.T) { assert := assert.New(t) dict := New() + dict.Set("key1", TypeString, []byte("hello")) + object, ok := dict.Get("key1") + assert.True(ok) + assert.Equal(object.Data(), []byte("hello")) + assert.Equal(object.Type(), TypeString) +} + +func TestDictMultiSet(t *testing.T) { + assert := assert.New(t) + dict := New() + for i := 0; i < 10000; i++ { key, value := genKV(rand.Int()) dict.Set(key, TypeString, value) diff --git a/internal/dict/object.go b/internal/dict/object.go index 5350613..81c0213 100644 --- a/internal/dict/object.go +++ b/internal/dict/object.go @@ -1,7 +1,5 @@ package dict -import "time" - // Type defines all rotom data types. type Type byte @@ -23,11 +21,12 @@ type Compressor interface { Decompress() } -// Object is the basic unit for storing in dict. +// Object is the basic element for storing in dict. type Object struct { - typ Type - lru uint32 - data any + typ Type + hasTTL bool + lastAccessd uint32 + data any } func (o *Object) Type() Type { return o.typ } @@ -35,5 +34,3 @@ func (o *Object) Type() Type { return o.typ } func (o *Object) Data() any { return o.data } func (o *Object) SetData(data any) { o.data = data } - -func (o *Object) updateLRU() { o.lru = uint32(time.Now().Unix()) } diff --git a/main.go b/main.go index d1b6a05..f88426e 100644 --- a/main.go +++ b/main.go @@ -15,10 +15,6 @@ var ( buildTime string ) -func runDebug() { - go http.ListenAndServe(":6060", nil) -} - func initLogger() zerolog.Logger { return zerolog. New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.DateTime}). @@ -28,6 +24,15 @@ func initLogger() zerolog.Logger { Logger() } +func config4Server(config *Config) { + if err := initServer(config); err != nil { + log.Fatal().Msgf("init server error: %v", err) + } + if err := InitDB(config); err != nil { + log.Fatal().Msgf("init db error: %v", err) + } +} + func main() { var path string var debug bool @@ -43,14 +48,9 @@ func main() { if err != nil { log.Fatal().Msgf("load config error: %v", err) } - if err = initServer(config); err != nil { - log.Fatal().Msgf("init server error: %v", err) - } - if err = InitDB(config); err != nil { - log.Fatal().Msgf("init db error: %v", err) - } + config4Server(config) if debug { - runDebug() + go http.ListenAndServe(":6060", nil) } log.Info().Int("port", config.Port).Msg("running on")