├── .gitignore ├── Makefile ├── cmd └── sider-server │ └── main.go ├── server ├── connection.go ├── pattern.go ├── debug.go ├── client.go ├── _test.go ├── info.go ├── database.go ├── strings.go ├── reader.go ├── config.go ├── set.go ├── keys.go ├── aof.go ├── list.go └── server.go ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *-server 3 | !cmd/sider-* 4 | *.aof 5 | *.rdb 6 | *.conf -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @ go build -o sider-server cmd/sider-server/*.go 3 | clean: 4 | rm -f sider-server 5 | install: all 6 | cp sider-server /usr/local/bin 7 | uninstall: 8 | rm -f /usr/local/bin/sider-server 9 | -------------------------------------------------------------------------------- /cmd/sider-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strings" 7 | 8 | "github.com/tidwall/sider/server" 9 | ) 10 | 11 | func main() { 12 | if err := server.Start(&server.Options{ 13 | Args: os.Args[1:], // pass the app args to the server 14 | }); err != nil { 15 | if !strings.HasPrefix(err.Error(), "options failure") && 16 | !strings.HasPrefix(err.Error(), "config failure") { 17 | log.Print(err) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/connection.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "strconv" 4 | 5 | func echoCommand(c *client) { 6 | if len(c.args) != 2 { 7 | c.replyAritryError() 8 | return 9 | } 10 | c.replyBulk(c.args[1]) 11 | } 12 | 13 | func pingCommand(c *client) { 14 | switch len(c.args) { 15 | default: 16 | c.replyAritryError() 17 | case 1: 18 | c.replyString("PONG") 19 | case 2: 20 | c.replyBulk(c.args[1]) 21 | } 22 | } 23 | 24 | func selectCommand(c *client) { 25 | if len(c.args) != 2 { 26 | c.replyAritryError() 27 | return 28 | } 29 | num, err := strconv.ParseUint(c.args[1], 10, 32) 30 | if err != nil { 31 | c.replyError("invalid DB index") 32 | return 33 | } 34 | c.db = c.s.selectDB(int(num)) 35 | c.replyString("OK") 36 | } 37 | -------------------------------------------------------------------------------- /server/pattern.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "path" 4 | 5 | type pattern struct { 6 | value string 7 | all bool 8 | glob bool 9 | greaterOrEqual string 10 | lessThan string 11 | } 12 | 13 | func parsePattern(value string) *pattern { 14 | if value == "*" { 15 | return &pattern{value: "*", all: true, glob: true} 16 | } 17 | if value == "" { 18 | return &pattern{value: ""} 19 | } 20 | p := &pattern{ 21 | value: value, 22 | } 23 | for i, c := range value { 24 | if c == '[' || c == '*' || c == '?' { 25 | p.greaterOrEqual = value[:i] 26 | p.glob = true 27 | break 28 | } 29 | } 30 | if !p.glob { 31 | p.greaterOrEqual = value 32 | } else if p.greaterOrEqual != "" { 33 | c := p.greaterOrEqual[len(p.greaterOrEqual)-1] 34 | if c == 0xFF { 35 | p.lessThan = p.greaterOrEqual + string(0) 36 | } else { 37 | p.lessThan = p.greaterOrEqual[:len(p.greaterOrEqual)-1] + string(c+1) 38 | } 39 | } 40 | return p 41 | } 42 | 43 | func (p *pattern) match(s string) bool { 44 | if p.all { 45 | return true 46 | } 47 | matched, _ := path.Match(p.value, s) 48 | return matched 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sider 2 | ===== 3 | A Redis clone written in Go. Supports 1.0 commands. 4 | I wrote this to prove to myself that Go can perform nearly as well as C for general networking and disk io. 5 | This project helped to lead to the development of [Tile38](https://github.com/tidwall/tile38) and [Redcon](https://github.com/tidwall/redcon). 6 | 7 | **This is an experimental side project and is not intended for production.** 8 | 9 | Why? 10 | ---- 11 | I wanted to understand all of the baseline challenges of running a Redis implementation in Go, and sometimes the best way to understand an architecture is to cleanroom it. 12 | 13 | 14 | Commands 15 | -------- 16 | **Strings** 17 | append,bitcount,decr,decrby,get,getset,incr,incrby,mget,mset,msetnx,set,setnx 18 | 19 | **Lists** 20 | lindex,llen,lpop,lpush,lrange,lrem,lset,ltrim,rpoplpush,rpop,rpush 21 | 22 | **Sets** 23 | sadd,scard,smembers,sismember,sdiff,sinter,sunion,sdiffstore,sinterstore,sunionstore,spop,srandmember,srem,smove 24 | 25 | **Connection** 26 | echo,ping,select 27 | 28 | **Server** 29 | auth,bgrewriteaof,bgsave,config,dbsize,debug,flushdb,flushall,info,lastsave,monitor,save,shutdown 30 | 31 | **Keys** 32 | del,exists,expireat,expire,keys,move,randomkey,rename,renamenx,sort,ttl,type 33 | 34 | 35 | License 36 | ------- 37 | BSD 38 | -------------------------------------------------------------------------------- /server/debug.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "strings" 8 | "syscall" 9 | ) 10 | 11 | func replyArgsError(c *client) { 12 | c.replyError("Unknown DEBUG subcommand or wrong number of arguments for '" + c.args[1] + "'") 13 | } 14 | 15 | func debugCommand(c *client) { 16 | if len(c.args) == 1 { 17 | c.replyError("You must specify a subcommand for DEBUG. Try DEBUG HELP for info.") 18 | return 19 | } 20 | switch strings.ToLower(c.args[1]) { 21 | default: 22 | replyArgsError(c) 23 | return 24 | case "help": 25 | msgs := []string{ 26 | "DEBUG arg arg ... arg. Subcommands:", 27 | "segfault -- Crash the server with sigsegv.", 28 | "object -- Show low level info about key and associated value.", 29 | "gc -- Force a garbage collection.", 30 | } 31 | c.replyMultiBulkLen(len(msgs)) 32 | for _, msg := range msgs { 33 | c.replyBulk(msg) 34 | } 35 | case "segfault": 36 | syscall.Kill(os.Getpid(), syscall.SIGSEGV) 37 | case "object": 38 | debugObjectCommand(c) 39 | case "gc": 40 | runtime.GC() 41 | c.replyString("OK") 42 | } 43 | } 44 | 45 | func debugObjectCommand(c *client) { 46 | if len(c.args) != 3 { 47 | replyArgsError(c) 48 | return 49 | } 50 | typ := c.db.getType(c.args[2]) 51 | _, ok := c.db.get(c.args[2]) 52 | if !ok { 53 | c.replyError("no such key") 54 | return 55 | } 56 | res := fmt.Sprintf("Value at:0x0 refcount:1 encoding:%s", typ) 57 | c.replyString(res) 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Joshua Baker 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 3. Neither the name of Redis nor the names of its contributors may be used 13 | to endorse or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /server/client.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "io" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type client struct { 10 | wr io.Writer // client writer 11 | s *Server // shared server 12 | db *database // the active database 13 | args []string // command arguments 14 | raw []byte // the raw command bytes 15 | addr string // the address of the client 16 | dirty int // the number of changes made by the client 17 | monitor bool // the client is in monitor mode 18 | errd bool // flag that indicates that the last command was an error 19 | authd int // 0 = no auth checked, 1 = protected checked, 2 = pass checked 20 | 21 | } 22 | 23 | // flushAOF checks if the the client has any dirty markers and 24 | // if so calls server.flushAOF 25 | func (c *client) flushAOF() error { 26 | if c.dirty > 0 { 27 | c.s.mu.Lock() 28 | defer c.s.mu.Unlock() 29 | if err := c.s.flushAOF(); err != nil { 30 | c.s.fatalError(err) 31 | return err 32 | } 33 | c.dirty = 0 34 | } 35 | return nil 36 | } 37 | 38 | func (c *client) authenticate(cmd *command) bool { 39 | if c.authd == 2 { 40 | return true 41 | } 42 | c.s.mu.RLock() 43 | defer c.s.mu.RUnlock() 44 | if c.authd == 0 { 45 | if c.s.protected() { 46 | if !strings.HasPrefix(c.addr, "127.0.0.1:") && !strings.HasPrefix(c.addr, "[::1]:") { 47 | c.replyProtectedError() 48 | return false 49 | } 50 | } 51 | c.authd = 1 52 | } 53 | if c.s.cfg.requirepass == "" { 54 | return true 55 | } 56 | if cmd.name != "auth" { 57 | c.replyNoAuthError() 58 | return false 59 | } 60 | cmd.funct(c) 61 | return false 62 | } 63 | 64 | func (c *client) replyString(s string) { 65 | io.WriteString(c.wr, "+"+s+"\r\n") 66 | } 67 | func (c *client) replyUniqueError(s string) { 68 | io.WriteString(c.wr, "-"+s+"\r\n") 69 | c.errd = true 70 | } 71 | func (c *client) replyBulk(s string) { 72 | io.WriteString(c.wr, "$"+strconv.FormatInt(int64(len(s)), 10)+"\r\n"+s+"\r\n") 73 | } 74 | func (c *client) replyNull() { 75 | io.WriteString(c.wr, "$-1\r\n") 76 | } 77 | func (c *client) replyInt(n int) { 78 | io.WriteString(c.wr, ":"+strconv.FormatInt(int64(n), 10)+"\r\n") 79 | } 80 | func (c *client) replyMultiBulkLen(n int) { 81 | io.WriteString(c.wr, "*"+strconv.FormatInt(int64(n), 10)+"\r\n") 82 | } 83 | func (c *client) replyError(s string) { 84 | c.replyUniqueError("ERR " + s) 85 | } 86 | func (c *client) replyAritryError() { 87 | c.replyError("wrong number of arguments for '" + c.args[0] + "'") 88 | } 89 | func (c *client) replyTypeError() { 90 | c.replyUniqueError("WRONGTYPE Operation against a key holding the wrong kind of value") 91 | } 92 | func (c *client) replyNoAuthError() { 93 | c.replyUniqueError("NOAUTH Authentication required.") 94 | } 95 | func (c *client) replySyntaxError() { 96 | c.replyError("syntax error") 97 | } 98 | func (c *client) replyInvalidIntError() { 99 | c.replyError("value is not an integer or out of range") 100 | } 101 | func (c *client) replyNoSuchKeyError() { 102 | c.replyError("no such key") 103 | } 104 | 105 | func (c *client) replyProtectedError() { 106 | c.replyUniqueError(`` + 107 | `DENIED ` + c.s.options.AppName + ` is running in protected ` + 108 | `mode because protected mode is enabled, no bind address was ` + 109 | `specified, no authentication password is requested to clients. ` + 110 | `In this mode connections are only accepted from the loopback ` + 111 | `interface. If you want to connect from external computers to ` + 112 | c.s.options.AppName + ` you may adopt one of the following ` + 113 | `solutions: 1) Just disable protected mode sending the command ` + 114 | `'CONFIG SET protected-mode no' from the loopback interface by ` + 115 | `connecting to ` + c.s.options.AppName + ` from the same host ` + 116 | `the server is running, however MAKE SURE ` + c.s.options.AppName + 117 | ` is not publicly accessible from internet if you do so. Use ` + 118 | `CONFIG REWRITE to make this change permanent. 2) Alternatively ` + 119 | `you can just disable the protected mode by editing the Redis ` + 120 | `configuration file, and setting the protected mode option to ` + 121 | `'no', and then restarting the server. 3) If you started the ` + 122 | `server manually just for testing, restart it with the ` + 123 | `'--protected-mode no' option. 4) Setup a bind address or an ` + 124 | `authentication password. NOTE: You only need to do one of the ` + 125 | `above things in order for the server to start accepting ` + 126 | `connections from the outside.`) 127 | } 128 | -------------------------------------------------------------------------------- /server/_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "testing" 4 | 5 | func testMakeSimpleList(t testing.TB) *list { 6 | l := newList() 7 | l.rpush("1") 8 | l.rpush("2") 9 | l.rpush("3") 10 | l.rpush("4") 11 | l.lpush("a") 12 | l.lpush("b") 13 | l.lpush("c") 14 | l.lpush("d") 15 | l.rpush("a", "b", "c", "d") 16 | l.lpush("1", "2", "3", "4") 17 | // - 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 18 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 19 | if l.String() != "4 3 2 1 d c b a 1 2 3 4 a b c d" { 20 | t.Fatal("simple list failure") 21 | } 22 | return l 23 | } 24 | 25 | func testListValue(t *testing.T, l *list, idx int, expect string, ok bool) { 26 | value, tok := l.lindex(idx) 27 | if tok != ok || value != expect { 28 | t.Fatalf("expected value='%v', ok='%v', got value='%v', ok='%v'", expect, ok, value, tok) 29 | } 30 | } 31 | 32 | func testListLen(t *testing.T, l *list, expect int) { 33 | if l.len() != expect { 34 | t.Fatalf("expected %v, got %v", expect, l.len()) 35 | } 36 | } 37 | 38 | func testListLpop(t *testing.T, l *list, expect string, ok bool) { 39 | value, tok := l.lpop() 40 | if tok != ok || value != expect { 41 | t.Fatalf("expected value='%v', ok='%v', got value='%v', ok='%v'", expect, ok, value, tok) 42 | } 43 | } 44 | func testListRpop(t *testing.T, l *list, expect string, ok bool) { 45 | value, tok := l.rpop() 46 | if tok != ok || value != expect { 47 | t.Fatalf("expected value='%v', ok='%v', got value='%v', ok='%v'", expect, ok, value, tok) 48 | } 49 | } 50 | 51 | func testListSet(t *testing.T, l *list, idx int, value string, ok bool) { 52 | got := l.set(idx, value) 53 | if got != ok { 54 | t.Fatalf("expected '%v', got '%v'", ok, got) 55 | } 56 | } 57 | 58 | func testListRem(t *testing.T, l *list, count int, value string, expect int) { 59 | got := l.rem(count, value) 60 | if got != expect { 61 | t.Fatalf("expected '%v', got '%v'", expect, got) 62 | } 63 | } 64 | 65 | func testListString(t *testing.T, l *list, expect string) { 66 | got := l.String() 67 | if got != expect { 68 | t.Fatalf("expected '%v', got '%v'", expect, got) 69 | } 70 | } 71 | 72 | func TestList(t *testing.T) { 73 | l := testMakeSimpleList(t) 74 | testListValue(t, l, 0, "4", true) 75 | testListValue(t, l, 1, "3", true) 76 | testListValue(t, l, 7, "a", true) 77 | testListValue(t, l, 8, "1", true) 78 | testListValue(t, l, 9, "2", true) 79 | testListValue(t, l, 15, "d", true) 80 | testListValue(t, l, 16, "", false) 81 | testListValue(t, l, -1, "d", true) 82 | testListValue(t, l, -7, "2", true) 83 | testListValue(t, l, -8, "1", true) 84 | testListValue(t, l, -9, "a", true) 85 | testListValue(t, l, -15, "3", true) 86 | testListValue(t, l, -16, "4", true) 87 | testListValue(t, l, -17, "", false) 88 | 89 | testListString(t, l, "4 3 2 1 d c b a 1 2 3 4 a b c d") 90 | 91 | testListLen(t, l, 16) 92 | testListLpop(t, l, "4", true) 93 | testListLen(t, l, 15) 94 | testListLpop(t, l, "3", true) 95 | testListLen(t, l, 14) 96 | testListRpop(t, l, "d", true) 97 | testListLen(t, l, 13) 98 | testListRpop(t, l, "c", true) 99 | testListLen(t, l, 12) 100 | 101 | testListString(t, l, "2 1 d c b a 1 2 3 4 a b") 102 | 103 | testListSet(t, l, 0, "3", true) 104 | testListSet(t, l, 1, "4", true) 105 | testListSet(t, l, 2, "5", true) 106 | testListSet(t, l, -1, "Z", true) 107 | testListSet(t, l, 12, "?", false) 108 | testListSet(t, l, -12, "A", true) 109 | testListSet(t, l, -13, "?", false) 110 | 111 | testListString(t, l, "A 4 5 c b a 1 2 3 4 a Z") 112 | 113 | testListRem(t, l, 3, "a", 2) 114 | testListRem(t, l, 3, "a", 0) 115 | testListRem(t, l, 3, "A", 1) 116 | testListRem(t, l, 3, "Z", 1) 117 | testListRem(t, l, -1, "2", 0) 118 | 119 | testListString(t, l, "4 5 c b 1 2 3 4") 120 | 121 | nl := newList() 122 | l.lrange(1, -2, func(n int) {}, func(v string) bool { 123 | nl.rpush(v) 124 | return true 125 | }) 126 | l = nl 127 | testListString(t, l, "5 c b 1 2 3") 128 | 129 | l.trim(1, -2) 130 | testListString(t, l, "c b 1 2") 131 | l.trim(1, -3) 132 | testListString(t, l, "b") 133 | l.trim(1, -1) 134 | testListString(t, l, "") 135 | 136 | l.rpush("1", "2", "3", "4", "5", "6", "7", "8") 137 | testListString(t, l, "1 2 3 4 5 6 7 8") 138 | l.trim(-1, 500) 139 | testListString(t, l, "8") 140 | l.trim(500, 501) 141 | testListString(t, l, "") 142 | l.rpush("1", "2", "3", "4", "5", "6", "7", "8") 143 | l.trim(-5, -3) 144 | testListString(t, l, "4 5 6") 145 | 146 | l.clear() 147 | l.rpush("1", "2", "3", "4", "5", "6", "7", "8") 148 | l.trim(-12, -8) 149 | testListString(t, l, "1") 150 | 151 | } 152 | -------------------------------------------------------------------------------- /server/info.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "os/exec" 9 | "runtime" 10 | "strings" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | func infoCommand(c *client) { 16 | if len(c.args) != 1 && len(c.args) != 2 { 17 | c.replyAritryError() 18 | return 19 | } 20 | 21 | allSections := []string{ 22 | "Server", "Clients", "Memory", "Persistence", "Stats", 23 | "Replication", "CPU", "Commandstats", "Cluster", "Keyspace", 24 | } 25 | defaultSections := []string{ 26 | "Server", "Clients", "Memory", "Persistence", "Stats", 27 | "Replication", "CPU", "Cluster", "Keyspace", 28 | } 29 | sections := defaultSections 30 | if len(c.args) == 2 { 31 | arg := strings.ToLower(c.args[1]) 32 | switch arg { 33 | default: 34 | sections = nil 35 | for _, section := range allSections { 36 | if strings.ToLower(section) == arg { 37 | sections = []string{section} 38 | break 39 | } 40 | } 41 | case "all": 42 | sections = allSections 43 | case "default": 44 | sections = defaultSections 45 | } 46 | } 47 | wr := &bytes.Buffer{} 48 | for i, section := range sections { 49 | if i > 0 { 50 | wr.WriteString("\n") 51 | } 52 | wr.WriteString("# " + section + "\n") 53 | switch strings.ToLower(section) { 54 | case "server": 55 | writeInfoServer(c, wr) 56 | case "clients": 57 | writeInfoClients(c, wr) 58 | case "memory": 59 | writeInfoMemory(c, wr) 60 | case "persistence": 61 | writeInfoPersistence(c, wr) 62 | case "stats": 63 | writeInfoStats(c, wr) 64 | case "replication": 65 | writeInfoReplication(c, wr) 66 | case "cpu": 67 | writeInfoCPU(c, wr) 68 | case "commandstats": 69 | writeInfoCommandStats(c, wr) 70 | case "cluster": 71 | writeInfoCluster(c, wr) 72 | case "Keyspace": 73 | writeInfoKeyspace(c, wr) 74 | } 75 | } 76 | c.replyBulk(wr.String()) 77 | } 78 | 79 | var osOnce sync.Once 80 | var osName string 81 | 82 | const ptrSize = 32 << (uint64(^uintptr(0)) >> 63) 83 | 84 | func writeInfoServer(c *client, w io.Writer) { 85 | now := time.Now() 86 | fmt.Fprintf(w, "redis_version:%s\n", c.s.options.Version) 87 | fmt.Fprintf(w, "redis_mode:%s\n", c.s.mode) 88 | osOnce.Do(func() { 89 | osb, err := exec.Command("uname", "-smr").Output() 90 | if err != nil { 91 | osName = runtime.GOOS 92 | } else { 93 | osName = strings.TrimSpace(string(osb)) 94 | } 95 | }) 96 | fmt.Fprintf(w, "os:%s\n", osName) 97 | fmt.Fprintf(w, "arch_bits:%d\n", ptrSize) 98 | fmt.Fprintf(w, "go_version:%s\n", runtime.Version()[2:]) 99 | fmt.Fprintf(w, "process_id:%d\n", os.Getpid()) 100 | fmt.Fprintf(w, "tcp_port:%s\n", c.s.l.Addr().String()[strings.LastIndex(c.s.l.Addr().String(), ":")+1:]) 101 | fmt.Fprintf(w, "uptime_in_seconds:%d\n", now.Sub(c.s.started)/time.Second) 102 | fmt.Fprintf(w, "uptime_in_days:%d\n", now.Sub(c.s.started)/time.Hour/24) 103 | fmt.Fprintf(w, "executable:%s\n", c.s.executable) 104 | } 105 | 106 | func human(m uint64) string { 107 | f := float64(m) 108 | if f < 1024 { 109 | return fmt.Sprintf("%.2fB", f) 110 | } else if f < 1024*1024 { 111 | return fmt.Sprintf("%.2fK", f/1024) 112 | } else if f < 1024*1024*1024 { 113 | return fmt.Sprintf("%.2fM", f/1024/1024) 114 | } 115 | return fmt.Sprintf("%.2fG", f/1024/1024/1024) 116 | } 117 | 118 | func writeInfoMemory(c *client, w io.Writer) { 119 | runtime.GC() 120 | var m runtime.MemStats 121 | runtime.ReadMemStats(&m) 122 | fmt.Fprintf(w, "used_memory:%d\n", m.Alloc) 123 | fmt.Fprintf(w, "used_memory_human:%s\n", human(m.Alloc)) 124 | // total_system_memory:17179869184 125 | // total_system_memory_human:16.00G 126 | } 127 | func writeInfoPersistence(c *client, w io.Writer) { 128 | // aof_enabled:0 129 | // aof_rewrite_in_progress:0 130 | // aof_rewrite_scheduled:0 131 | // aof_last_rewrite_time_sec:-1 132 | // aof_current_rewrite_time_sec:-1 133 | // aof_last_bgrewrite_status:ok 134 | // aof_last_write_status:ok 135 | } 136 | 137 | func writeInfoStats(c *client, w io.Writer) {} 138 | func writeInfoReplication(c *client, w io.Writer) { 139 | // role:master 140 | // connected_slaves:0 141 | // master_repl_offset:0 142 | // repl_backlog_active:0 143 | // repl_backlog_size:1048576 144 | // repl_backlog_first_byte_offset:0 145 | // repl_backlog_histlen:0 146 | } 147 | func writeInfoCPU(c *client, w io.Writer) {} 148 | func writeInfoCommandStats(c *client, w io.Writer) {} 149 | func writeInfoCluster(c *client, w io.Writer) {} 150 | func writeInfoKeyspace(c *client, w io.Writer) {} 151 | 152 | func writeInfoClients(c *client, w io.Writer) { 153 | fmt.Fprintf(w, "connected_clients:%d\n", len(c.s.clients)) 154 | } 155 | -------------------------------------------------------------------------------- /server/database.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type dbItem struct { 10 | expires bool 11 | value interface{} 12 | } 13 | 14 | type database struct { 15 | num int 16 | items map[string]dbItem 17 | expires map[string]time.Time 18 | aofbuf bytes.Buffer 19 | } 20 | 21 | func newDB(num int) *database { 22 | return &database{ 23 | num: num, 24 | items: make(map[string]dbItem), 25 | expires: make(map[string]time.Time), 26 | } 27 | } 28 | 29 | func (db *database) len() int { 30 | return len(db.items) 31 | } 32 | 33 | func (db *database) flush() { 34 | db.items = make(map[string]dbItem) 35 | db.expires = make(map[string]time.Time) 36 | } 37 | 38 | func (db *database) set(key string, value interface{}) { 39 | delete(db.expires, key) 40 | db.items[key] = dbItem{value: value} 41 | } 42 | 43 | func (db *database) get(key string) (interface{}, bool) { 44 | item, ok := db.items[key] 45 | if !ok { 46 | return nil, false 47 | } 48 | if item.expires { 49 | if t, ok := db.expires[key]; ok { 50 | if time.Now().After(t) { 51 | return nil, false 52 | } 53 | } 54 | } 55 | return item.value, true 56 | } 57 | 58 | func (db *database) getType(key string) string { 59 | v, ok := db.get(key) 60 | if !ok { 61 | return "none" 62 | } 63 | switch v.(type) { 64 | default: 65 | // should not be reached 66 | return "unknown" 67 | case int: 68 | return "string" 69 | case string: 70 | return "string" 71 | case *list: 72 | return "list" 73 | case *set: 74 | return "set" 75 | } 76 | } 77 | 78 | func (db *database) del(key string) (interface{}, bool) { 79 | item, ok := db.items[key] 80 | if !ok { 81 | return nil, false 82 | } 83 | delete(db.items, key) 84 | if item.expires { 85 | delete(db.expires, key) 86 | if t, ok := db.expires[key]; ok { 87 | if time.Now().After(t) { 88 | return nil, false 89 | } 90 | } 91 | } 92 | return item.value, true 93 | } 94 | 95 | func (db *database) expire(key string, when time.Time) bool { 96 | item, ok := db.items[key] 97 | if !ok { 98 | return false 99 | } 100 | item.expires = true 101 | db.items[key] = item 102 | db.expires[key] = when 103 | return true 104 | } 105 | 106 | func (db *database) getExpires(key string) (interface{}, time.Time, bool) { 107 | item, ok := db.items[key] 108 | if !ok { 109 | return nil, time.Time{}, false 110 | } 111 | var expires time.Time 112 | if item.expires { 113 | if t, ok := db.expires[key]; ok { 114 | expires = t 115 | if time.Now().After(t) { 116 | return nil, time.Time{}, false 117 | } 118 | } 119 | } 120 | return item.value, expires, true 121 | } 122 | 123 | func (db *database) getList(key string, create bool) (*list, bool) { 124 | value, ok := db.get(key) 125 | if ok { 126 | switch v := value.(type) { 127 | default: 128 | return nil, false 129 | case *list: 130 | return v, true 131 | } 132 | } 133 | if create { 134 | l := newList() 135 | db.set(key, l) 136 | return l, true 137 | } 138 | return nil, true 139 | } 140 | 141 | func (db *database) getSet(key string, create bool) (*set, bool) { 142 | value, ok := db.get(key) 143 | if ok { 144 | switch v := value.(type) { 145 | default: 146 | return nil, false 147 | case *set: 148 | return v, true 149 | } 150 | } 151 | if create { 152 | st := newSet() 153 | db.set(key, st) 154 | return st, true 155 | } 156 | return nil, true 157 | } 158 | 159 | func (db *database) ascend(iterator func(key string, value interface{}) bool) { 160 | now := time.Now() 161 | for key, item := range db.items { 162 | if item.expires { 163 | if t, ok := db.expires[key]; ok { 164 | if now.After(t) { 165 | continue 166 | } 167 | } 168 | } 169 | if !iterator(key, item.value) { 170 | return 171 | } 172 | } 173 | } 174 | 175 | func (db *database) update(key string, value interface{}) { 176 | item, ok := db.items[key] 177 | if ok { 178 | item.value = value 179 | } else { 180 | item = dbItem{value: value} 181 | } 182 | db.items[key] = item 183 | } 184 | 185 | func (db *database) deleteExpires() bool { 186 | if len(db.expires) == 0 { 187 | return false 188 | } 189 | deleted := false 190 | now := time.Now() 191 | for key, t := range db.expires { 192 | if now.Before(t) { 193 | continue 194 | } 195 | delete(db.items, key) 196 | db.aofbuf.WriteString("*2\r\n$3\r\nDEL\r\n$") 197 | db.aofbuf.WriteString(strconv.FormatInt(int64(len(key)), 10)) 198 | db.aofbuf.WriteString("\r\n") 199 | db.aofbuf.WriteString(key) 200 | db.aofbuf.WriteString("\r\n") 201 | delete(db.expires, key) 202 | deleted = true 203 | } 204 | return deleted 205 | } 206 | -------------------------------------------------------------------------------- /server/strings.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | ) 7 | 8 | func getCommand(c *client) { 9 | if len(c.args) != 2 { 10 | c.replyAritryError() 11 | return 12 | } 13 | key, ok := c.db.get(c.args[1]) 14 | if !ok { 15 | c.replyNull() 16 | return 17 | } 18 | switch s := key.(type) { 19 | default: 20 | c.replyTypeError() 21 | return 22 | case string: 23 | c.replyBulk(s) 24 | } 25 | } 26 | 27 | func getsetCommand(c *client) { 28 | if len(c.args) != 3 { 29 | c.replyAritryError() 30 | return 31 | } 32 | var res string 33 | key, ok := c.db.get(c.args[1]) 34 | if ok { 35 | switch s := key.(type) { 36 | default: 37 | c.replyTypeError() 38 | return 39 | case string: 40 | res = s 41 | } 42 | } 43 | c.db.set(c.args[1], c.args[2]) 44 | if !ok { 45 | c.replyNull() 46 | } else { 47 | c.replyBulk(res) 48 | } 49 | c.dirty++ 50 | } 51 | 52 | func incrCommand(c *client) { 53 | if len(c.args) != 2 { 54 | c.replyAritryError() 55 | return 56 | } 57 | genericIncrbyCommand(c, 1) 58 | } 59 | 60 | func incrbyCommand(c *client) { 61 | if len(c.args) != 3 { 62 | c.replyAritryError() 63 | return 64 | } 65 | n, err := atoi(c.args[2]) 66 | if err != nil { 67 | c.replyError("value is not an integer or out of range") 68 | return 69 | } 70 | genericIncrbyCommand(c, n) 71 | } 72 | 73 | func decrCommand(c *client) { 74 | if len(c.args) != 2 { 75 | c.replyAritryError() 76 | return 77 | } 78 | genericIncrbyCommand(c, -1) 79 | } 80 | 81 | func decrbyCommand(c *client) { 82 | if len(c.args) != 3 { 83 | c.replyAritryError() 84 | return 85 | } 86 | n, err := atoi(c.args[2]) 87 | if err != nil { 88 | c.replyError("value is not an integer or out of range") 89 | return 90 | } 91 | genericIncrbyCommand(c, -n) 92 | } 93 | 94 | func genericIncrbyCommand(c *client, delta int) { 95 | var n int 96 | value, ok := c.db.get(c.args[1]) 97 | if ok { 98 | switch s := value.(type) { 99 | default: 100 | c.replyTypeError() 101 | return 102 | case string: 103 | var err error 104 | n, err = atoi(s) 105 | if err != nil { 106 | c.replyTypeError() 107 | return 108 | } 109 | n += int(delta) 110 | } 111 | } else { 112 | n = 1 113 | } 114 | c.db.update(c.args[1], itoa(n)) 115 | c.replyInt(int(n)) 116 | c.dirty++ 117 | } 118 | 119 | func setCommand(c *client) { 120 | if len(c.args) < 3 { 121 | c.replyAritryError() 122 | return 123 | } 124 | var nx, xx bool 125 | var ex, px time.Time 126 | var expires bool 127 | var when time.Time 128 | for i := 3; i < len(c.args); i++ { 129 | switch strings.ToLower(c.args[i]) { 130 | case "nx": 131 | if xx { 132 | c.replySyntaxError() 133 | return 134 | } 135 | nx = true 136 | case "xx": 137 | if nx { 138 | c.replySyntaxError() 139 | return 140 | } 141 | xx = true 142 | case "ex": 143 | if !px.IsZero() || i == len(c.args)-1 { 144 | c.replySyntaxError() 145 | return 146 | } 147 | i++ 148 | n, err := atoi(c.args[i]) 149 | if err != nil { 150 | c.replySyntaxError() 151 | return 152 | } 153 | ex = time.Now().Add(time.Duration(n) * time.Second) 154 | expires = true 155 | when = ex 156 | case "px": 157 | if !ex.IsZero() || i == len(c.args)-1 { 158 | c.replySyntaxError() 159 | return 160 | } 161 | i++ 162 | n, err := atoi(c.args[i]) 163 | if err != nil { 164 | c.replySyntaxError() 165 | return 166 | } 167 | px = time.Now().Add(time.Duration(n) * time.Millisecond) 168 | expires = true 169 | when = px 170 | } 171 | } 172 | if nx || xx { 173 | _, ok := c.db.get(c.args[1]) 174 | if (ok && nx) || (!ok && xx) { 175 | c.replyNull() 176 | return 177 | } 178 | } 179 | c.db.set(c.args[1], c.args[2]) 180 | if expires { 181 | c.db.expire(c.args[1], when) 182 | } 183 | c.replyString("OK") 184 | c.dirty++ 185 | } 186 | 187 | func setnxCommand(c *client) { 188 | if len(c.args) != 3 { 189 | c.replyAritryError() 190 | return 191 | } 192 | _, ok := c.db.get(c.args[1]) 193 | if ok { 194 | c.replyInt(0) 195 | return 196 | } 197 | c.db.set(c.args[1], c.args[2]) 198 | c.replyInt(1) 199 | c.dirty++ 200 | } 201 | 202 | func msetCommand(c *client) { 203 | if len(c.args) < 3 || (len(c.args)-1)%2 != 0 { 204 | c.replyAritryError() 205 | return 206 | } 207 | for i := 1; i < len(c.args); i += 2 { 208 | c.db.set(c.args[i+0], c.args[i+1]) 209 | c.dirty++ 210 | } 211 | c.replyString("OK") 212 | } 213 | 214 | func msetnxCommand(c *client) { 215 | if len(c.args) < 3 || (len(c.args)-1)%2 != 0 { 216 | c.replyAritryError() 217 | return 218 | } 219 | for i := 1; i < len(c.args); i += 2 { 220 | _, ok := c.db.get(c.args[1]) 221 | if ok { 222 | c.replyInt(0) 223 | return 224 | } 225 | } 226 | for i := 1; i < len(c.args); i += 2 { 227 | c.db.set(c.args[i+0], c.args[i+1]) 228 | c.dirty++ 229 | } 230 | c.replyInt(1) 231 | } 232 | 233 | func appendCommand(c *client) { 234 | if len(c.args) != 3 { 235 | c.replyAritryError() 236 | return 237 | } 238 | key, ok := c.db.get(c.args[1]) 239 | if !ok { 240 | c.db.set(c.args[1], c.args[2]) 241 | c.replyInt(len(c.args[2])) 242 | c.dirty++ 243 | return 244 | } 245 | switch s := key.(type) { 246 | default: 247 | c.replyTypeError() 248 | return 249 | case string: 250 | s += c.args[2] 251 | c.db.set(c.args[1], s) 252 | c.replyInt(len(s)) 253 | c.dirty++ 254 | } 255 | } 256 | 257 | func bitcountCommand(c *client) { 258 | var start, end int 259 | var all bool 260 | switch len(c.args) { 261 | default: 262 | c.replyAritryError() 263 | case 2: 264 | all = true 265 | case 4: 266 | n1, err1 := atoi(c.args[2]) 267 | n2, err2 := atoi(c.args[3]) 268 | if err1 != nil || err2 != nil { 269 | c.replyError("value is not an integer or out of range") 270 | return 271 | } 272 | start, end = int(n1), int(n2) 273 | } 274 | key, ok := c.db.get(c.args[1]) 275 | if !ok { 276 | c.replyInt(0) 277 | return 278 | } 279 | switch s := key.(type) { 280 | default: 281 | c.replyTypeError() 282 | return 283 | case string: 284 | var count int 285 | if all { 286 | start, end = 0, len(s) 287 | } else { 288 | if start < 0 { 289 | start = len(s) + start 290 | if start < 0 { 291 | start = 0 292 | } 293 | } 294 | if end < 0 { 295 | end = len(s) + end 296 | if end < 0 { 297 | end = 0 298 | } 299 | } 300 | } 301 | for i := start; i <= end && i < len(s); i++ { 302 | c := s[i] 303 | for j := 0; j < 8; j++ { 304 | count += int((c >> uint(j)) & 0x01) 305 | } 306 | } 307 | c.replyInt(count) 308 | } 309 | } 310 | 311 | func mgetCommand(c *client) { 312 | if len(c.args) < 2 { 313 | c.replyAritryError() 314 | return 315 | } 316 | c.replyMultiBulkLen(len(c.args) - 1) 317 | for i := 1; i < len(c.args); i++ { 318 | key, ok := c.db.get(c.args[i]) 319 | if !ok { 320 | c.replyNull() 321 | } else if s, ok := key.(string); ok { 322 | c.replyBulk(s) 323 | } else { 324 | c.replyNull() 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /server/reader.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "strconv" 8 | ) 9 | 10 | type protocolError struct { 11 | msg string 12 | } 13 | 14 | func (err *protocolError) Error() string { 15 | return "Protocol error: " + err.msg 16 | } 17 | 18 | type commandReader struct { 19 | rd io.Reader 20 | rbuf []byte 21 | buf []byte 22 | copied bool 23 | args []string 24 | } 25 | 26 | func newCommandReader(rd io.Reader) *commandReader { 27 | return &commandReader{ 28 | rd: rd, 29 | rbuf: make([]byte, 64*1024), 30 | } 31 | } 32 | 33 | // autoConvertArgsToMultiBulk converts telnet style commands to resp autobulk commands. 34 | func autoConvertArgsToMultiBulk(raw []byte, args []string, telnet, flush bool) ([]byte, []string, bool, error) { 35 | if telnet { 36 | var buf bytes.Buffer 37 | buf.WriteString("*" + strconv.FormatInt(int64(len(args)), 10) + "\r\n") 38 | for _, arg := range args { 39 | buf.WriteString("$" + strconv.FormatInt(int64(len(arg)), 10) + "\r\n") 40 | buf.WriteString(arg + "\r\n") 41 | } 42 | raw = buf.Bytes() 43 | } 44 | return raw, args, flush, nil 45 | } 46 | 47 | func (rd *commandReader) readCommand() (raw []byte, args []string, flush bool, err error) { 48 | if len(rd.buf) > 0 { 49 | // there is already data in the buffer, do we have enough to make a full command? 50 | raw, args, telnet, err := rd.readBufferedCommand(rd.buf) 51 | if err != nil { 52 | return nil, nil, false, err 53 | } 54 | if len(raw) == len(rd.buf) { 55 | // we have a command and it's exactly the size of the buffer. 56 | // clear out the buffer and return the command 57 | // notify the caller that we should flush after this command. 58 | rd.buf = nil 59 | if telnet { 60 | return autoConvertArgsToMultiBulk(raw, args, telnet, true) 61 | } 62 | return raw, args, true, nil 63 | } 64 | if len(raw) > 0 { 65 | // have a command, but there's still data in the buffer. 66 | // notify the caller that we should flush *only* when there's copied data. 67 | rd.buf = rd.buf[len(raw):] 68 | if telnet { 69 | return autoConvertArgsToMultiBulk(raw, args, telnet, rd.copied) 70 | } 71 | return raw, args, rd.copied, nil 72 | } 73 | // only have a partial command, read more data 74 | } 75 | n, err := rd.rd.Read(rd.rbuf) 76 | if err != nil { 77 | return nil, nil, false, err 78 | } 79 | if len(rd.buf) == 0 { 80 | // copy the data rather than assign a slice, otherwise string 81 | // corruption may occur on the next network read. 82 | rd.buf = append([]byte(nil), rd.rbuf[:n]...) 83 | rd.copied = false 84 | } else { 85 | rd.buf = append(rd.buf, rd.rbuf[:n]...) 86 | rd.copied = true 87 | } 88 | return rd.readCommand() 89 | } 90 | 91 | func (rd *commandReader) readBufferedCommand(data []byte) ([]byte, []string, bool, error) { 92 | if data[0] != '*' { 93 | return readBufferedTelnetCommand(data) 94 | } 95 | for i := 1; i < len(data); i++ { 96 | if data[i] == '\n' { 97 | if data[i-1] != '\r' { 98 | return nil, nil, false, &protocolError{"invalid multibulk length"} 99 | } 100 | n, err := atoi(string(data[1 : i-1])) 101 | if err != nil { 102 | return nil, nil, false, &protocolError{"invalid multibulk length"} 103 | } 104 | if n <= 0 { 105 | return data[:i+1], []string{}, false, nil 106 | } 107 | 108 | // grow the args array 109 | if n > len(rd.args) { 110 | nlen := len(rd.args) 111 | if nlen == 0 { 112 | nlen = 1 113 | } 114 | for n > nlen { 115 | nlen *= 2 116 | } 117 | rd.args = make([]string, nlen) 118 | } 119 | i++ 120 | for j := 0; j < n; j++ { 121 | if i == len(data) { 122 | return nil, nil, false, nil 123 | } 124 | if data[i] != '$' { 125 | return nil, nil, false, &protocolError{"expected '$', got '" + string(data[i]) + "'"} 126 | } 127 | ii := i + 1 128 | for ; i < len(data); i++ { 129 | if data[i] == '\n' { 130 | if data[i-1] != '\r' { 131 | return nil, nil, false, &protocolError{"invalid bulk length"} 132 | } 133 | n2, err := atoui(string(data[ii : i-1])) 134 | if err != nil { 135 | return nil, nil, false, &protocolError{"invalid bulk length"} 136 | } 137 | i++ 138 | if len(data)-i < n2+2 { 139 | return nil, nil, false, nil // more data 140 | } 141 | rd.args[j] = string(data[i : i+n2]) 142 | i += n2 + 2 143 | if j == n-1 { 144 | return data[:i], rd.args[:n], false, nil 145 | } 146 | break 147 | } 148 | } 149 | } 150 | break 151 | } 152 | } 153 | return nil, nil, false, nil // more data 154 | } 155 | 156 | func readBufferedTelnetCommand(data []byte) ([]byte, []string, bool, error) { 157 | for i := 1; i < len(data); i++ { 158 | if data[i] == '\n' { 159 | var line []byte 160 | if data[i-1] == '\r' { 161 | line = data[:i-1] 162 | } else { 163 | line = data[:i] 164 | } 165 | if len(line) == 0 { 166 | return data[:i+1], []string{}, true, nil 167 | } 168 | args, err := parseArgsFromTelnetLine(line) 169 | if err != nil { 170 | return nil, nil, true, err 171 | } 172 | return data[:i+1], args, true, nil 173 | } 174 | } 175 | return nil, nil, true, nil 176 | } 177 | 178 | func parseArgsFromTelnetLine(line []byte) ([]string, error) { 179 | var args []string 180 | var s int 181 | lspace := true 182 | quote := false 183 | lquote := false 184 | for i := 0; i < len(line); i++ { 185 | switch line[i] { 186 | default: 187 | lspace = false 188 | case '"': 189 | if quote { 190 | args = append(args, string(line[s+1:i])) 191 | quote = false 192 | s = i + 1 193 | lquote = true 194 | continue 195 | } 196 | if !lspace { 197 | return nil, &protocolError{"unbalanced quotes in request"} 198 | } 199 | lspace = false 200 | quote = true 201 | case ' ': 202 | if lquote { 203 | s++ 204 | continue 205 | } 206 | args = append(args, string(line[s:i])) 207 | s = i + 1 208 | lspace = true 209 | } 210 | } 211 | if quote { 212 | return nil, &protocolError{"unbalanced quotes in request"} 213 | } 214 | if s < len(line) { 215 | args = append(args, string(line[s:])) 216 | } 217 | return args, nil 218 | } 219 | 220 | func atoi(s string) (int, error) { 221 | if len(s) == 0 { 222 | return 0, errors.New("invalid integer") 223 | } 224 | var sign bool 225 | var n int 226 | for i := 0; i < len(s); i++ { 227 | c := s[i] 228 | if c >= '0' && c <= '9' { 229 | n = n*10 + int(c-'0') 230 | } else if c == '-' { 231 | if i != 0 || len(s) == 1 { 232 | return 0, errors.New("invalid integer") 233 | } 234 | sign = true 235 | } else { 236 | return 0, errors.New("invalid integer") 237 | } 238 | } 239 | if sign { 240 | n *= -1 241 | } 242 | return n, nil 243 | } 244 | 245 | func itoa(n int) string { 246 | return strconv.Itoa(n) 247 | } 248 | 249 | func atoui(s string) (int, error) { 250 | if len(s) == 0 { 251 | return 0, errors.New("invalid integer") 252 | } 253 | var n int 254 | for i := 0; i < len(s); i++ { 255 | c := s[i] 256 | if c >= '0' && c <= '9' { 257 | n = n*10 + int(s[i]-'0') 258 | } else { 259 | return 0, errors.New("invalid integer") 260 | } 261 | } 262 | return n, nil 263 | } 264 | -------------------------------------------------------------------------------- /server/config.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "path" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | type config struct { 15 | port int 16 | bind string 17 | bindIsLocal bool 18 | protectedMode bool 19 | requirepass string 20 | 21 | kvm map[string]string 22 | file string 23 | } 24 | 25 | type cfgerr struct { 26 | message string 27 | property string 28 | value string 29 | } 30 | 31 | func (err *cfgerr) Error() string { 32 | return fmt.Sprintf("Fatal config file error: '%s \"%s\"': %s", err.property, err.value, err.message) 33 | } 34 | 35 | func fillBoolConfigOption(configMap map[string]string, configKey string, defaultValue bool) { 36 | switch strings.ToLower(configMap[configKey]) { 37 | default: 38 | if defaultValue { 39 | configMap[configKey] = "yes" 40 | } else { 41 | configMap[configKey] = "no" 42 | } 43 | case "yes", "no": 44 | configMap[configKey] = strings.ToLower(configMap[configKey]) 45 | } 46 | } 47 | 48 | // fillOptions takes makes sure that the options are sane and 49 | // set to defaults. 50 | func fillOptions(options *Options) ( 51 | nopts *Options, 52 | configMap map[string]string, 53 | configFile string, 54 | ok bool, 55 | ) { 56 | if options == nil { 57 | options = &Options{} 58 | } 59 | if options.LogWriter == nil { 60 | options.LogWriter = os.Stderr 61 | } 62 | if options.AppendOnlyPath == "" { 63 | options.AppendOnlyPath = "appendonly.aof" 64 | } 65 | if options.AppName == "" { 66 | options.AppName = "Sider" 67 | } 68 | if options.Version == "" { 69 | options.Version = "999.999.9999" 70 | } 71 | if len(options.Args) > 0 { 72 | configMap, configFile, ok = loadConfigArgs(options) 73 | if !ok { 74 | return options, nil, "", false 75 | } 76 | } 77 | if configMap == nil { 78 | configMap = map[string]string{} 79 | } 80 | // force valid strings into each config property 81 | s := func(s string) string { 82 | return s 83 | } 84 | configMap["bind"] = s(configMap["bind"]) 85 | configMap["port"] = s(configMap["port"]) 86 | configMap["protected-mode"] = s(configMap["protected-mode"]) 87 | configMap["requirepass"] = s(configMap["requirepass"]) 88 | 89 | // defaults 90 | if configMap["port"] == "" { 91 | configMap["port"] = "6379" 92 | } 93 | fillBoolConfigOption(configMap, "protected-mode", true) 94 | return options, configMap, configFile, true 95 | } 96 | 97 | func fillConfig(configMap map[string]string, configFile string) (*config, error) { 98 | cfg := &config{} 99 | cfg.file = configFile 100 | cfg.kvm = configMap 101 | n, err := strconv.ParseUint(configMap["port"], 10, 16) 102 | if err != nil { 103 | return nil, &cfgerr{"Invalid port", "port", configMap["port"]} 104 | } 105 | cfg.port = int(n) 106 | cfg.bind = strings.ToLower(configMap["bind"]) 107 | cfg.bindIsLocal = cfg.bind == "" || cfg.bind == "127.0.0.1" || cfg.bind == "::1" || cfg.bind == "localhost" 108 | switch strings.ToLower(configMap["protected-mode"]) { 109 | default: 110 | return nil, &cfgerr{"argument must be 'yes' or 'no'", "protected-mode", configMap["protected-mode"]} 111 | case "yes": 112 | cfg.protectedMode = true 113 | case "no": 114 | cfg.protectedMode = false 115 | } 116 | cfg.requirepass = configMap["requirepass"] 117 | return cfg, nil 118 | } 119 | 120 | func loadConfigArgs(options *Options) (config map[string]string, file string, ok bool) { 121 | config = make(map[string]string) 122 | ln := 2 123 | for i := 0; i < len(options.Args); i++ { 124 | arg := options.Args[i] 125 | switch arg { 126 | default: 127 | if file == "" && !strings.HasPrefix(arg, "--") { 128 | file = arg 129 | var ok bool 130 | ln, ok = readConfigFile(file, config, options) 131 | if !ok { 132 | return nil, "", false 133 | } 134 | continue 135 | } 136 | i++ 137 | var vals []string 138 | for ; i < len(options.Args); i++ { 139 | if strings.HasPrefix(options.Args[i], "--") { 140 | break 141 | } 142 | vals = append(vals, options.Args[i]) 143 | } 144 | i += len(vals) 145 | if strings.HasPrefix(arg, "--") { 146 | arg = arg[2:] 147 | } 148 | switch arg { 149 | default: 150 | printBadConfig(arg, vals, ln, options) 151 | return nil, "", false 152 | case "port": 153 | if len(vals) != 1 { 154 | printBadConfig(arg, vals, ln, options) 155 | return nil, "", false 156 | } 157 | config["port"] = vals[0] 158 | case "bind": 159 | if len(vals) != 1 { 160 | printBadConfig(arg, vals, ln, options) 161 | return nil, "", false 162 | } 163 | config["port"] = vals[0] 164 | } 165 | ln++ 166 | case "--help", "-h": 167 | printHelp(options) 168 | return nil, "", false 169 | case "--version", "-v": 170 | printVersion(options) 171 | return nil, "", false 172 | } 173 | } 174 | return config, file, true 175 | } 176 | 177 | func readConfigFile(file string, config map[string]string, options *Options) (int, bool) { 178 | ln := 0 179 | f, err := os.Open(file) 180 | if err != nil { 181 | log(options.LogWriter, '#', "Fatal error, can't open config file '"+file+"'") 182 | return 0, false 183 | } 184 | defer f.Close() 185 | rd := bufio.NewReader(f) 186 | for { 187 | ln++ 188 | lineb, err := rd.ReadBytes('\n') 189 | if err != nil && err != io.EOF { 190 | log(options.LogWriter, '#', "Fatal error, can't open config file '"+file+"'") 191 | return 0, false 192 | } 193 | if len(lineb) == 0 { 194 | break 195 | } 196 | 197 | line := strings.TrimSpace(string(lineb)) 198 | if line == "" || strings.HasPrefix(line, "#") { 199 | continue 200 | } 201 | 202 | var arg, val string 203 | sp := strings.Index(line, " ") 204 | if sp == -1 { 205 | arg = line 206 | val = "" 207 | } else { 208 | arg = line[:sp] 209 | val = strings.TrimSpace(line[sp:]) 210 | } 211 | config[arg] = val 212 | switch arg { 213 | default: 214 | printBadConfig(line, nil, ln, options) 215 | return 0, false 216 | case "port", "protected-mode", "bind", "requirepass": 217 | if val == "" { 218 | printBadConfig(line, nil, ln, options) 219 | return 0, false 220 | } 221 | } 222 | if err == io.EOF { 223 | break 224 | } 225 | 226 | } 227 | return ln + 1, true 228 | } 229 | 230 | func mergeConfigFile(file string, config map[string]string) error { 231 | data, err := ioutil.ReadFile(file) 232 | if err != nil { 233 | return err 234 | } 235 | lines := strings.Split(strings.TrimSpace(string(data)), "\n") 236 | nextkey: 237 | for k, v := range config { 238 | for i, line := range lines { 239 | line = strings.TrimSpace(line) 240 | if line == k || strings.HasPrefix(line, k+" ") { 241 | if v == "" { 242 | // erase line 243 | lines = append(lines[:i], lines[i+1:]...) 244 | } else { 245 | // modify line 246 | lines[i] = k + " " + v 247 | } 248 | continue nextkey 249 | } 250 | } 251 | if v != "" { 252 | lines = append(lines, k+" "+v) 253 | } 254 | } 255 | return ioutil.WriteFile(file, []byte(strings.Join(lines, "\n")), 0644) 256 | } 257 | 258 | func printHelp(options *Options) { 259 | base := path.Base(os.Args[0]) 260 | io.WriteString(options.LogWriter, strings.TrimSpace(` 261 | Usage: ./`+base+` [/path/to/`+strings.ToLower(options.AppName)+`.conf] [options] 262 | ./`+base+` -v or --version 263 | ./`+base+` -h or --help 264 | 265 | Examples: 266 | ./`+base+` (run the server with default conf) 267 | ./`+base+` /etc/`+strings.ToLower(options.AppName)+`/9851.conf 268 | ./`+base+` --port 7777 269 | ./`+base+` /etc/my`+strings.ToLower(options.AppName)+`.conf --loglevel verbose 270 | `)+"\n") 271 | } 272 | 273 | func printVersion(options *Options) { 274 | fmt.Fprintf(options.LogWriter, "%s server v=%s", options.AppName, options.Version) 275 | } 276 | 277 | func printBadConfig(prop string, vals []string, ln int, options *Options) { 278 | msg := prop 279 | for _, val := range vals { 280 | msg += " \"" + val + "\"" 281 | } 282 | fmt.Fprintf(options.LogWriter, ` 283 | *** FATAL CONFIG FILE ERROR *** 284 | Reading the configuration file, at line %d 285 | >>> '%s' 286 | Bad directive or wrong number of arguments 287 | `, ln, msg) 288 | } 289 | -------------------------------------------------------------------------------- /server/set.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "strconv" 4 | 5 | type set struct { 6 | m map[string]bool 7 | } 8 | 9 | func newSet() *set { 10 | s := &set{make(map[string]bool)} 11 | return s 12 | } 13 | 14 | func (s *set) add(member string) bool { 15 | if !s.m[member] { 16 | s.m[member] = true 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | func (s *set) del(member string) bool { 23 | if s.m[member] { 24 | delete(s.m, member) 25 | return true 26 | } 27 | return false 28 | } 29 | 30 | func (s *set) ascend(iterator func(s string) bool) { 31 | for v := range s.m { 32 | if !iterator(v) { 33 | return 34 | } 35 | } 36 | } 37 | 38 | func (s1 *set) diff(s2 *set) *set { 39 | s3 := newSet() 40 | for v1 := range s1.m { 41 | found := false 42 | for v2 := range s2.m { 43 | if v1 == v2 { 44 | found = true 45 | break 46 | } 47 | } 48 | if !found { 49 | s3.m[v1] = true 50 | } 51 | } 52 | return s3 53 | } 54 | 55 | func (s1 *set) inter(s2 *set) *set { 56 | s3 := newSet() 57 | for v1 := range s1.m { 58 | found := false 59 | for v2 := range s2.m { 60 | if v1 == v2 { 61 | found = true 62 | break 63 | } 64 | } 65 | if found { 66 | s3.m[v1] = true 67 | } 68 | } 69 | return s3 70 | } 71 | 72 | func (s1 *set) union(s2 *set) *set { 73 | s3 := newSet() 74 | for v := range s1.m { 75 | s3.m[v] = true 76 | } 77 | for v := range s2.m { 78 | s3.m[v] = true 79 | } 80 | return s3 81 | } 82 | 83 | func (s *set) popRand(count int, pop bool) []string { 84 | many := false 85 | if count < 0 { 86 | if pop { 87 | return nil 88 | } else { 89 | count *= -1 90 | many = true 91 | } 92 | } 93 | var res []string 94 | if count > 1024 { 95 | res = make([]string, 0, 1024) 96 | } else { 97 | res = make([]string, 0, count) 98 | } 99 | for { 100 | for key := range s.m { 101 | if count <= 0 { 102 | break 103 | } 104 | if pop { 105 | delete(s.m, key) 106 | } 107 | res = append(res, key) 108 | count-- 109 | } 110 | if !many || count == 0 { 111 | break 112 | } 113 | } 114 | return res 115 | } 116 | func (s *set) pop(count int) []string { 117 | return s.popRand(count, true) 118 | } 119 | func (s *set) rand(count int) []string { 120 | return s.popRand(count, false) 121 | } 122 | 123 | func (s *set) isMember(member string) bool { 124 | return s.m[member] 125 | } 126 | 127 | func (s *set) len() int { 128 | return len(s.m) 129 | } 130 | 131 | func (s *set) strArr() []string { 132 | i := 0 133 | arr := make([]string, len(s.m)) 134 | for s := range s.m { 135 | arr[i] = s 136 | i++ 137 | } 138 | return arr 139 | } 140 | 141 | func (s *set) numArr() []float64 { 142 | i := 0 143 | arr := make([]float64, len(s.m)) 144 | for s := range s.m { 145 | n, err := strconv.ParseFloat(s, 64) 146 | if err != nil { 147 | return nil 148 | } 149 | arr[i] = n 150 | i++ 151 | } 152 | return arr 153 | } 154 | 155 | func saddCommand(c *client) { 156 | if len(c.args) < 3 { 157 | c.replyAritryError() 158 | return 159 | } 160 | st, ok := c.db.getSet(c.args[1], true) 161 | if !ok { 162 | c.replyTypeError() 163 | return 164 | } 165 | count := 0 166 | for i := 2; i < len(c.args); i++ { 167 | if st.add(c.args[i]) { 168 | c.dirty++ 169 | count++ 170 | } 171 | } 172 | c.replyInt(count) 173 | 174 | } 175 | 176 | func scardCommand(c *client) { 177 | if len(c.args) != 2 { 178 | c.replyAritryError() 179 | return 180 | } 181 | st, ok := c.db.getSet(c.args[1], false) 182 | if !ok { 183 | c.replyTypeError() 184 | return 185 | } 186 | if st == nil { 187 | c.replyInt(0) 188 | return 189 | } 190 | c.replyInt(st.len()) 191 | } 192 | func smembersCommand(c *client) { 193 | if len(c.args) != 2 { 194 | c.replyAritryError() 195 | return 196 | } 197 | st, ok := c.db.getSet(c.args[1], false) 198 | if !ok { 199 | c.replyTypeError() 200 | return 201 | } 202 | if st == nil { 203 | c.replyMultiBulkLen(0) 204 | return 205 | } 206 | c.replyMultiBulkLen(st.len()) 207 | st.ascend(func(s string) bool { 208 | c.replyBulk(s) 209 | return true 210 | }) 211 | } 212 | func sismembersCommand(c *client) { 213 | if len(c.args) != 3 { 214 | c.replyAritryError() 215 | return 216 | } 217 | st, ok := c.db.getSet(c.args[1], false) 218 | if !ok { 219 | c.replyTypeError() 220 | return 221 | } 222 | if st == nil { 223 | c.replyInt(0) 224 | return 225 | } 226 | if st.isMember(c.args[2]) { 227 | c.replyInt(1) 228 | } else { 229 | c.replyInt(0) 230 | } 231 | } 232 | 233 | func sdiffinterunionGenericCommand(c *client, diff, union bool, store bool) { 234 | if (!store && len(c.args) < 2) || (store && len(c.args) < 3) { 235 | c.replyAritryError() 236 | return 237 | } 238 | basei := 1 239 | if store { 240 | basei = 2 241 | } 242 | var st *set 243 | for i := basei; i < len(c.args); i++ { 244 | stt, ok := c.db.getSet(c.args[i], false) 245 | if !ok { 246 | c.replyTypeError() 247 | return 248 | } 249 | if stt == nil { 250 | if diff || union { 251 | continue 252 | } else { 253 | st = nil 254 | break 255 | } 256 | } 257 | if st == nil { 258 | st = stt 259 | } else { 260 | if diff { 261 | st = st.diff(stt) 262 | } else if union { 263 | st = st.union(stt) 264 | } else { 265 | st = st.inter(stt) 266 | } 267 | } 268 | } 269 | if store { 270 | if st == nil || st.len() == 0 { 271 | _, ok := c.db.del(c.args[1]) 272 | if ok { 273 | c.dirty++ 274 | } 275 | c.replyInt(0) 276 | } else { 277 | c.db.set(c.args[1], st) 278 | c.dirty++ 279 | c.replyInt(st.len()) 280 | } 281 | } else { 282 | if st == nil { 283 | c.replyMultiBulkLen(0) 284 | return 285 | } 286 | c.replyMultiBulkLen(st.len()) 287 | st.ascend(func(s string) bool { 288 | c.replyBulk(s) 289 | return true 290 | }) 291 | } 292 | } 293 | func sdiffCommand(c *client) { 294 | sdiffinterunionGenericCommand(c, true, false, false) 295 | } 296 | func sinterCommand(c *client) { 297 | sdiffinterunionGenericCommand(c, false, false, false) 298 | } 299 | func sunionCommand(c *client) { 300 | sdiffinterunionGenericCommand(c, false, true, false) 301 | } 302 | func sdiffstoreCommand(c *client) { 303 | sdiffinterunionGenericCommand(c, true, false, true) 304 | } 305 | func sinterstoreCommand(c *client) { 306 | sdiffinterunionGenericCommand(c, false, false, true) 307 | } 308 | func sunionstoreCommand(c *client) { 309 | sdiffinterunionGenericCommand(c, false, true, true) 310 | } 311 | 312 | func srandmemberpopGenericCommand(c *client, pop bool) { 313 | if len(c.args) < 2 || len(c.args) > 3 { 314 | c.replyAritryError() 315 | return 316 | } 317 | countSpecified := false 318 | count := 1 319 | if len(c.args) > 2 { 320 | n, err := strconv.ParseInt(c.args[2], 10, 64) 321 | if err != nil { 322 | c.replyInvalidIntError() 323 | return 324 | } 325 | if pop && n < 0 { 326 | c.replyError("index out of range") 327 | return 328 | } 329 | count = int(n) 330 | countSpecified = true 331 | } 332 | st, ok := c.db.getSet(c.args[1], false) 333 | if !ok { 334 | c.replyTypeError() 335 | return 336 | } 337 | if st == nil { 338 | if countSpecified { 339 | c.replyMultiBulkLen(0) 340 | } else { 341 | c.replyNull() 342 | } 343 | return 344 | } 345 | var res []string 346 | if pop { 347 | res = st.pop(count) 348 | c.dirty += len(res) 349 | } else { 350 | res = st.rand(count) 351 | } 352 | if countSpecified { 353 | c.replyMultiBulkLen(len(res)) 354 | } else if len(res) == 0 { 355 | c.replyNull() 356 | } 357 | for _, s := range res { 358 | c.replyBulk(s) 359 | if !countSpecified { 360 | break 361 | } 362 | } 363 | if pop && st.len() == 0 { 364 | c.db.del(c.args[1]) 365 | } 366 | } 367 | 368 | func srandmemberCommand(c *client) { 369 | srandmemberpopGenericCommand(c, false) 370 | } 371 | 372 | func spopCommand(c *client) { 373 | srandmemberpopGenericCommand(c, true) 374 | } 375 | 376 | func sremCommand(c *client) { 377 | if len(c.args) < 3 { 378 | c.replyAritryError() 379 | return 380 | } 381 | st, ok := c.db.getSet(c.args[1], false) 382 | if !ok { 383 | c.replyTypeError() 384 | return 385 | } 386 | if st == nil { 387 | c.replyInt(0) 388 | return 389 | } 390 | var count int 391 | for i := 2; i < len(c.args); i++ { 392 | if st.del(c.args[i]) { 393 | count++ 394 | c.dirty++ 395 | } 396 | } 397 | if st.len() == 0 { 398 | c.db.del(c.args[1]) 399 | } 400 | c.replyInt(count) 401 | } 402 | 403 | func smoveCommand(c *client) { 404 | if len(c.args) != 4 { 405 | c.replyAritryError() 406 | return 407 | } 408 | src, ok := c.db.getSet(c.args[1], false) 409 | if !ok { 410 | c.replyTypeError() 411 | return 412 | } 413 | dst, ok := c.db.getSet(c.args[2], false) 414 | if !ok { 415 | c.replyTypeError() 416 | return 417 | } 418 | if src == nil { 419 | c.replyInt(0) 420 | return 421 | } 422 | if !src.del(c.args[3]) { 423 | c.replyInt(0) 424 | return 425 | } 426 | if dst == nil { 427 | dst = newSet() 428 | dst.add(c.args[3]) 429 | c.db.set(c.args[2], dst) 430 | c.replyInt(1) 431 | c.dirty++ 432 | return 433 | } 434 | dst.add(c.args[3]) 435 | c.replyInt(1) 436 | c.dirty++ 437 | } 438 | -------------------------------------------------------------------------------- /server/keys.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "sort" 5 | "strconv" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | func delCommand(c *client) { 11 | if len(c.args) < 2 { 12 | c.replyAritryError() 13 | return 14 | } 15 | count := 0 16 | for i := 1; i < len(c.args); i++ { 17 | if _, ok := c.db.del(c.args[i]); ok { 18 | count++ 19 | } 20 | } 21 | c.dirty += count 22 | c.replyInt(count) 23 | } 24 | 25 | func renameCommand(c *client) { 26 | if len(c.args) != 3 { 27 | c.replyAritryError() 28 | return 29 | } 30 | key, ok := c.db.get(c.args[1]) 31 | if !ok { 32 | c.replyError("no such key") 33 | return 34 | } 35 | c.db.del(c.args[1]) 36 | c.db.set(c.args[2], key) 37 | c.dirty++ 38 | c.replyString("OK") 39 | } 40 | 41 | func renamenxCommand(c *client) { 42 | if len(c.args) != 3 { 43 | c.replyAritryError() 44 | return 45 | } 46 | key, ok := c.db.get(c.args[1]) 47 | if !ok { 48 | c.replyError("no such key") 49 | return 50 | } 51 | _, ok = c.db.get(c.args[2]) 52 | if ok { 53 | c.replyInt(0) 54 | return 55 | } 56 | c.db.del(c.args[1]) 57 | c.db.set(c.args[2], key) 58 | c.replyInt(1) 59 | c.dirty++ 60 | } 61 | 62 | func keysCommand(c *client) { 63 | if len(c.args) != 2 { 64 | c.replyAritryError() 65 | return 66 | } 67 | var keys []string 68 | pattern := parsePattern(c.args[1]) 69 | c.db.ascend(func(key string, value interface{}) bool { 70 | if pattern.match(key) { 71 | keys = append(keys, key) 72 | } 73 | return true 74 | }) 75 | c.replyMultiBulkLen(len(keys)) 76 | for _, key := range keys { 77 | c.replyString(key) 78 | } 79 | } 80 | 81 | func typeCommand(c *client) { 82 | if len(c.args) != 2 { 83 | c.replyAritryError() 84 | return 85 | } 86 | typ := c.db.getType(c.args[1]) 87 | c.replyString(typ) 88 | } 89 | 90 | func randomkeyCommand(c *client) { 91 | if len(c.args) != 1 { 92 | c.replyAritryError() 93 | return 94 | } 95 | got := false 96 | c.db.ascend(func(key string, value interface{}) bool { 97 | c.replyBulk(key) 98 | got = true 99 | return false 100 | }) 101 | if !got { 102 | c.replyNull() 103 | } 104 | } 105 | 106 | func existsCommand(c *client) { 107 | if len(c.args) == 1 { 108 | c.replyAritryError() 109 | return 110 | } 111 | var count int 112 | for i := 1; i < len(c.args); i++ { 113 | if _, ok := c.db.get(c.args[i]); ok { 114 | count++ 115 | } 116 | } 117 | c.replyInt(count) 118 | } 119 | func expireCommand(c *client) { 120 | if len(c.args) != 3 { 121 | c.replyAritryError() 122 | return 123 | } 124 | seconds, err := strconv.ParseInt(c.args[2], 10, 64) 125 | if err != nil { 126 | c.replyError("value is not an integer or out of range") 127 | return 128 | } 129 | if c.db.expire(c.args[1], time.Now().Add(time.Duration(seconds)*time.Second)) { 130 | c.replyInt(1) 131 | c.dirty++ 132 | } else { 133 | c.replyInt(0) 134 | } 135 | } 136 | func ttlCommand(c *client) { 137 | if len(c.args) != 2 { 138 | c.replyAritryError() 139 | return 140 | } 141 | _, expires, ok := c.db.getExpires(c.args[1]) 142 | if !ok { 143 | c.replyInt(-2) 144 | } else if expires.IsZero() { 145 | c.replyInt(-1) 146 | } else { 147 | c.replyInt(int(expires.Sub(time.Now()) / time.Second)) 148 | } 149 | } 150 | func moveCommand(c *client) { 151 | if len(c.args) != 3 { 152 | c.replyAritryError() 153 | return 154 | } 155 | num, err := strconv.ParseUint(c.args[2], 10, 32) 156 | if err != nil { 157 | c.replyError("index out of range") 158 | return 159 | } 160 | 161 | value, ok := c.db.get(c.args[1]) 162 | if !ok { 163 | c.replyInt(0) 164 | return 165 | } 166 | db := c.s.selectDB(int(num)) 167 | _, ok = db.get(c.args[1]) 168 | if ok { 169 | c.replyInt(0) 170 | return 171 | } 172 | db.set(c.args[1], value) 173 | c.db.del(c.args[1]) 174 | c.replyInt(1) 175 | c.dirty++ 176 | } 177 | 178 | type sortValues struct { 179 | db *database 180 | asc bool 181 | alpha bool 182 | arr []string 183 | byPrefix string 184 | bySuffix string 185 | byProvided bool 186 | invalid bool 187 | } 188 | 189 | func (v *sortValues) Len() int { 190 | return len(v.arr) 191 | } 192 | 193 | func isless(s1, s2 string, alpha bool) (less bool, ok bool) { 194 | if alpha { 195 | return s1 < s2, true 196 | } 197 | n1, err := strconv.ParseFloat(s1, 64) 198 | if err != nil { 199 | return false, false 200 | } 201 | n2, err := strconv.ParseFloat(s2, 64) 202 | if err != nil { 203 | return false, false 204 | } 205 | return n1 < n2, true 206 | } 207 | 208 | func (v *sortValues) Less(i, j int) bool { 209 | if v.invalid { 210 | return false 211 | } 212 | s1, s2 := v.arr[i], v.arr[j] 213 | 214 | // test if the values are less from the 'by' clause 215 | if v.byProvided { 216 | v1, _ := v.db.get(v.byPrefix + s1 + v.bySuffix) 217 | v2, _ := v.db.get(v.byPrefix + s2 + v.bySuffix) 218 | if vs1, ok := v1.(string); ok { 219 | if vs2, ok := v2.(string); ok { 220 | // both exist, compare 221 | if !v.alpha { 222 | vn1, err := strconv.ParseFloat(vs1, 64) 223 | if err != nil { 224 | v.invalid = true 225 | return false 226 | } 227 | vn2, err := strconv.ParseFloat(vs2, 64) 228 | if err != nil { 229 | v.invalid = true 230 | return false 231 | } 232 | if vn1 != vn2 { 233 | return vn1 < vn2 234 | } 235 | } else { 236 | if vs1 != vs2 { 237 | return vs1 < vs2 238 | } 239 | } 240 | } else { 241 | // 1st exists, but not the 2nd, return false 242 | if !v.alpha { 243 | _, err := strconv.ParseFloat(vs1, 64) 244 | if err != nil { 245 | v.invalid = true 246 | return false 247 | } 248 | } 249 | return false 250 | } 251 | } else if vs2, ok := v2.(string); ok { 252 | // 2nd exists, but not the 1st, return true 253 | if !v.alpha { 254 | _, err := strconv.ParseFloat(vs2, 64) 255 | if err != nil { 256 | v.invalid = true 257 | return false 258 | } 259 | } 260 | return true 261 | } else { 262 | // neither exist, noop 263 | } 264 | } 265 | 266 | less, ok := isless(s1, s2, v.alpha) 267 | if !ok { 268 | v.invalid = true 269 | return false 270 | } 271 | return less 272 | } 273 | 274 | func (v *sortValues) Swap(i, j int) { 275 | v.arr[i], v.arr[j] = v.arr[j], v.arr[i] 276 | } 277 | 278 | func sortCommand(c *client) { 279 | if len(c.args) < 2 { 280 | c.replyAritryError() 281 | return 282 | } 283 | asc := true 284 | alpha := false 285 | store := "" 286 | storeProvided := false 287 | offset := 0 288 | count := 0 289 | limitProvided := false 290 | by := "" 291 | byPrefix := "" 292 | bySuffix := "" 293 | byProvided := false 294 | noSort := false 295 | gets := []string(nil) 296 | for i := 2; i < len(c.args); i++ { 297 | switch strings.ToLower(c.args[i]) { 298 | default: 299 | c.replySyntaxError() 300 | return 301 | case "get": 302 | i++ 303 | if i == len(c.args) { 304 | c.replySyntaxError() 305 | return 306 | } 307 | gets = append(gets, c.args[i]) 308 | case "by": 309 | i++ 310 | if i == len(c.args) { 311 | c.replySyntaxError() 312 | return 313 | } 314 | by = c.args[i] 315 | idx := strings.Index(by, "*") 316 | if idx != -1 { 317 | byPrefix = by[:idx] 318 | bySuffix = by[idx+1:] 319 | byProvided = true 320 | } else { 321 | noSort = true 322 | } 323 | case "alpha": 324 | alpha = true 325 | case "asc": 326 | asc = true 327 | case "desc": 328 | asc = false 329 | case "store": 330 | i++ 331 | if i == len(c.args) { 332 | c.replySyntaxError() 333 | return 334 | } 335 | storeProvided = true 336 | store = c.args[i] 337 | case "limit": 338 | i += 2 339 | if i == len(c.args) { 340 | c.replySyntaxError() 341 | return 342 | } 343 | limitProvided = true 344 | n1, err := strconv.ParseInt(c.args[i-1], 10, 64) 345 | if err != nil { 346 | c.replyError("value is not an integer or out of range") 347 | return 348 | } 349 | n2, err := strconv.ParseInt(c.args[i], 10, 64) 350 | if err != nil { 351 | c.replyError("value is not an integer or out of range") 352 | return 353 | } 354 | if n1 < 0 { 355 | n1 = 0 356 | } 357 | if n2 < 0 { 358 | n2 = 0 359 | } 360 | offset = int(n1) 361 | count = int(n2) 362 | } 363 | } 364 | if false { 365 | println(asc, alpha, store, storeProvided, offset, count, limitProvided, by, byProvided, gets) 366 | } 367 | key, ok := c.db.get(c.args[1]) 368 | if !ok || key == nil { 369 | c.replyMultiBulkLen(0) 370 | return 371 | } 372 | 373 | var arr []string 374 | switch v := key.(type) { 375 | default: 376 | c.replyTypeError() 377 | return 378 | case *list: 379 | arr = v.strArr() 380 | case *set: 381 | arr = v.strArr() 382 | } 383 | if limitProvided { 384 | if offset >= len(arr) { 385 | c.replyMultiBulkLen(0) 386 | return 387 | } 388 | if offset+count > len(arr) { 389 | count = len(arr) - offset 390 | } 391 | } 392 | if !noSort { 393 | values := &sortValues{ 394 | db: c.db, 395 | asc: asc, 396 | alpha: alpha, 397 | arr: arr, 398 | byPrefix: byPrefix, 399 | bySuffix: bySuffix, 400 | byProvided: byProvided, 401 | } 402 | sort.Sort(values) 403 | if values.invalid { 404 | c.replyError("One or more scores can't be converted into double") 405 | return 406 | } 407 | } 408 | if limitProvided { 409 | arr = arr[offset : offset+count] 410 | } 411 | if storeProvided { 412 | l := newList() 413 | l.rpush(arr...) 414 | c.db.set(store, l) 415 | c.replyInt(l.len()) 416 | c.dirty++ 417 | return 418 | } 419 | c.replyMultiBulkLen(len(arr)) 420 | for _, value := range arr { 421 | c.replyBulk(value) 422 | } 423 | } 424 | 425 | func expireatCommand(c *client) { 426 | if len(c.args) != 3 { 427 | c.replyAritryError() 428 | return 429 | } 430 | seconds, err := strconv.ParseInt(c.args[2], 10, 64) 431 | if err != nil { 432 | c.replyError("value is not an integer or out of range") 433 | return 434 | } 435 | if c.db.expire(c.args[1], time.Unix(seconds, 0)) { 436 | c.replyInt(1) 437 | c.dirty++ 438 | } else { 439 | c.replyInt(0) 440 | } 441 | } 442 | -------------------------------------------------------------------------------- /server/aof.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "path" 11 | "sort" 12 | "strconv" 13 | "time" 14 | ) 15 | 16 | // openAOF opens the appendonly.aof file and loads it. 17 | // There is also a background goroutine that syncs every seconds. 18 | func (s *Server) openAOF() error { 19 | f, err := os.OpenFile(s.aofPath, os.O_CREATE|os.O_RDWR, 0644) 20 | if err != nil { 21 | return err 22 | } 23 | s.aof = f 24 | go func() { 25 | t := time.NewTicker(time.Second) 26 | defer t.Stop() 27 | for range t.C { 28 | s.mu.Lock() 29 | if s.aofclosed { 30 | s.mu.Unlock() 31 | return 32 | } 33 | s.aof.Sync() 34 | s.mu.Unlock() 35 | } 36 | }() 37 | return s.loadAOF() 38 | } 39 | 40 | func writeBulk(wr io.Writer, arg string) { 41 | fmt.Fprintf(wr, "$%d\r\n%s\r\n", len(arg), arg) 42 | } 43 | func writeMultiBulkLen(wr io.Writer, n int) { 44 | fmt.Fprintf(wr, "*%d\r\n", n) 45 | } 46 | func writeMultiBulk(wr io.Writer, args ...interface{}) { 47 | writeMultiBulkLen(wr, len(args)) 48 | for _, arg := range args { 49 | var sarg string 50 | switch v := arg.(type) { 51 | default: 52 | sarg = fmt.Sprintf("%v", v) 53 | case string: 54 | sarg = v 55 | } 56 | writeBulk(wr, sarg) 57 | } 58 | } 59 | 60 | type dbsByNumber []*database 61 | 62 | func (a dbsByNumber) Len() int { 63 | return len(a) 64 | } 65 | 66 | func (a dbsByNumber) Less(i, j int) bool { 67 | return a[i].num < a[j].num 68 | } 69 | 70 | func (a dbsByNumber) Swap(i, j int) { 71 | a[i], a[j] = a[j], a[i] 72 | } 73 | 74 | type keyItemByKey struct { 75 | keys []string 76 | items []dbItem 77 | } 78 | 79 | func (a *keyItemByKey) Len() int { 80 | return len(a.keys) 81 | } 82 | 83 | func (a *keyItemByKey) Less(i, j int) bool { 84 | return a.keys[i] < a.keys[j] 85 | } 86 | 87 | func (a *keyItemByKey) Swap(i, j int) { 88 | a.keys[i], a.keys[j] = a.keys[j], a.keys[i] 89 | a.items[i], a.items[j] = a.items[j], a.items[i] 90 | } 91 | 92 | // rewriteAOF triggers a background rewrite of the AOF file. 93 | // Returns true if the process was started, or false if the the process a 94 | // rewrite is already in progress. There are a number of locks and unlocks which 95 | // is required to keep the server running in the foreground and background. We 96 | // process in chunks and try not to hang on to locks for longer than we need. 97 | // The rewrite process will slow down the main server a little bit but it 98 | // shouldn't be too noticeable. 99 | func (s *Server) rewriteAOF() bool { 100 | if s.aofrewrite { 101 | return false 102 | } 103 | s.aofrewrite = true 104 | s.lnoticef("Background append only file rewriting started") 105 | go func() { 106 | // We use one err variable for the entire process. When we encounter an 107 | // error we should assign this variable and return. Before calling 108 | // return there we should be in lock mode (s.mu.Lock()). 109 | var err error 110 | s.mu.Lock() 111 | defer func() { 112 | if err == nil { 113 | s.lnoticef("Background AOF rewrite finished successfully") 114 | } else { 115 | s.lnoticef("Background AOF rewrite failed: %v", err) 116 | } 117 | s.aofrewrite = false 118 | s.mu.Unlock() 119 | }() 120 | 121 | // Create a temporary aof file for writting the new commands to. If this 122 | // process is successful then this file will become the active AOF. 123 | 124 | tempName := path.Join(path.Dir(s.aofPath), 125 | fmt.Sprintf("temp-rewrite-%d.aof", os.Getpid())) 126 | var f *os.File 127 | f, err = os.Create(tempName) 128 | if err != nil { 129 | return 130 | } 131 | defer func() { 132 | // If this process was successful then the next close/removeall 133 | // calls are noops. Otherwise we need to cleanup. 134 | f.Close() 135 | os.RemoveAll(tempName) 136 | }() 137 | 138 | // We use a buffered writer instead of writing directly to the file. 139 | // Doing so keeps makes the process much quicker by avoiding too many 140 | // writes to the file. 141 | wr := bufio.NewWriter(f) 142 | 143 | // Get the size of the active AOF file, and get the last DB num that was 144 | // used when the previous command was written. These both will be used 145 | // to return to the active AOF file and sync the remaining commands 146 | // which reflect changes that have occured since the start of the 147 | // rewrite. 148 | var lastpos int64 149 | lastpos, err = s.aof.Seek(0, 1) 150 | if err != nil { 151 | return 152 | } 153 | lastdbnum := s.aofdbnum 154 | s.ldebugf("AOF starting pos: %v, dbnum: %v", lastpos, lastdbnum) 155 | s.mu.Unlock() 156 | 157 | // From here until near the end of the process we will only use read 158 | // locks since we are not mutating the database during the rewrite. 159 | // This will allow for other clients to do reading commands such as 160 | // GET without penalty. Writing command such as SET will see a slight 161 | // delay, but we refresh the read locks often enough that the won't 162 | // be much of a performance hit. 163 | 164 | // Read all dbs into a local array 165 | s.mu.RLock() 166 | dbs := make([]*database, len(s.dbs)) 167 | var i int 168 | for _, db := range s.dbs { 169 | dbs[i] = db 170 | i++ 171 | } 172 | s.mu.RUnlock() 173 | 174 | // Sort the dbs by number. 175 | sort.Sort(dbsByNumber(dbs)) 176 | 177 | dbnum := -1 178 | 179 | // Use single time for all expires. 180 | now := time.Now() 181 | 182 | // Loop though local db array proessing each one. Since we are iterating 183 | // a map there the order of the databases will be random. 184 | for _, db := range dbs { 185 | s.mu.RLock() 186 | if len(db.items) == 0 { 187 | s.mu.RUnlock() 188 | continue // skip empty databases 189 | } 190 | // write a SELECT command. If the first command is `SELECT 0` then 191 | // skip this write. 192 | if !(dbnum == -1 && db.num == 0) { 193 | writeMultiBulk(wr, "SELECT", db.num) 194 | } 195 | dbnum = db.num 196 | // collect db items (keys) into local variables 197 | var msets []interface{} 198 | 199 | keys := make([]string, len(db.items)) 200 | items := make([]dbItem, len(db.items)) 201 | expires := make(map[string]time.Time) 202 | expireKeys := make([]string, len(expires)) 203 | i := 0 204 | for key, item := range db.items { 205 | items[i] = item 206 | keys[i] = key 207 | i++ 208 | } 209 | i = 0 210 | for key, t := range db.expires { 211 | expires[key] = t 212 | expireKeys[i] = key 213 | i++ 214 | } 215 | // Sort the keys and let the lock breath for a moment 216 | s.mu.RUnlock() 217 | sort.Strings(expireKeys) 218 | sort.Sort(&keyItemByKey{keys, items}) 219 | s.mu.RLock() 220 | for i := 0; i < len(keys); i++ { 221 | key := keys[i] 222 | item := items[i] 223 | if i%100 == 0 { 224 | // let the lock breath for a moment 225 | s.mu.RUnlock() 226 | err = wr.Flush() 227 | if err != nil { 228 | s.mu.Lock() // lock write on error 229 | return 230 | } 231 | s.mu.RLock() 232 | } 233 | expired := false 234 | if item.expires { 235 | if t, ok := expires[key]; ok { 236 | seconds := int((t.Sub(now) / time.Second) + 1) 237 | if seconds <= 0 { 238 | expired = true 239 | } 240 | } 241 | } 242 | if !expired { 243 | switch v := item.value.(type) { 244 | default: 245 | s.mu.RUnlock() // unlock read 246 | s.mu.Lock() // lock write on error 247 | err = errors.New("invalid type in database") 248 | case string: 249 | if len(msets) == 0 { 250 | msets = append(msets, "MSET", key, v) 251 | } else { 252 | msets = append(msets, key, v) 253 | } 254 | if len(msets) >= 20 { 255 | writeMultiBulk(wr, msets...) 256 | msets = nil 257 | } 258 | case *list: 259 | var strs []interface{} 260 | v.ascend(func(v string) bool { 261 | if len(strs) == 0 { 262 | strs = append(strs, "RPUSH", v) 263 | } else { 264 | strs = append(strs, v) 265 | } 266 | if len(strs) >= 20 { 267 | writeMultiBulk(wr, strs...) 268 | strs = nil 269 | } 270 | return true 271 | }) 272 | if len(strs) != 0 { 273 | writeMultiBulk(wr, strs...) 274 | strs = nil 275 | } 276 | case *set: 277 | var strs []interface{} 278 | v.ascend(func(v string) bool { 279 | if len(strs) == 0 { 280 | strs = append(strs, "SADD", v) 281 | } else { 282 | strs = append(strs, v) 283 | } 284 | if len(strs) >= 20 { 285 | writeMultiBulk(wr, strs...) 286 | strs = nil 287 | } 288 | return true 289 | }) 290 | if len(strs) != 0 { 291 | writeMultiBulk(wr, strs...) 292 | strs = nil 293 | } 294 | } 295 | } 296 | } 297 | if len(msets) != 0 { 298 | writeMultiBulk(wr, msets...) 299 | msets = nil 300 | } 301 | // write expires 302 | for _, key := range expireKeys { 303 | t := expires[key] 304 | seconds := int((t.Sub(now) / time.Second) + 1) 305 | if seconds > 0 { 306 | writeMultiBulk(wr, "EXPIRE", key, seconds) 307 | } 308 | } 309 | s.mu.RUnlock() 310 | } 311 | err = wr.Flush() 312 | if err != nil { 313 | s.mu.Lock() // relock on error 314 | return 315 | } 316 | // time.Sleep(time.Second * 10) // artifical delay 317 | s.mu.Lock() 318 | 319 | // The base aof has been rewritten. There may have been new aof 320 | // commands since the start of the rewrite. Let's find out! 321 | // First flush the current aof. 322 | s.flushAOF() 323 | 324 | // Then skip to the position in the live aof. 325 | var cf *os.File 326 | cf, err = os.Open(s.aofPath) 327 | if err != nil { 328 | return 329 | } 330 | defer cf.Close() 331 | var ln int64 332 | ln, err = cf.Seek(0, 2) 333 | if err != nil { 334 | return 335 | } 336 | 337 | // Write out the new commands that were added since the rewrite began. 338 | if ln != lastpos { 339 | if lastdbnum != dbnum { 340 | writeMultiBulk(wr, "SELECT", lastdbnum) 341 | } 342 | err = wr.Flush() 343 | if err != nil { 344 | return 345 | } 346 | _, err = cf.Seek(lastpos, 0) 347 | if err != nil { 348 | return 349 | } 350 | _, err = io.Copy(f, cf) 351 | if err != nil { 352 | return 353 | } 354 | s.lnoticef("Residual parent diff successfully flushed to the "+ 355 | "rewritten AOF (%0.2f MB)", float64(ln-lastpos)/1024.0/1024.0) 356 | } 357 | 358 | // Close the all of temp file resources. 359 | if err = cf.Close(); err != nil { 360 | return 361 | } 362 | if err = f.Close(); err != nil { 363 | return 364 | } 365 | 366 | // Finally switch out the aof files, failures here can by sucky 367 | if err = os.Rename(tempName, s.aofPath); err != nil { 368 | return 369 | } 370 | 371 | var nf *os.File 372 | nf, err = os.OpenFile(s.aofPath, os.O_CREATE|os.O_RDWR, 0644) 373 | if err != nil { 374 | s.fatalError(err) 375 | return 376 | } 377 | if _, err = nf.Seek(0, 2); err != nil { 378 | s.fatalError(err) 379 | return 380 | } 381 | s.aof.Close() 382 | s.aof = nf 383 | 384 | // We are really really done. Celebrate with a bag of Funyuns! 385 | 386 | }() 387 | return true 388 | } 389 | 390 | // flushAOF flushes the 391 | func (s *Server) flushAOF() error { 392 | if s.dbs[s.aofdbnum] != nil { 393 | db := s.dbs[s.aofdbnum] 394 | if db.aofbuf.Len() > 0 { 395 | if _, err := s.aof.Write(db.aofbuf.Bytes()); err != nil { 396 | return err 397 | } 398 | db.aofbuf.Reset() 399 | } 400 | } 401 | for num, db := range s.dbs { 402 | if db.aofbuf.Len() > 0 { 403 | selstr := strconv.FormatInt(int64(num), 10) 404 | lenstr := strconv.FormatInt(int64(len(selstr)), 10) 405 | if _, err := s.aof.WriteString("*2\r\n$6\r\nSELECT\r\n$" + lenstr + 406 | "\r\n" + selstr + "\r\n"); err != nil { 407 | return err 408 | } 409 | if _, err := s.aof.Write(db.aofbuf.Bytes()); err != nil { 410 | return err 411 | } 412 | db.aofbuf.Reset() 413 | s.aofdbnum = num 414 | } 415 | } 416 | return nil 417 | } 418 | 419 | func (s *Server) closeAOF() { 420 | s.mu.Lock() 421 | defer s.mu.Unlock() 422 | s.flushAOF() 423 | s.aof.Sync() 424 | s.aof.Close() 425 | s.aofclosed = true 426 | } 427 | 428 | func (s *Server) loadAOF() error { 429 | start := time.Now() 430 | rd := &commandReader{rd: s.aof, rbuf: make([]byte, 64*1024)} 431 | c := &client{wr: ioutil.Discard, s: s} 432 | c.db = s.selectDB(0) 433 | defer func() { 434 | s.aofdbnum = c.db.num 435 | }() 436 | var read int 437 | for { 438 | raw, args, _, err := rd.readCommand() 439 | if err != nil { 440 | if err == io.EOF { 441 | break 442 | } 443 | s.lwarningf("%v", err) 444 | return err 445 | } 446 | c.args = args 447 | c.raw = raw 448 | commandName := autocase(args[0]) 449 | if cmd, ok := s.cmds[commandName]; ok { 450 | cmd.funct(c) 451 | } else { 452 | return errors.New("unknown command '" + args[0] + "'") 453 | } 454 | read++ 455 | } 456 | s.lnoticef("DB loaded from disk: %.3f seconds", 457 | float64(time.Now().Sub(start))/float64(time.Second)) 458 | return nil 459 | } 460 | -------------------------------------------------------------------------------- /server/list.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type listItem struct { 10 | value string 11 | prev *listItem 12 | next *listItem 13 | } 14 | type list struct { 15 | count int 16 | front *listItem 17 | back *listItem 18 | } 19 | 20 | func newList() *list { 21 | return &list{} 22 | } 23 | 24 | // ridx resolves an index, the input could be a negative number. 25 | // The output is always a valid index. 26 | // The return bool is false when the index could not be resolved. 27 | // An input of -1 is equal to l.len()-1 28 | // When the 'outside' param is true, an index outside the range is accepted. 29 | // Returns the resolved index and a bool indicating that the resolved index is within range. 30 | func (l *list) ridx(idx int, outside bool) (resolvedRange, resolvedAny int, ok bool) { 31 | var oidx int 32 | if l.count == 0 { 33 | return 0, 0, false 34 | } 35 | if idx < 0 { 36 | idx = l.count + idx 37 | oidx = idx 38 | if idx < 0 { 39 | if !outside { 40 | return 0, 0, false 41 | } else { 42 | idx = 0 43 | } 44 | } 45 | } else if idx >= l.count { 46 | oidx = idx 47 | if !outside { 48 | return 0, 0, false 49 | } else { 50 | idx = l.count - 1 51 | } 52 | } else { 53 | oidx = idx 54 | } 55 | return idx, oidx, true 56 | } 57 | 58 | func (l *list) lindex(idx int) (value string, ok bool) { 59 | if idx, _, ok = l.ridx(idx, false); !ok { 60 | return "", false 61 | } 62 | if idx < l.count/2 { 63 | i := 0 64 | el := l.front 65 | for el != nil { 66 | if i == idx { 67 | return el.value, true 68 | } 69 | el = el.next 70 | i++ 71 | } 72 | } else { 73 | i := l.count - 1 74 | el := l.back 75 | for el != nil { 76 | if i == idx { 77 | return el.value, true 78 | } 79 | el = el.prev 80 | i-- 81 | } 82 | } 83 | return "", false 84 | } 85 | 86 | func (l *list) len() int { 87 | return l.count 88 | } 89 | 90 | func (l *list) lpop() (value string, ok bool) { 91 | if l.count == 0 { 92 | return "", false 93 | } 94 | el := l.front 95 | l.front = el.next 96 | if l.front == nil { 97 | l.back = nil 98 | } else { 99 | l.front.prev = nil 100 | } 101 | l.count-- 102 | return el.value, true 103 | } 104 | 105 | func (l *list) rpop() (value string, ok bool) { 106 | if l.count == 0 { 107 | return "", false 108 | } 109 | el := l.back 110 | l.back = el.prev 111 | if l.back == nil { 112 | l.front = nil 113 | } else { 114 | l.back.next = nil 115 | } 116 | l.count-- 117 | return el.value, true 118 | } 119 | 120 | func (l *list) lpush(values ...string) int { 121 | for _, value := range values { 122 | el := &listItem{value: value} 123 | if l.front == nil { 124 | l.back = el 125 | l.front = el 126 | } else { 127 | l.front.prev = el 128 | el.next = l.front 129 | l.front = el 130 | } 131 | } 132 | l.count += len(values) 133 | return l.count 134 | } 135 | 136 | func (l *list) rpush(values ...string) int { 137 | for _, value := range values { 138 | el := &listItem{value: value} 139 | if l.back == nil { 140 | l.back = el 141 | l.front = el 142 | } else { 143 | l.back.next = el 144 | el.prev = l.back 145 | l.back = el 146 | } 147 | } 148 | l.count += len(values) 149 | return l.count 150 | } 151 | 152 | func (l *list) set(idx int, value string) bool { 153 | var ok bool 154 | if idx, _, ok = l.ridx(idx, false); !ok { 155 | return false 156 | } 157 | if idx < l.count/2 { 158 | i := 0 159 | el := l.front 160 | for el != nil { 161 | if i == idx { 162 | el.value = value 163 | return true 164 | } 165 | el = el.next 166 | i++ 167 | } 168 | } else { 169 | i := l.count - 1 170 | el := l.back 171 | for el != nil { 172 | if i == idx { 173 | el.value = value 174 | return true 175 | } 176 | el = el.prev 177 | i-- 178 | } 179 | } 180 | return false 181 | } 182 | 183 | func (l *list) rem(count int, value string) int { 184 | if count < 0 { 185 | return 0 186 | } 187 | n := 0 188 | el := l.front 189 | for el != nil { 190 | if n == count { 191 | break 192 | } 193 | nel := el.next 194 | if el.value == value { 195 | if el.prev == nil { 196 | if el.next == nil { 197 | l.front, l.back = nil, nil 198 | } else { 199 | el.next.prev = nil 200 | l.front = el.next 201 | } 202 | } else if el.next == nil { 203 | el.prev.next = nil 204 | l.back = el.prev 205 | } else { 206 | el.prev.next = el.next 207 | el.next.prev = el.prev 208 | } 209 | l.count-- 210 | n++ 211 | } 212 | el = nel 213 | } 214 | return n 215 | } 216 | 217 | func (l *list) ascend(iterator func(value string) bool) { 218 | el := l.front 219 | for el != nil { 220 | if !iterator(el.value) { 221 | return 222 | } 223 | el = el.next 224 | } 225 | } 226 | 227 | func (l *list) lrange(start, stop int, count func(n int), iterator func(value string) bool) { 228 | var ok bool 229 | if start, _, ok = l.ridx(start, true); !ok { 230 | count(0) 231 | return 232 | } 233 | if stop, _, ok = l.ridx(stop, true); !ok { 234 | count(0) 235 | return 236 | } 237 | if start > stop { 238 | count(0) 239 | return 240 | } 241 | n := stop - start + 1 242 | count(n) 243 | var el *listItem 244 | if start < l.count/2 { 245 | i := 0 246 | el = l.front 247 | for el != nil { 248 | if i == start { 249 | break 250 | } 251 | el = el.next 252 | i++ 253 | } 254 | } else { 255 | i := l.count - 1 256 | el = l.back 257 | for el != nil { 258 | if i == start { 259 | break 260 | } 261 | el = el.prev 262 | i-- 263 | } 264 | } 265 | i := 0 266 | for el != nil { 267 | if i == n { 268 | break 269 | } 270 | if !iterator(el.value) { 271 | return 272 | } 273 | el = el.next 274 | i++ 275 | } 276 | } 277 | 278 | func (l *list) clear() { 279 | l.front = nil 280 | l.back = nil 281 | l.count = 0 282 | } 283 | 284 | func (l *list) findel(idx int) *listItem { 285 | if idx < l.count/2 { 286 | i := 0 287 | el := l.front 288 | for el != nil { 289 | if i == idx { 290 | return el 291 | } 292 | el = el.next 293 | i++ 294 | } 295 | } else { 296 | i := l.count - 1 297 | el := l.back 298 | for el != nil { 299 | if i == idx { 300 | return el 301 | } 302 | el = el.prev 303 | i-- 304 | } 305 | } 306 | return nil 307 | } 308 | 309 | func (l *list) strArr() []string { 310 | i := 0 311 | arr := make([]string, l.count) 312 | el := l.front 313 | for el != nil { 314 | arr[i] = el.value 315 | el = el.next 316 | i++ 317 | } 318 | return arr 319 | } 320 | 321 | func (l *list) numArr() []float64 { 322 | i := 0 323 | arr := make([]float64, l.count) 324 | el := l.front 325 | for el != nil { 326 | n, err := strconv.ParseFloat(el.value, 64) 327 | if err != nil { 328 | return nil 329 | } 330 | arr[i] = n 331 | el = el.next 332 | i++ 333 | } 334 | return arr 335 | } 336 | 337 | func (l *list) trim(start, stop int) { 338 | var ok bool 339 | var ostart, ostop int 340 | if start, ostart, ok = l.ridx(start, true); !ok { 341 | l.clear() 342 | return 343 | } 344 | if stop, ostop, ok = l.ridx(stop, true); !ok { 345 | l.clear() 346 | return 347 | } 348 | 349 | if ostart > ostop || 350 | ((ostart < 0 || ostart >= l.count) && (ostop < 0 || ostop >= l.count)) { 351 | l.clear() 352 | return 353 | } 354 | n := stop - start + 1 355 | if n == l.count { 356 | // nothing to trim 357 | return 358 | } 359 | 360 | // find the start element 361 | startEl := l.findel(start) 362 | stopEl := l.findel(stop) 363 | 364 | l.front = startEl 365 | l.front.prev = nil 366 | l.back = stopEl 367 | l.back.next = nil 368 | 369 | l.count = n 370 | } 371 | 372 | func (l *list) String() string { 373 | s := "" 374 | el := l.front 375 | for el != nil { 376 | s += fmt.Sprintf("%v ", el.value) 377 | el = el.next 378 | } 379 | return strings.TrimSpace(s) 380 | } 381 | 382 | /* commands */ 383 | func lpushCommand(c *client) { 384 | if len(c.args) < 3 { 385 | c.replyAritryError() 386 | return 387 | } 388 | l, ok := c.db.getList(c.args[1], true) 389 | if !ok { 390 | c.replyTypeError() 391 | return 392 | } 393 | l.lpush(c.args[2:]...) 394 | c.replyInt(l.len()) 395 | c.dirty++ 396 | } 397 | 398 | func rpushCommand(c *client) { 399 | if len(c.args) < 3 { 400 | c.replyAritryError() 401 | return 402 | } 403 | l, ok := c.db.getList(c.args[1], true) 404 | if !ok { 405 | c.replyTypeError() 406 | return 407 | } 408 | l.rpush(c.args[2:]...) 409 | c.replyInt(l.len()) 410 | c.dirty++ 411 | } 412 | 413 | func lrangeCommand(c *client) { 414 | if len(c.args) != 4 { 415 | c.replyAritryError() 416 | return 417 | } 418 | start, err := strconv.ParseInt(c.args[2], 10, 64) 419 | if err != nil { 420 | c.replyInvalidIntError() 421 | return 422 | } 423 | stop, err := strconv.ParseInt(c.args[3], 10, 64) 424 | if err != nil { 425 | c.replyInvalidIntError() 426 | return 427 | } 428 | 429 | l, ok := c.db.getList(c.args[1], false) 430 | if !ok { 431 | c.replyTypeError() 432 | return 433 | } 434 | if l == nil { 435 | c.replyMultiBulkLen(0) 436 | return 437 | } 438 | l.lrange(int(start), int(stop), func(n int) { 439 | c.replyMultiBulkLen(n) 440 | }, func(value string) bool { 441 | c.replyBulk(value) 442 | return true 443 | }) 444 | } 445 | 446 | func llenCommand(c *client) { 447 | if len(c.args) != 2 { 448 | c.replyAritryError() 449 | return 450 | } 451 | l, ok := c.db.getList(c.args[1], false) 452 | if !ok { 453 | c.replyTypeError() 454 | return 455 | } 456 | if l == nil { 457 | c.replyInt(0) 458 | return 459 | } 460 | c.replyInt(l.len()) 461 | } 462 | 463 | func lpopCommand(c *client) { 464 | if len(c.args) != 2 { 465 | c.replyAritryError() 466 | return 467 | } 468 | l, ok := c.db.getList(c.args[1], false) 469 | if !ok { 470 | c.replyTypeError() 471 | return 472 | } 473 | if l == nil { 474 | c.replyNull() 475 | return 476 | } 477 | value, ok := l.lpop() 478 | if !ok { 479 | c.replyNull() 480 | return 481 | } 482 | if l.len() == 0 { 483 | c.db.del(c.args[1]) 484 | } 485 | c.replyBulk(value) 486 | c.dirty++ 487 | 488 | } 489 | 490 | func rpopCommand(c *client) { 491 | if len(c.args) != 2 { 492 | c.replyAritryError() 493 | return 494 | } 495 | l, ok := c.db.getList(c.args[1], false) 496 | if !ok { 497 | c.replyTypeError() 498 | return 499 | } 500 | if l == nil { 501 | c.replyNull() 502 | return 503 | } 504 | value, ok := l.rpop() 505 | if !ok { 506 | c.replyNull() 507 | return 508 | } 509 | if l.len() == 0 { 510 | c.db.del(c.args[1]) 511 | } 512 | c.replyBulk(value) 513 | c.dirty++ 514 | } 515 | 516 | func lindexCommand(c *client) { 517 | if len(c.args) != 3 { 518 | c.replyAritryError() 519 | return 520 | } 521 | idx, err := strconv.ParseInt(c.args[2], 10, 64) 522 | if err != nil { 523 | c.replyInvalidIntError() 524 | return 525 | } 526 | l, ok := c.db.getList(c.args[1], false) 527 | if !ok { 528 | c.replyTypeError() 529 | return 530 | } 531 | if l == nil { 532 | c.replyNull() 533 | return 534 | } 535 | value, ok := l.lindex(int(idx)) 536 | if !ok { 537 | c.replyNull() 538 | return 539 | } 540 | c.replyBulk(value) 541 | } 542 | 543 | func lremCommand(c *client) { 544 | if len(c.args) != 4 { 545 | c.replyAritryError() 546 | return 547 | } 548 | count, err := strconv.ParseInt(c.args[2], 10, 64) 549 | if err != nil { 550 | c.replyInvalidIntError() 551 | return 552 | } 553 | l, ok := c.db.getList(c.args[1], false) 554 | if !ok { 555 | c.replyTypeError() 556 | return 557 | } 558 | if l == nil { 559 | c.replyInt(0) 560 | return 561 | } 562 | n := l.rem(int(count), c.args[3]) 563 | if l.len() == 0 { 564 | c.db.del(c.args[1]) 565 | } 566 | c.dirty += n 567 | c.replyInt(n) 568 | } 569 | 570 | func lsetCommand(c *client) { 571 | if len(c.args) != 4 { 572 | c.replyAritryError() 573 | return 574 | } 575 | idx, err := strconv.ParseInt(c.args[2], 10, 64) 576 | if err != nil { 577 | c.replyInvalidIntError() 578 | return 579 | } 580 | l, ok := c.db.getList(c.args[1], false) 581 | if !ok { 582 | c.replyTypeError() 583 | return 584 | } 585 | if l == nil { 586 | c.replyNoSuchKeyError() 587 | return 588 | } 589 | ok = l.set(int(idx), c.args[3]) 590 | if !ok { 591 | c.replyError("index out of range") 592 | return 593 | } 594 | c.replyString("OK") 595 | c.dirty++ 596 | } 597 | 598 | func ltrimCommand(c *client) { 599 | if len(c.args) != 4 { 600 | c.replyAritryError() 601 | return 602 | } 603 | start, err := strconv.ParseInt(c.args[2], 10, 64) 604 | if err != nil { 605 | c.replyInvalidIntError() 606 | return 607 | } 608 | stop, err := strconv.ParseInt(c.args[3], 10, 64) 609 | if err != nil { 610 | c.replyInvalidIntError() 611 | return 612 | } 613 | 614 | l, ok := c.db.getList(c.args[1], false) 615 | if !ok { 616 | c.replyTypeError() 617 | return 618 | } 619 | if l == nil { 620 | c.replyString("OK") 621 | return 622 | } 623 | 624 | llen := l.len() 625 | l.trim(int(start), int(stop)) 626 | if llen != l.len() { 627 | c.dirty++ 628 | } 629 | if l.len() == 0 { 630 | c.db.del(c.args[1]) 631 | } 632 | c.replyString("OK") 633 | } 634 | 635 | func rpoplpushCommand(c *client) { 636 | if len(c.args) != 3 { 637 | c.replyAritryError() 638 | return 639 | } 640 | l1, ok := c.db.getList(c.args[1], false) 641 | if !ok { 642 | c.replyTypeError() 643 | return 644 | } 645 | l2, ok := c.db.getList(c.args[2], false) 646 | if !ok { 647 | c.replyTypeError() 648 | return 649 | } 650 | if l1 == nil { 651 | c.replyNull() 652 | return 653 | } 654 | v, ok := l1.rpop() 655 | if !ok { 656 | c.replyNull() 657 | return 658 | } 659 | if l2 == nil { 660 | l2 = newList() 661 | c.db.set(c.args[2], l2) 662 | } 663 | l2.lpush(v) 664 | c.replyBulk(v) 665 | c.dirty++ 666 | } 667 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "net" 10 | "os" 11 | "path" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | func (s *Server) commandTable() { 19 | // "+" append aof 20 | // "w" write lock 21 | // "r" read lock 22 | s.register("get", getCommand, "r") // Strings 23 | s.register("getset", getsetCommand, "w+") // Strings 24 | s.register("set", setCommand, "w+") // Strings 25 | s.register("append", appendCommand, "w+") // Strings 26 | s.register("bitcount", bitcountCommand, "r") // Strings 27 | s.register("incr", incrCommand, "w+") // Strings 28 | s.register("incrby", incrbyCommand, "w+") // Strings 29 | s.register("decr", decrCommand, "w+") // Strings 30 | s.register("decrby", decrbyCommand, "w+") // Strings 31 | s.register("mget", mgetCommand, "r") // Strings 32 | s.register("setnx", setnxCommand, "w+") // Strings 33 | s.register("mset", msetCommand, "w+") // Strings 34 | s.register("msetnx", msetnxCommand, "w+") // Strings 35 | 36 | s.register("lpush", lpushCommand, "w+") // Lists 37 | s.register("rpush", rpushCommand, "w+") // Lists 38 | s.register("lrange", lrangeCommand, "r") // Lists 39 | s.register("llen", llenCommand, "r") // Lists 40 | s.register("lpop", lpopCommand, "w+") // Lists 41 | s.register("rpop", rpopCommand, "w+") // Lists 42 | s.register("lindex", lindexCommand, "r") // Lists 43 | s.register("lrem", lremCommand, "w+") // Lists 44 | s.register("lset", lsetCommand, "w+") // Lists 45 | s.register("ltrim", ltrimCommand, "w+") // Lists 46 | s.register("rpoplpush", rpoplpushCommand, "w+") // Lists 47 | 48 | s.register("sadd", saddCommand, "w+") // Sets 49 | s.register("scard", scardCommand, "r") // Sets 50 | s.register("smembers", smembersCommand, "r") // Sets 51 | s.register("sismember", sismembersCommand, "r") // Sets 52 | s.register("sdiff", sdiffCommand, "r") // Sets 53 | s.register("sinter", sinterCommand, "r") // Sets 54 | s.register("sunion", sunionCommand, "r") // Sets 55 | s.register("sdiffstore", sdiffstoreCommand, "w+") // Sets 56 | s.register("sinterstore", sinterstoreCommand, "w+") // Sets 57 | s.register("sunionstore", sunionstoreCommand, "w+") // Sets 58 | s.register("spop", spopCommand, "w+") // Sets 59 | s.register("srandmember", srandmemberCommand, "r") // Sets 60 | s.register("srem", sremCommand, "w+") // Sets 61 | s.register("smove", smoveCommand, "w+") // Sets 62 | 63 | s.register("echo", echoCommand, "") // Connection 64 | s.register("ping", pingCommand, "") // Connection 65 | s.register("select", selectCommand, "w") // Connection 66 | 67 | s.register("flushdb", flushdbCommand, "w+") // Server 68 | s.register("flushall", flushallCommand, "w+") // Server 69 | s.register("dbsize", dbsizeCommand, "r") // Server 70 | s.register("debug", debugCommand, "w") // Server 71 | s.register("bgrewriteaof", bgrewriteaofCommand, "w") // Server 72 | s.register("bgsave", bgsaveCommand, "w") // Server 73 | s.register("save", saveCommand, "w") // Server 74 | s.register("lastsave", lastsaveCommand, "r") // Server 75 | s.register("shutdown", shutdownCommand, "w") // Server 76 | s.register("info", infoCommand, "r") // Server 77 | s.register("monitor", monitorCommand, "w") // Server 78 | s.register("config", configCommand, "w") // Server 79 | s.register("auth", authCommand, "r") // Server 80 | 81 | s.register("del", delCommand, "w+") // Keys 82 | s.register("keys", keysCommand, "r") // Keys 83 | s.register("rename", renameCommand, "w+") // Keys 84 | s.register("renamenx", renamenxCommand, "w+") // Keys 85 | s.register("type", typeCommand, "r") // Keys 86 | s.register("randomkey", randomkeyCommand, "r") // Keys 87 | s.register("exists", existsCommand, "r") // Keys 88 | s.register("expire", expireCommand, "w+") // Keys 89 | s.register("ttl", ttlCommand, "r") // Keys 90 | s.register("move", moveCommand, "w+") // Keys 91 | s.register("sort", sortCommand, "w+") // Keys 92 | s.register("expireat", expireatCommand, "w+") // Keys 93 | } 94 | 95 | var errShutdownSave = errors.New("shutdown and save") 96 | var errShutdownNoSave = errors.New("shutdown and nosave") 97 | 98 | type command struct { 99 | name string 100 | aof bool 101 | read bool 102 | write bool 103 | funct func(c *client) 104 | } 105 | 106 | // Options alter the behavior of the server. 107 | type Options struct { 108 | LogWriter io.Writer 109 | IgnoreLogDebug bool 110 | IgnoreLogVerbose bool 111 | IgnoreLogNotice bool 112 | IgnoreLogWarning bool 113 | AppendOnlyPath string 114 | AppName, Version string 115 | Args []string 116 | } 117 | 118 | // Server represents a server object. 119 | type Server struct { 120 | mu sync.RWMutex 121 | l net.Listener 122 | options *Options // options that are passed from the caller 123 | cfg *config // server configuration 124 | cmds map[string]*command 125 | dbs map[int]*database 126 | started time.Time 127 | 128 | clients map[*client]bool // connected clients 129 | monitors map[*client]bool // clients monitoring 130 | 131 | follower bool 132 | mode string 133 | executable string 134 | 135 | expiresdone bool // flag for when the expires loop ends 136 | 137 | aof *os.File // the aof file handle 138 | aofdbnum int // the db num of the last "select" written to the aof 139 | aofclosed bool // flag for when the aof file is closed 140 | aofrewrite bool // flag for when the aof is in the process of being rewritten 141 | aofPath string // the full absolute path to the aof file 142 | 143 | ferr error // a fatal error. setting this should happen in the fatalError function 144 | ferrcond *sync.Cond // synchronize the watch 145 | ferrdone bool // flag for when the fatal error watch is complete 146 | 147 | } 148 | 149 | // register is called from the commandTable() function. The command map will contains 150 | // two entries assigned to the same command. One with an all uppercase key and one with 151 | // an all lower case key. 152 | func (s *Server) register(commandName string, f func(c *client), opts string) { 153 | var cmd command 154 | cmd.name = commandName 155 | cmd.funct = f 156 | for _, c := range []byte(opts) { 157 | switch c { 158 | case '+': 159 | cmd.aof = true 160 | case 'r': 161 | cmd.read = true 162 | case 'w': 163 | cmd.write = true 164 | } 165 | } 166 | s.cmds[strings.ToLower(commandName)] = &cmd 167 | s.cmds[strings.ToUpper(commandName)] = &cmd 168 | } 169 | 170 | // The log format is described at http://build47.com/redis-log-format-levels/ 171 | func log(w io.Writer, c byte, format string, args ...interface{}) { 172 | fmt.Fprintf( 173 | w, 174 | "%d:M %s %c %s\n", 175 | os.Getpid(), 176 | time.Now().Format("2 Jan 15:04:05.000"), 177 | c, 178 | fmt.Sprintf(format, args...), 179 | ) 180 | } 181 | 182 | func (s *Server) ldebugf(format string, args ...interface{}) { 183 | if !s.options.IgnoreLogDebug { 184 | log(s.options.LogWriter, '.', format, args...) 185 | } 186 | } 187 | func (s *Server) lverbosf(format string, args ...interface{}) { 188 | if !s.options.IgnoreLogVerbose { 189 | log(s.options.LogWriter, '-', format, args...) 190 | } 191 | } 192 | func (s *Server) lnoticef(format string, args ...interface{}) { 193 | if !s.options.IgnoreLogNotice { 194 | log(s.options.LogWriter, '*', format, args...) 195 | } 196 | } 197 | func (s *Server) lwarningf(format string, args ...interface{}) { 198 | if !s.options.IgnoreLogWarning { 199 | log(s.options.LogWriter, '#', format, args...) 200 | } 201 | } 202 | 203 | func (s *Server) fatalError(err error) { 204 | s.ferrcond.L.Lock() 205 | if s.ferr == nil { 206 | s.ferr = err 207 | } 208 | s.ferrcond.Broadcast() 209 | s.ferrcond.L.Unlock() 210 | } 211 | 212 | func (s *Server) getFatalError() error { 213 | s.ferrcond.L.Lock() 214 | defer s.ferrcond.L.Unlock() 215 | return s.ferr 216 | } 217 | 218 | // startExpireLoop runs a background routine which watches for exipred keys 219 | // and forces their removal from the database. One second resolution. 220 | func (s *Server) startExpireLoop() { 221 | go func() { 222 | t := time.NewTicker(time.Second) 223 | defer t.Stop() 224 | for range t.C { 225 | s.mu.Lock() 226 | if s.expiresdone { 227 | s.mu.Unlock() 228 | return 229 | } 230 | s.forceDeleteExpires() 231 | s.mu.Unlock() 232 | } 233 | }() 234 | } 235 | 236 | func (s *Server) forceDeleteExpires() { 237 | if s.follower { 238 | return 239 | } 240 | deleted := false 241 | for _, db := range s.dbs { 242 | if db.deleteExpires() { 243 | deleted = true 244 | } 245 | } 246 | if deleted { 247 | if err := s.flushAOF(); err != nil { 248 | s.fatalError(err) 249 | return 250 | } 251 | } 252 | } 253 | 254 | // stopExpireLoop will force delete all expires and stop the background routine 255 | func (s *Server) stopExpireLoop() { 256 | s.mu.Lock() 257 | s.forceDeleteExpires() 258 | s.expiresdone = true 259 | s.mu.Unlock() 260 | } 261 | 262 | // startFatalErrorWatch 263 | func (s *Server) startFatalErrorWatch() { 264 | go func() { 265 | for { 266 | s.ferrcond.L.Lock() 267 | if s.ferrdone { 268 | s.ferrcond.L.Unlock() 269 | return 270 | } 271 | if s.ferr != nil { 272 | s.l.Close() 273 | s.ferrdone = true 274 | s.ferrcond.L.Unlock() 275 | return 276 | } 277 | s.ferrcond.Wait() 278 | s.ferrcond.L.Unlock() 279 | } 280 | }() 281 | } 282 | 283 | func (s *Server) stopFatalErrorWatch() { 284 | s.ferrcond.L.Lock() 285 | s.ferrdone = true 286 | s.ferrcond.Broadcast() 287 | s.ferrcond.L.Unlock() 288 | } 289 | 290 | func (s *Server) selectDB(num int) *database { 291 | db, ok := s.dbs[num] 292 | if !ok { 293 | db = newDB(num) 294 | s.dbs[num] = db 295 | } 296 | return db 297 | } 298 | 299 | func Start(options *Options) (err error) { 300 | s := &Server{ 301 | cmds: make(map[string]*command), 302 | dbs: make(map[int]*database), 303 | clients: make(map[*client]bool), 304 | monitors: make(map[*client]bool), 305 | aofdbnum: -1, 306 | ferrcond: sync.NewCond(&sync.Mutex{}), 307 | started: time.Now(), 308 | mode: "standalone", 309 | follower: false, 310 | } 311 | var ready bool 312 | defer func() { 313 | if err == nil && s.ferr != nil { 314 | err = s.ferr 315 | } 316 | switch err { 317 | case errShutdownSave, errShutdownNoSave: 318 | err = nil 319 | } 320 | if ready { 321 | s.lwarningf("%s is now ready to exit, bye bye...", s.options.AppName) 322 | } 323 | }() 324 | options, configMap, configFile, ok := fillOptions(options) 325 | s.options = options // this should be set even if there's an error. 326 | if !ok { 327 | err = errors.New("options failure") 328 | return 329 | } 330 | s.cfg, err = fillConfig(configMap, configFile) 331 | if err != nil { 332 | err = errors.New("config failure") 333 | //s.lwarningf("%v", err) 334 | return 335 | } 336 | s.lwarningf("Server started, %s version %s", s.options.AppName, s.options.Version) 337 | s.commandTable() 338 | ready = true 339 | 340 | var wd string 341 | wd, err = os.Getwd() 342 | if err != nil { 343 | s.lwarningf("%v", err) 344 | return err 345 | } 346 | s.executable = path.Join(wd, os.Args[0]) 347 | s.aofPath = s.options.AppendOnlyPath 348 | if !path.IsAbs(s.aofPath) { 349 | s.aofPath = path.Join(wd, s.aofPath) 350 | } 351 | if err = s.openAOF(); err != nil { 352 | s.lwarningf("%v", err) 353 | return err 354 | } 355 | defer func() { 356 | switch s.getFatalError() { 357 | case errShutdownSave: 358 | s.lnoticef("DB saved on disk") 359 | } 360 | }() 361 | defer s.closeAOF() 362 | defer s.flushAOF() 363 | s.startExpireLoop() 364 | defer s.stopExpireLoop() 365 | addr := s.cfg.kvm["bind"] + ":" + s.cfg.kvm["port"] 366 | s.l, err = net.Listen("tcp", addr) 367 | if err != nil { 368 | s.lwarningf("%v", err) 369 | return err 370 | } 371 | defer s.l.Close() 372 | 373 | s.lnoticef("The server is now ready to accept connections on port %s", s.l.Addr().String()[strings.LastIndex(s.l.Addr().String(), ":")+1:]) 374 | 375 | // Start watching for fatal errors. 376 | s.startFatalErrorWatch() 377 | defer s.stopFatalErrorWatch() 378 | 379 | conns := make(map[net.Conn]bool) 380 | defer func() { 381 | for conn := range conns { 382 | conn.Close() 383 | delete(conns, conn) 384 | } 385 | }() 386 | defer func() { 387 | switch s.getFatalError() { 388 | case errShutdownSave, errShutdownNoSave: 389 | s.lwarningf("User requested shutdown...") 390 | } 391 | }() 392 | 393 | for { 394 | conn, err := s.l.Accept() 395 | if err != nil { 396 | ferr := s.getFatalError() 397 | if ferr != errShutdownSave && ferr != errShutdownNoSave { 398 | return err 399 | } else { 400 | return nil 401 | } 402 | } 403 | conns[conn] = true 404 | go handleConn(conn, s) 405 | 406 | } 407 | } 408 | 409 | func (s *Server) broadcastMonitors(dbnum int, addr string, args []string) { 410 | s.mu.Lock() 411 | t := float64(time.Now().UnixNano()) / float64(time.Second) 412 | s.mu.Unlock() 413 | w := &bytes.Buffer{} 414 | fmt.Fprintf(w, "+%.6f [%d %s]", t, dbnum, addr) 415 | for _, arg := range args { 416 | w.WriteByte(' ') 417 | w.WriteByte('"') 418 | for i := 0; i < len(arg); i++ { 419 | ch := arg[i] 420 | switch { 421 | default: 422 | w.WriteByte('\\') 423 | w.WriteByte('x') 424 | hex := strconv.FormatUint(uint64(ch), 16) 425 | if len(hex) == 1 { 426 | w.WriteByte('0') 427 | } 428 | w.WriteString(hex) 429 | case ch > 31 && ch < 127: 430 | w.WriteByte(ch) 431 | } 432 | } 433 | w.WriteByte('"') 434 | } 435 | w.WriteByte('\r') 436 | w.WriteByte('\n') 437 | s.mu.Lock() 438 | for c := range s.monitors { 439 | if wr, ok := c.wr.(*bufio.Writer); ok { 440 | wr.WriteString(w.String()) 441 | wr.Flush() 442 | } 443 | } 444 | s.mu.Unlock() 445 | } 446 | 447 | // autocase will return an ascii string in uppercase or lowercase, but never 448 | // mixed case. It's quicker than calling strings.(ToLower/ToUpper). 449 | // The thinking is that commands are usually sent in all upper or 450 | // all lower case, such as 'GET' or 'get'. But, rarely 'Get'. 451 | func autocase(command string) string { 452 | for i := 0; i < len(command); i++ { 453 | c := command[i] 454 | if c >= 'A' && c <= 'Z' { 455 | i++ 456 | for ; i < len(command); i++ { 457 | c = command[i] 458 | if c >= 'a' && c <= 'z' { 459 | return strings.ToUpper(command) 460 | } 461 | } 462 | break 463 | } else if c >= 'a' && c <= 'z' { 464 | i++ 465 | for ; i < len(command); i++ { 466 | c = command[i] 467 | if c >= 'A' && c <= 'Z' { 468 | return strings.ToLower(command) 469 | } 470 | } 471 | break 472 | } 473 | } 474 | return command 475 | } 476 | 477 | func (s *Server) protected() bool { 478 | if !s.cfg.protectedMode { 479 | return false 480 | } 481 | if !s.cfg.bindIsLocal { 482 | return false 483 | } 484 | return s.cfg.protectedMode && s.cfg.requirepass == "" 485 | } 486 | 487 | func handleConn(conn net.Conn, s *Server) { 488 | defer conn.Close() 489 | rd := newCommandReader(conn) 490 | wr := bufio.NewWriter(conn) 491 | defer wr.Flush() 492 | c := &client{wr: wr, s: s} 493 | c.addr = conn.RemoteAddr().String() 494 | defer c.flushAOF() 495 | s.mu.Lock() 496 | s.clients[c] = true 497 | c.db = s.selectDB(0) 498 | s.mu.Unlock() 499 | defer func() { 500 | s.mu.Lock() 501 | delete(s.clients, c) 502 | delete(s.monitors, c) 503 | s.mu.Unlock() 504 | }() 505 | var flush bool 506 | var err error 507 | for { 508 | dbnum := c.db.num 509 | c.errd = false 510 | c.raw, c.args, flush, err = rd.readCommand() 511 | if err != nil { 512 | if err, ok := err.(*protocolError); ok { 513 | c.replyError(err.Error()) 514 | } 515 | return 516 | } 517 | if len(c.args) == 0 { 518 | continue 519 | } 520 | commandName := autocase(c.args[0]) 521 | if cmd, ok := s.cmds[commandName]; ok { 522 | if c.authenticate(cmd) { 523 | if cmd.write { 524 | s.mu.Lock() 525 | } else if cmd.read { 526 | s.mu.RLock() 527 | } 528 | cmd.funct(c) 529 | if c.dirty > 0 && cmd.aof { 530 | c.db.aofbuf.Write(c.raw) 531 | } 532 | 533 | if cmd.write { 534 | s.mu.Unlock() 535 | } else if cmd.read { 536 | s.mu.RUnlock() 537 | } 538 | if !c.errd && cmd.name != "monitor" { 539 | s.broadcastMonitors(dbnum, c.addr, c.args) 540 | } 541 | } 542 | } else { 543 | switch commandName { 544 | default: 545 | c.replyError("unknown command '" + c.args[0] + "'") 546 | case "quit": 547 | c.replyString("OK") 548 | return 549 | } 550 | } 551 | if flush { 552 | if err := c.flushAOF(); err != nil { 553 | return 554 | } 555 | if err := wr.Flush(); err != nil { 556 | return 557 | } 558 | } 559 | } 560 | } 561 | 562 | /* Commands */ 563 | func flushdbCommand(c *client) { 564 | if len(c.args) != 1 { 565 | c.replyAritryError() 566 | return 567 | } 568 | c.db.flush() 569 | c.replyString("OK") 570 | c.dirty++ 571 | } 572 | 573 | func flushallCommand(c *client) { 574 | if len(c.args) != 1 { 575 | c.replyAritryError() 576 | return 577 | } 578 | for _, db := range c.s.dbs { 579 | db.flush() 580 | } 581 | c.replyString("OK") 582 | c.dirty++ 583 | } 584 | 585 | func dbsizeCommand(c *client) { 586 | if len(c.args) != 1 { 587 | c.replyAritryError() 588 | return 589 | } 590 | c.replyInt(c.db.len()) 591 | } 592 | 593 | func bgrewriteaofCommand(c *client) { 594 | if len(c.args) != 1 { 595 | c.replyAritryError() 596 | return 597 | } 598 | if ok := c.s.rewriteAOF(); !ok { 599 | c.replyError("Background append only file rewriting already in progress") 600 | return 601 | } 602 | c.replyString("Background append only file rewriting started") 603 | } 604 | 605 | func bgsaveCommand(c *client) { 606 | if len(c.args) != 1 { 607 | c.replyAritryError() 608 | return 609 | } 610 | if ok := c.s.rewriteAOF(); !ok { 611 | c.replyError("Background save already in progress") 612 | return 613 | } 614 | c.replyString("Background saving started") 615 | } 616 | 617 | func lastsaveCommand(c *client) { 618 | if len(c.args) != 1 { 619 | c.replyAritryError() 620 | return 621 | } 622 | fi, err := c.s.aof.Stat() 623 | if err != nil { 624 | c.replyError("Could not get the UNIX timestamp") 625 | return 626 | } 627 | c.replyInt(int(fi.ModTime().Unix())) 628 | } 629 | 630 | func saveCommand(c *client) { 631 | if len(c.args) != 1 { 632 | c.replyAritryError() 633 | return 634 | } 635 | if !c.s.rewriteAOF() { 636 | c.replyError("Background save already in progress") 637 | return 638 | } 639 | c.s.mu.Unlock() 640 | t := time.NewTicker(time.Millisecond * 50) 641 | defer t.Stop() 642 | for range t.C { 643 | c.s.mu.Lock() 644 | res := c.s.aofrewrite 645 | c.s.mu.Unlock() 646 | if !res { 647 | break 648 | } 649 | } 650 | c.s.mu.Lock() 651 | c.replyString("OK") 652 | } 653 | 654 | func shutdownCommand(c *client) { 655 | if len(c.args) != 1 && len(c.args) != 2 { 656 | c.replyAritryError() 657 | return 658 | } 659 | save := true 660 | if len(c.args) == 2 { 661 | switch strings.ToLower(c.args[1]) { 662 | default: 663 | c.replySyntaxError() 664 | return 665 | case "save": 666 | save = true 667 | case "nosave": 668 | save = false 669 | } 670 | } 671 | 672 | if save { 673 | c.s.fatalError(errShutdownSave) 674 | } else { 675 | c.s.fatalError(errShutdownNoSave) 676 | } 677 | } 678 | 679 | func monitorCommand(c *client) { 680 | if len(c.args) != 1 { 681 | c.replyAritryError() 682 | return 683 | } 684 | if c.monitor { 685 | return 686 | } 687 | c.monitor = true 688 | c.s.monitors[c] = true 689 | c.replyString("OK") 690 | } 691 | 692 | func configCommand(c *client) { 693 | if len(c.args) < 2 { 694 | c.replyAritryError() 695 | return 696 | } 697 | switch strings.ToLower(c.args[1]) { 698 | default: 699 | c.replyError("CONFIG subcommand must be one of GET, SET, RESETSTAT, REWRITE") 700 | case "get": 701 | configGetCommand(c) 702 | case "set": 703 | configSetCommand(c) 704 | case "resetstat": 705 | configResetStatCommand(c) 706 | case "rewrite": 707 | configRewriteCommand(c) 708 | } 709 | } 710 | func configGetCommand(c *client) { 711 | if len(c.args) != 3 { 712 | c.replyError("Wrong number of arguments for CONFIG " + c.args[1]) 713 | return 714 | } 715 | switch c.args[2] { 716 | default: 717 | c.replyMultiBulkLen(0) 718 | return 719 | case "port", "bind", "protected-mode", "requirepass": 720 | } 721 | c.replyMultiBulkLen(2) 722 | c.replyBulk(c.args[2]) 723 | c.replyBulk(c.s.cfg.kvm[c.args[2]]) 724 | 725 | } 726 | func configSetCommand(c *client) { 727 | if len(c.args) != 4 { 728 | c.replyError("Wrong number of arguments for CONFIG " + c.args[1]) 729 | return 730 | } 731 | switch strings.ToLower(c.args[2]) { 732 | default: 733 | c.replyError("Unsupported CONFIG parameter: " + c.args[2]) 734 | return 735 | case "requirepass": 736 | c.s.cfg.kvm["requirepass"] = c.args[3] 737 | c.s.cfg.requirepass = c.args[3] 738 | case "protected-mode": 739 | switch strings.ToLower(c.args[3]) { 740 | default: 741 | c.replyError("Invalid argument '" + c.args[3] + "' for CONFIG SET '" + c.args[2] + "'") 742 | return 743 | case "yes": 744 | c.s.cfg.kvm["protected-mode"] = "yes" 745 | c.s.cfg.protectedMode = true 746 | case "no": 747 | c.s.cfg.kvm["protected-mode"] = "no" 748 | c.s.cfg.protectedMode = false 749 | } 750 | } 751 | c.replyString("OK") 752 | } 753 | func configResetStatCommand(c *client) { 754 | c.replyString("OK") 755 | } 756 | func configRewriteCommand(c *client) { 757 | if len(c.args) != 2 { 758 | c.replyError("Wrong number of arguments for CONFIG " + c.args[1]) 759 | return 760 | } 761 | if c.s.cfg.file == "" { 762 | c.replyError("The server is running without a config file") 763 | return 764 | } 765 | if err := mergeConfigFile(c.s.cfg.file, c.s.cfg.kvm); err != nil { 766 | c.replyError(fmt.Sprintf("Rewriting config: %v", err)) 767 | return 768 | } 769 | c.replyString("OK") 770 | } 771 | 772 | func authCommand(c *client) { 773 | if len(c.args) != 2 { 774 | c.replyAritryError() 775 | return 776 | } 777 | if c.s.cfg.requirepass != c.args[1] { 778 | c.replyError("invalid password") 779 | return 780 | } 781 | c.authd = 2 782 | c.replyString("OK") 783 | } 784 | --------------------------------------------------------------------------------