diff --git a/Dockerfile b/Dockerfile index c4055e3..a3a7bfb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,8 +32,8 @@ VOLUME /data WORKDIR /data COPY --from=builder /build/rotom /data/rotom -COPY config.json /etc/rotom/config.json +COPY rotom.toml /etc/rotom/rotom.toml EXPOSE 6379 -CMD ["./rotom", "-config", "/etc/rotom/config.json"] \ No newline at end of file +CMD ["./rotom", "-conf", "/etc/rotom/rotom.toml"] \ No newline at end of file diff --git a/command.go b/command.go index 922fd2d..d8661ae 100644 --- a/command.go +++ b/command.go @@ -70,6 +70,7 @@ var cmdTable = []*Command{ {"flushdb", flushdbCommand, 0, true}, {"load", loadCommand, 0, false}, {"save", saveCommand, 0, false}, + {"config", configCommand, 2, false}, } func equalFold(a, b string) bool { @@ -609,6 +610,19 @@ func saveCommand(writer *resp.Writer, _ []redcon.RESP) { writer.WriteString("OK") } +func configCommand(writer *resp.Writer, args []redcon.RESP) { + key := args[0].String() + op := args[1].String() + switch strings.ToLower(op) { + case "get": + writer.WriteAny(configGet(key)) + case "set": + writer.WriteString("OK") + default: + writer.WriteError(fmt.Sprintf("ERR unknown op type: %s", op)) + } +} + func fetchMap(key []byte, setnx ...bool) (Map, error) { return fetch(key, func() Map { return hash.New() }, setnx...) } diff --git a/command_test.go b/command_test.go index 77467a8..b268a4a 100644 --- a/command_test.go +++ b/command_test.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/alicebob/miniredis/v2" "math/rand/v2" - "os" "sync" "testing" "time" @@ -15,16 +14,8 @@ import ( ) func startup() { - config := &Config{ - Port: 20082, - AppendOnly: true, - AppendFileName: "test.aof", - Save: true, - SaveFileName: "dump.rdb", - } - _ = os.Remove(config.AppendFileName) - config4Server(config) - printBanner(config) + config4Server("rotom_test.toml") + printBanner() RegisterAeLoop(&server) // custom server.aeLoop.AddTimeEvent(AeOnce, 300, func(_ *AeLoop, _ int, _ interface{}) {}, nil) @@ -64,7 +55,7 @@ func TestCommand(t *testing.T) { go startup() time.Sleep(time.Second / 2) rdb := redis.NewClient(&redis.Options{ - Addr: ":20082", + Addr: ":7979", }) sleepFn := func(dur time.Duration) { time.Sleep(dur) @@ -583,21 +574,3 @@ func testCommand(t *testing.T, testType string, rdb *redis.Client, sleepFn func( ast.Nil(rdb.Close()) }) } - -func TestConfig(t *testing.T) { - ast := assert.New(t) - cfg, _ := LoadConfig("config.json") - ast.Equal(cfg.Port, 6379) - _, err := LoadConfig("not-exist.json") - ast.NotNil(err) - _, err = LoadConfig("go.mod") - ast.NotNil(err) -} - -func TestReadableSize(t *testing.T) { - ast := assert.New(t) - ast.Equal(readableSize(50), "50B") - ast.Equal(readableSize(50*KB), "50.0KB") - ast.Equal(readableSize(50*MB), "50.0MB") - ast.Equal(readableSize(50*GB), "50.0GB") -} diff --git a/config.go b/config.go new file mode 100644 index 0000000..2097e85 --- /dev/null +++ b/config.go @@ -0,0 +1,38 @@ +package main + +import ( + "github.com/spf13/viper" +) + +const ( + defaultConfigFileName = "rotom.toml" +) + +func initConfig(fileName string) error { + viper.SetConfigFile(fileName) + return viper.ReadInConfig() +} + +func configGet(key string) any { return viper.Get(key) } + +func configGetString(key string) string { return viper.GetString(key) } + +func configGetInt(key string) int { return viper.GetInt(key) } + +func configGetBool(key string) bool { return viper.GetBool(key) } + +func configGetPort() int { + return configGetInt("tcp.port") +} + +func configGetAppendOnly() bool { + return configGetBool("aof.appendonly") +} + +func configGetAppendFileName() string { + return configGetString("aof.appendfilename") +} + +func configGetDbFileName() string { + return configGetString("rdb.dbfilename") +} diff --git a/config.json b/config.json deleted file mode 100644 index 6a830ea..0000000 --- a/config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "port": 6379, - "appendonly": false, - "appendfilename": "appendonly.aof", - "save": false, - "savefilename": "dump.rdb" -} diff --git a/const.go b/const.go index 0ac5580..70fbbf0 100644 --- a/const.go +++ b/const.go @@ -1,6 +1,7 @@ package main import ( + "github.com/dustin/go-humanize" "github.com/redis/go-redis/v9" "github.com/xgzlucario/rotom/internal/hash" "github.com/xgzlucario/rotom/internal/iface" @@ -28,9 +29,8 @@ const ( ) const ( - KB = 1024 - MB = 1024 * KB - GB = 1024 * MB + KB = humanize.Byte + MB = humanize.MiByte ) var type2c = map[ObjectType]func() iface.Encoder{ diff --git a/go.mod b/go.mod index 7b83a23..fa718ff 100644 --- a/go.mod +++ b/go.mod @@ -7,31 +7,47 @@ require ( github.com/chen3feng/stl4go v0.1.1 github.com/cockroachdb/swiss v0.0.0-20240612210725-f4de07ae6964 github.com/deckarep/golang-set/v2 v2.7.0 + github.com/dustin/go-humanize v1.0.1 github.com/klauspost/rvarint v1.0.1 github.com/redis/go-redis/v9 v9.7.0 github.com/rs/zerolog v1.33.0 + github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 github.com/tidwall/mmap v0.3.0 github.com/tidwall/redcon v1.6.2 github.com/zyedidia/generic v1.2.1 - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + golang.org/x/exp v0.0.0-20241210194714-1829a127f884 golang.org/x/sys v0.28.0 ) require ( github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/edsrzf/mmap-go v1.2.0 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 536d583..b047181 100644 --- a/go.sum +++ b/go.sum @@ -15,17 +15,27 @@ github.com/chen3feng/stl4go v0.1.1/go.mod h1:5ml3psLgETJjRJnMbPE+JiHLrCpt+Ajc2we github.com/cockroachdb/swiss v0.0.0-20240612210725-f4de07ae6964 h1:Ew0znI2JatzKy52N1iS5muUsHkf2UJuhocH7uFW7jjs= github.com/cockroachdb/swiss v0.0.0-20240612210725-f4de07ae6964/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84= github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/klauspost/rvarint v1.0.1 h1:5G/DQaex33Z9TL38B0csG7a7DCa79wNUF3UO0GaDAEI= github.com/klauspost/rvarint v1.0.1/go.mod h1:N3y17WM1y6FeND/unrPnTyHXl4qMM8Ny7IU7pZYyAe8= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -35,26 +45,46 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= @@ -68,16 +98,22 @@ github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc= github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= +golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/net/net.go b/internal/net/net.go index f94d2d9..29293da 100644 --- a/internal/net/net.go +++ b/internal/net/net.go @@ -4,7 +4,15 @@ import ( "golang.org/x/sys/unix" ) -const BACKLOG int = 64 +/* +BACKLOG is TCP listen() backlog. +In high requests-per-second environments you need a high backlog in order +to avoid slow clients connections issues. Note that the Linux kernel +will silently truncate it to the value of /proc/sys/net/core/somaxconn so +make sure to raise both the value of somaxconn and tcp_max_syn_backlog +in order to get the desired effect. +*/ +const BACKLOG int = 511 func Accept(fd int) (int, error) { nfd, _, err := unix.Accept(fd) diff --git a/main.go b/main.go index f7575b3..0b86fdc 100644 --- a/main.go +++ b/main.go @@ -26,16 +26,19 @@ func initLogger() zerolog.Logger { Logger() } -func config4Server(config *Config) { - if err := initServer(config); err != nil { +func config4Server(fileName string) { + if err := initConfig(fileName); err != nil { + log.Fatal().Msgf("init config error: %v", err) + } + if err := initServer(); err != nil { log.Fatal().Msgf("init server error: %v", err) } - if err := InitDB(config); err != nil { + if err := InitDB(); err != nil { log.Fatal().Msgf("init db error: %v", err) } } -func printBanner(config *Config) { +func printBanner() { log.Printf(` ________ _____ ___ __ \_______ /_____________ ___ Rotom %d bit (%s/%s) @@ -44,7 +47,7 @@ _ _, _// /_/ / /_ / /_/ / / / / / / Build: %s /_/ |_| \____/\__/ \____//_/ /_/ /_/ `, strconv.IntSize, runtime.GOARCH, runtime.GOOS, - config.Port, os.Getpid(), + configGetPort(), os.Getpid(), buildTime) } @@ -52,35 +55,26 @@ _ _, _// /_/ / /_ / /_/ / / / / / / Build: %s func RegisterAeLoop(server *Server) { server.aeLoop.AddRead(server.fd, AcceptHandler, nil) server.aeLoop.AddTimeEvent(AeNormal, 100, CronEvictExpired, nil) - if server.config.AppendOnly { + if configGetAppendOnly() { server.aeLoop.AddTimeEvent(AeNormal, 1000, CronSyncAOF, nil) } - if server.config.Save { - // TODO: rdb - } } func main() { var path string var debug bool - flag.StringVar(&path, "config", "config.json", "default config file path.") + flag.StringVar(&path, "conf", defaultConfigFileName, "config file path.") flag.BoolVar(&debug, "debug", false, "run with debug mode.") flag.Parse() - config, err := LoadConfig(path) - if err != nil { - log.Fatal().Msgf("load config error: %v", err) - } - printBanner(config) + config4Server(path) + printBanner() if debug { go http.ListenAndServe(":6060", nil) } - log.Info().Str("config", path).Msg("read config file") - config4Server(config) - log.Info().Msg("rotom server is ready to accept.") RegisterAeLoop(&server) diff --git a/rdb.go b/rdb.go index 74be18c..34a4c13 100644 --- a/rdb.go +++ b/rdb.go @@ -9,11 +9,10 @@ import ( ) type Rdb struct { - path string } -func NewRdb(path string) *Rdb { - return &Rdb{path: path} +func NewRdb() *Rdb { + return &Rdb{} } func (r *Rdb) SaveDB() (err error) { @@ -54,12 +53,12 @@ func (r *Rdb) SaveDB() (err error) { if err != nil { return err } - return os.Rename(fname, r.path) + return os.Rename(fname, configGetDbFileName()) } func (r *Rdb) LoadDB() error { // Read file data by mmap. - data, err := mmap.Open(r.path, false) + data, err := mmap.Open(configGetDbFileName(), false) if len(data) == 0 { return nil } diff --git a/rotom.go b/rotom.go index 4fd7c60..3791da9 100644 --- a/rotom.go +++ b/rotom.go @@ -1,15 +1,12 @@ package main import ( - "encoding/json" - "fmt" + "github.com/dustin/go-humanize" "github.com/tidwall/redcon" "github.com/xgzlucario/rotom/internal/iface" + "github.com/xgzlucario/rotom/internal/list" "github.com/xgzlucario/rotom/internal/net" "github.com/xgzlucario/rotom/internal/resp" - "os" - - "github.com/xgzlucario/rotom/internal/list" ) const ( @@ -43,37 +40,28 @@ type Client struct { type Server struct { fd int - config *Config aeLoop *AeLoop clients map[int]*Client } -type Config struct { - Port int `json:"port"` - AppendOnly bool `json:"appendonly"` - AppendFileName string `json:"appendfilename"` - Save bool `json:"save"` - SaveFileName string `json:"savefilename"` -} - var ( db DB server Server ) // InitDB initializes database and redo appendonly files if needed. -func InitDB(config *Config) (err error) { +func InitDB() (err error) { db.dict = New() - if config.Save { - db.rdb = NewRdb(config.SaveFileName) + if configGetBool("save") { + db.rdb = NewRdb() log.Debug().Msg("start loading rdb file...") if err = db.rdb.LoadDB(); err != nil { return err } } - if config.AppendOnly { - db.aof, err = NewAof(config.AppendFileName) + if configGetAppendOnly() { + db.aof, err = NewAof(configGetAppendFileName()) if err != nil { return } @@ -93,18 +81,6 @@ func InitDB(config *Config) (err error) { return nil } -func LoadConfig(path string) (config *Config, err error) { - jsonStr, err := os.ReadFile(path) - if err != nil { - return - } - config = &Config{} - if err = json.Unmarshal(jsonStr, config); err != nil { - return nil, err - } - return -} - // AcceptHandler is the main file event of aeloop. func AcceptHandler(loop *AeLoop, fd int, _ interface{}) { cfd, err := net.Accept(fd) @@ -153,7 +129,8 @@ READ: // queryBuf need grow up if client.recvx == len(client.queryBuf) { client.queryBuf = append(client.queryBuf, make([]byte, client.recvx)...) - log.Warn().Msgf("client %d queryBuf grow up to size %s", fd, readableSize(len(client.queryBuf))) + sz := uint64(len(client.queryBuf)) + log.Warn().Msgf("client %d queryBuf grow up to size %s", fd, humanize.Bytes(sz)) goto READ } @@ -204,7 +181,7 @@ func ProcessQueryBuf(client *Client) { } else { cmd.process(client.replyWriter, respBuf) // write aof file - if cmd.persist && server.config.AppendOnly { + if cmd.persist && configGetAppendOnly() { _, _ = db.aof.Write(queryBuf[:n]) } } @@ -233,8 +210,7 @@ func SendReplyToClient(loop *AeLoop, fd int, extra interface{}) { loop.ModRead(fd, ReadQueryFromClient, client) } -func initServer(config *Config) (err error) { - server.config = config +func initServer() (err error) { server.clients = make(map[int]*Client) // init aeLoop server.aeLoop, err = AeLoopCreate() @@ -242,31 +218,19 @@ func initServer(config *Config) (err error) { return err } // init tcp server - server.fd, err = net.TcpServer(config.Port) + server.fd, err = net.TcpServer(configGetPort()) if err != nil { return err } return nil } -func CronSyncAOF(_ *AeLoop, _ int, _ interface{}) { +func CronSyncAOF(ae *AeLoop, fd int, extra interface{}) { if err := db.aof.Flush(); err != nil { log.Error().Msgf("sync aof error: %v", err) } } -func CronEvictExpired(_ *AeLoop, _ int, _ interface{}) { +func CronEvictExpired(ae *AeLoop, fd int, extra interface{}) { db.dict.EvictExpired() } - -func readableSize[T int | uint64](sz T) string { - switch { - case sz >= GB: - return fmt.Sprintf("%.1fGB", float64(sz)/float64(GB)) - case sz >= MB: - return fmt.Sprintf("%.1fMB", float64(sz)/float64(MB)) - case sz >= KB: - return fmt.Sprintf("%.1fKB", float64(sz)/float64(KB)) - } - return fmt.Sprintf("%dB", sz) -} diff --git a/rotom.toml b/rotom.toml new file mode 100644 index 0000000..2fd9379 --- /dev/null +++ b/rotom.toml @@ -0,0 +1,11 @@ +[tcp] +port = 6379 + +[rdb] +save = false +dbfilename = "dump.rdb" + +[aof] +appendonly = false +appendfilename = "appendonly.aof" +appendfsync = "everysec" \ No newline at end of file diff --git a/rotom_test.toml b/rotom_test.toml new file mode 100644 index 0000000..53d4b13 --- /dev/null +++ b/rotom_test.toml @@ -0,0 +1,11 @@ +[tcp] +port = 7979 + +[rdb] +save = true +dbfilename = "dump.rdb" + +[aof] +appendonly = true +appendfilename = "appendonly.aof" +appendfsync = "everysec" \ No newline at end of file