diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65a501e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +ssdb/testdb \ No newline at end of file diff --git a/cmd/ssdb/main.go b/cmd/ssdb/main.go new file mode 100644 index 0000000..64b50c2 --- /dev/null +++ b/cmd/ssdb/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "flag" + "github.com/siddontang/go-ssdb/ssdb" + "os" + "os/signal" + "syscall" +) + +var configFile = flag.String("config", "", "ssdb config file") + +func main() { + flag.Parse() + + if len(*configFile) == 0 { + panic("must use a config file") + } + + cfg, err := ssdb.NewConfigWithFile(*configFile) + if err != nil { + panic(err) + } + + var app *ssdb.App + app, err = ssdb.NewApp(cfg) + if err != nil { + panic(err) + } + + sc := make(chan os.Signal, 1) + signal.Notify(sc, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT) + + go func() { + <-sc + + app.Close() + }() + + app.Run() +} diff --git a/ssdb/app.go b/ssdb/app.go index e478e64..d3286aa 100644 --- a/ssdb/app.go +++ b/ssdb/app.go @@ -42,8 +42,6 @@ func NewApp(cfg *Config) (*App, error) { return nil, err } - app.dbMutex = newKeyMutex(128) - return app, nil } @@ -63,7 +61,3 @@ func (app *App) Run() { newClient(conn, app) } } - -func (app *App) getMutex(key []byte) *sync.Mutex { - return app.dbMutex.Get(key) -} diff --git a/ssdb/app_test.go b/ssdb/app_test.go index 28f2e2a..d633f18 100644 --- a/ssdb/app_test.go +++ b/ssdb/app_test.go @@ -1,9 +1,72 @@ package ssdb import ( + "github.com/garyburd/redigo/redis" + "os" + "sync" "testing" ) -func TestApp(t *testing.T) { +var testAppOnce sync.Once +var testApp *App +var testPool *redis.Pool + +func newTestRedisPool() { + f := func() (redis.Conn, error) { + c, err := redis.Dial("tcp", "127.0.0.1:6380") + if err != nil { + return nil, err + } + + return c, nil + } + + testPool = redis.NewPool(f, 4) +} + +func getTestConn() redis.Conn { + return testPool.Get() +} + +func startTestApp() { + f := func() { + newTestRedisPool() + + os.RemoveAll("./testdb") + + var d = []byte(` + { + "addr" : "127.0.0.1:6380", + "leveldb" : + { + "path" : "./testdb", + "compression":true, + "block_size" : 32768, + "write_buffer_size" : 2097152, + "cache_size" : 20971520 + } + } + `) + + cfg, err := NewConfig(d) + if err != nil { + println(err.Error()) + panic(err) + } + + testApp, err = NewApp(cfg) + if err != nil { + println(err.Error()) + panic(err) + } + + go testApp.Run() + } + + testAppOnce.Do(f) +} + +func TestApp(t *testing.T) { + startTestApp() } diff --git a/ssdb/client.go b/ssdb/client.go index 36e549c..2c8ad96 100644 --- a/ssdb/client.go +++ b/ssdb/client.go @@ -12,7 +12,7 @@ import ( "strings" ) -var errReadRequest = errors.New("read request error, invalid format") +var errReadRequest = errors.New("invalid request protocol") type client struct { app *App @@ -88,15 +88,16 @@ func (c *client) readRequest() ([][]byte, error) { return nil, errReadRequest } - var n int - if n, err = strconv.Atoi(hack.String(l[1:])); err != nil { + var nparams int + if nparams, err = strconv.Atoi(hack.String(l[1:])); err != nil { return nil, err - } else if n <= 0 { + } else if nparams <= 0 { return nil, errReadRequest } - req := make([][]byte, 0, n) - for i := 0; i < n; i++ { + req := make([][]byte, 0, nparams) + var n int + for i := 0; i < nparams; i++ { if l, err = c.readLine(); err != nil { return nil, err } @@ -115,12 +116,15 @@ func (c *client) readRequest() ([][]byte, error) { return nil, err } else if buf[len(buf)-2] != '\r' || buf[len(buf)-1] != '\n' { return nil, errReadRequest + } else { req = append(req, buf[0:len(buf)-2]) } } } else { + println("return 6") + return nil, errReadRequest } } diff --git a/ssdb/cmd_kv_test.go b/ssdb/cmd_kv_test.go new file mode 100644 index 0000000..49fc92f --- /dev/null +++ b/ssdb/cmd_kv_test.go @@ -0,0 +1,145 @@ +package ssdb + +import ( + "github.com/garyburd/redigo/redis" + "testing" +) + +func TestKV(t *testing.T) { + startTestApp() + + c := getTestConn() + defer c.Close() + + if ok, err := redis.String(c.Do("set", "a", "1234")); err != nil { + t.Fatal(err) + } else if ok != OK { + t.Fatal(ok) + } + + if n, err := redis.Int(c.Do("setnx", "a", "123")); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + + if n, err := redis.Int(c.Do("setnx", "b", "123")); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if v, err := redis.String(c.Do("get", "a")); err != nil { + t.Fatal(err) + } else if v != "1234" { + t.Fatal(v) + } + + if v, err := redis.String(c.Do("getset", "a", "123")); err != nil { + t.Fatal(err) + } else if v != "1234" { + t.Fatal(v) + } + + if v, err := redis.String(c.Do("get", "a")); err != nil { + t.Fatal(err) + } else if v != "123" { + t.Fatal(v) + } + + if n, err := redis.Int(c.Do("exists", "a")); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if n, err := redis.Int(c.Do("exists", "empty_key_test")); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + + if _, err := redis.Int(c.Do("del", "a", "b")); err != nil { + t.Fatal(err) + } + + if n, err := redis.Int(c.Do("exists", "a")); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } + + if n, err := redis.Int(c.Do("exists", "b")); err != nil { + t.Fatal(err) + } else if n != 0 { + t.Fatal(n) + } +} + +func TestKVM(t *testing.T) { + startTestApp() + + c := getTestConn() + defer c.Close() + + if ok, err := redis.String(c.Do("mset", "a", "1", "b", "2")); err != nil { + t.Fatal(err) + } else if ok != OK { + t.Fatal(ok) + } + + if v, err := redis.MultiBulk(c.Do("mget", "a", "b", "c")); err != nil { + t.Fatal(err) + } else if len(v) != 3 { + t.Fatal(len(v)) + } else { + if vv, ok := v[0].([]byte); !ok || string(vv) != "1" { + t.Fatal("not 1") + } + + if vv, ok := v[1].([]byte); !ok || string(vv) != "2" { + t.Fatal("not 2") + } + + if v[2] != nil { + t.Fatal("must nil") + } + } +} + +func TestKVIncrDecr(t *testing.T) { + startTestApp() + + c := getTestConn() + defer c.Close() + + if n, err := redis.Int64(c.Do("incr", "n")); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if n, err := redis.Int64(c.Do("incr", "n")); err != nil { + t.Fatal(err) + } else if n != 2 { + t.Fatal(n) + } + + if n, err := redis.Int64(c.Do("decr", "n")); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } + + if n, err := redis.Int64(c.Do("incrby", "n", 10)); err != nil { + t.Fatal(err) + } else if n != 11 { + t.Fatal(n) + } + + if n, err := redis.Int64(c.Do("decrby", "n", 10)); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } +} diff --git a/ssdb/t_kv.go b/ssdb/t_kv.go index 96240f7..b7bb4d4 100644 --- a/ssdb/t_kv.go +++ b/ssdb/t_kv.go @@ -10,7 +10,7 @@ var errEmptyKVKey = errors.New("invalid empty kv key") var errKVKey = errors.New("invalid encode kv key") func encode_kv_key(key []byte) []byte { - ek := make([]byte, key+1) + ek := make([]byte, len(key)+1) ek[0] = KV_TYPE copy(ek[1:], key) return ek @@ -97,7 +97,7 @@ func (a *App) kv_exists(key []byte) (int64, error) { var v []byte v, err = a.db.Get(key) - if v != nil && err != nil { + if v != nil && err == nil { return 1, nil } else { return 0, err diff --git a/ssdb/tx.go b/ssdb/tx.go index 658b034..2f34525 100644 --- a/ssdb/tx.go +++ b/ssdb/tx.go @@ -33,7 +33,6 @@ func (t *tx) Commit() error { return err } -func (t *tx) Rollback() error { - err := t.wb.Rollback() - return err +func (t *tx) Rollback() { + t.wb.Rollback() }