2014-08-25 10:18:23 +04:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"github.com/siddontang/ledisdb/ledis"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2014-09-25 06:44:07 +04:00
|
|
|
var txUnsupportedCmds = map[string]struct{}{
|
|
|
|
"select": struct{}{},
|
|
|
|
"slaveof": struct{}{},
|
|
|
|
"fullsync": struct{}{},
|
|
|
|
"sync": struct{}{},
|
|
|
|
"begin": struct{}{},
|
|
|
|
"flushall": struct{}{},
|
|
|
|
"flushdb": struct{}{},
|
|
|
|
"eval": struct{}{},
|
|
|
|
}
|
|
|
|
|
2014-09-02 13:55:12 +04:00
|
|
|
var scriptUnsupportedCmds = map[string]struct{}{
|
|
|
|
"slaveof": struct{}{},
|
|
|
|
"fullsync": struct{}{},
|
|
|
|
"sync": struct{}{},
|
2014-09-25 06:44:07 +04:00
|
|
|
"begin": struct{}{},
|
|
|
|
"commit": struct{}{},
|
|
|
|
"rollback": struct{}{},
|
2014-09-02 13:55:12 +04:00
|
|
|
"flushall": struct{}{},
|
|
|
|
"flushdb": struct{}{},
|
2014-08-25 10:18:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
type responseWriter interface {
|
|
|
|
writeError(error)
|
|
|
|
writeStatus(string)
|
|
|
|
writeInteger(int64)
|
|
|
|
writeBulk([]byte)
|
|
|
|
writeArray([]interface{})
|
|
|
|
writeSliceArray([][]byte)
|
|
|
|
writeFVPairArray([]ledis.FVPair)
|
|
|
|
writeScorePairArray([]ledis.ScorePair, bool)
|
|
|
|
writeBulkFrom(int64, io.Reader)
|
|
|
|
flush()
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:28:09 +04:00
|
|
|
type syncAck struct {
|
|
|
|
id uint64
|
|
|
|
ch chan uint64
|
|
|
|
}
|
|
|
|
|
2014-08-25 10:18:23 +04:00
|
|
|
type client struct {
|
|
|
|
app *App
|
|
|
|
ldb *ledis.Ledis
|
2014-09-02 13:55:12 +04:00
|
|
|
|
|
|
|
db *ledis.DB
|
2014-08-25 10:18:23 +04:00
|
|
|
|
|
|
|
remoteAddr string
|
|
|
|
cmd string
|
|
|
|
args [][]byte
|
|
|
|
|
|
|
|
resp responseWriter
|
|
|
|
|
|
|
|
syncBuf bytes.Buffer
|
|
|
|
compressBuf []byte
|
|
|
|
|
2014-09-23 13:53:52 +04:00
|
|
|
lastLogID uint64
|
2014-09-23 13:28:09 +04:00
|
|
|
|
|
|
|
ack *syncAck
|
|
|
|
|
2014-08-25 10:18:23 +04:00
|
|
|
reqErr chan error
|
|
|
|
|
|
|
|
buf bytes.Buffer
|
|
|
|
|
2014-09-25 06:44:07 +04:00
|
|
|
tx *ledis.Tx
|
|
|
|
|
2014-09-02 13:55:12 +04:00
|
|
|
script *ledis.Multi
|
2014-08-25 10:18:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func newClient(app *App) *client {
|
|
|
|
c := new(client)
|
|
|
|
|
|
|
|
c.app = app
|
|
|
|
c.ldb = app.ldb
|
|
|
|
c.db, _ = app.ldb.Select(0) //use default db
|
|
|
|
|
2014-09-02 13:55:12 +04:00
|
|
|
c.compressBuf = []byte{}
|
2014-08-25 10:18:23 +04:00
|
|
|
c.reqErr = make(chan error)
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *client) perform() {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
|
|
|
if len(c.cmd) == 0 {
|
|
|
|
err = ErrEmptyCommand
|
|
|
|
} else if exeCmd, ok := regCmds[c.cmd]; !ok {
|
|
|
|
err = ErrNotFound
|
|
|
|
} else {
|
2014-09-25 06:44:07 +04:00
|
|
|
if c.db.IsTransaction() {
|
|
|
|
if _, ok := txUnsupportedCmds[c.cmd]; ok {
|
|
|
|
err = fmt.Errorf("%s not supported in transaction", c.cmd)
|
|
|
|
}
|
|
|
|
} else if c.db.IsInMulti() {
|
2014-09-02 13:55:12 +04:00
|
|
|
if _, ok := scriptUnsupportedCmds[c.cmd]; ok {
|
|
|
|
err = fmt.Errorf("%s not supported in multi", c.cmd)
|
|
|
|
}
|
2014-08-25 10:18:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
go func() {
|
|
|
|
c.reqErr <- exeCmd(c)
|
|
|
|
}()
|
|
|
|
|
|
|
|
err = <-c.reqErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
duration := time.Since(start)
|
|
|
|
|
|
|
|
if c.app.access != nil {
|
|
|
|
fullCmd := c.catGenericCommand()
|
|
|
|
cost := duration.Nanoseconds() / 1000000
|
|
|
|
|
|
|
|
truncateLen := len(fullCmd)
|
|
|
|
if truncateLen > 256 {
|
|
|
|
truncateLen = 256
|
|
|
|
}
|
|
|
|
|
|
|
|
c.app.access.Log(c.remoteAddr, cost, fullCmd[:truncateLen], err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
c.resp.writeError(err)
|
|
|
|
}
|
|
|
|
c.resp.flush()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *client) catGenericCommand() []byte {
|
|
|
|
buffer := c.buf
|
|
|
|
buffer.Reset()
|
|
|
|
|
|
|
|
buffer.Write([]byte(c.cmd))
|
|
|
|
|
|
|
|
for _, arg := range c.args {
|
|
|
|
buffer.WriteByte(' ')
|
|
|
|
buffer.Write(arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer.Bytes()
|
|
|
|
}
|
2014-09-02 13:55:12 +04:00
|
|
|
|
|
|
|
func writeValue(w responseWriter, value interface{}) {
|
|
|
|
switch v := value.(type) {
|
|
|
|
case []interface{}:
|
|
|
|
w.writeArray(v)
|
|
|
|
case [][]byte:
|
|
|
|
w.writeSliceArray(v)
|
|
|
|
case []byte:
|
|
|
|
w.writeBulk(v)
|
|
|
|
case string:
|
|
|
|
w.writeStatus(v)
|
|
|
|
case nil:
|
|
|
|
w.writeBulk(nil)
|
|
|
|
case int64:
|
|
|
|
w.writeInteger(v)
|
|
|
|
default:
|
|
|
|
panic("invalid value type")
|
|
|
|
}
|
|
|
|
}
|