Allow QUEUED reply only if pipelined; don't return status replies from flushPipeline; Rewrite someTests

This commit is contained in:
Jason Livesay 2014-04-17 03:47:44 -07:00
commit 1068022dfb

View file

@ -58,8 +58,10 @@ proc raiseInvalidReply(expected, got: char) =
"Expected '$1' at the beginning of a status reply got '$2'" % "Expected '$1' at the beginning of a status reply got '$2'" %
[$expected, $got]) [$expected, $got])
proc raiseNoOK(status: string) = proc raiseNoOK(status: string, pipelineEnabled:bool) =
if status != "QUEUED" and status != "OK": if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
elif not pipelineEnabled and status != "OK":
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status) raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
template readSocket(r: TRedis, dummyVal:expr): stmt = template readSocket(r: TRedis, dummyVal:expr): stmt =
@ -71,7 +73,7 @@ template readSocket(r: TRedis, dummyVal:expr): stmt =
proc parseStatus(r: TRedis, line: string = ""): TRedisStatus = proc parseStatus(r: TRedis, line: string = ""): TRedisStatus =
if r.pipeline.enabled: if r.pipeline.enabled:
return "OK" return "PIPELINED"
if line == "": if line == "":
raise newException(ERedis, "Server closed connection prematurely") raise newException(ERedis, "Server closed connection prematurely")
@ -84,14 +86,14 @@ proc parseStatus(r: TRedis, line: string = ""): TRedisStatus =
return line.substr(1) # Strip '+' return line.substr(1) # Strip '+'
proc readStatus(r:TRedis): TRedisStatus = proc readStatus(r:TRedis): TRedisStatus =
r.readSocket("OK") r.readSocket("PIPELINED")
return r.parseStatus(line) return r.parseStatus(line)
proc parseInteger(r: TRedis, line: string = ""): TRedisInteger = proc parseInteger(r: TRedis, line: string = ""): TRedisInteger =
if r.pipeline.enabled: return -1 if r.pipeline.enabled: return -1
if line == "+QUEUED": # inside of multi #if line == "+QUEUED": # inside of multi
return -1 # return -1
if line == "": if line == "":
raise newException(ERedis, "Server closed connection prematurely") raise newException(ERedis, "Server closed connection prematurely")
@ -193,7 +195,6 @@ proc flushPipeline*(r: TRedis, wasMulti = false): TRedisList =
r.socket.send(r.pipeline.buffer) r.socket.send(r.pipeline.buffer)
r.pipeline.buffer = "" r.pipeline.buffer = ""
var prevState = r.pipeline.enabled
r.pipeline.enabled = false r.pipeline.enabled = false
result = @[] result = @[]
@ -201,23 +202,21 @@ proc flushPipeline*(r: TRedis, wasMulti = false): TRedisList =
for i in 0..tot-1: for i in 0..tot-1:
var ret = r.readNext() var ret = r.readNext()
if ret.len == 1 and (ret[0] == "OK" or ret[0] == "QUEUED"): for item in ret:
# Skip acknowledgement replies in multi var isOK = item.contains("OK")
if not wasMulti: result.add(ret) if not (item.contains("OK") or item.contains("QUEUED")):
else: result.add(item)
result.add(ret)
r.pipeline.expected = 0 r.pipeline.expected = 0
r.pipeline.enabled = prevState
proc setPipeline*(r: TRedis, state: bool) = proc startPipelining*(r: TRedis) =
## Enable or disable command pipelining (reduces network roundtrips). ## Enable command pipelining (reduces network roundtrips).
## Note that when enabled, you must call flushPipeline to actually send commands, except ## Note that when enabled, you must call flushPipeline to actually send commands, except
## for multi/exec() which enable and flush the pipeline automatically. ## for multi/exec() which enable and flush the pipeline automatically.
## Commands return immediately with dummy values; actual results returned from ## Commands return immediately with dummy values; actual results returned from
## flushPipeline() or exec() ## flushPipeline() or exec()
r.pipeline.expected = 0 r.pipeline.expected = 0
r.pipeline.enabled = state r.pipeline.enabled = true
proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) = proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) =
var request = "*" & $(1 + args.len()) & "\c\L" var request = "*" & $(1 + args.len()) & "\c\L"
@ -300,7 +299,7 @@ proc rename*(r: TRedis, key, newkey: string): TRedisStatus =
## ##
## **WARNING:** Overwrites `newkey` if it exists! ## **WARNING:** Overwrites `newkey` if it exists!
r.sendCommand("RENAME", key, newkey) r.sendCommand("RENAME", key, newkey)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc renameNX*(r: TRedis, key, newkey: string): bool = proc renameNX*(r: TRedis, key, newkey: string): bool =
## Same as ``rename`` but doesn't continue if `newkey` exists. ## Same as ``rename`` but doesn't continue if `newkey` exists.
@ -372,7 +371,7 @@ proc setk*(r: TRedis, key, value: string) =
## ##
## NOTE: This function had to be renamed due to a clash with the `set` type. ## NOTE: This function had to be renamed due to a clash with the `set` type.
r.sendCommand("SET", key, value) r.sendCommand("SET", key, value)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc setNX*(r: TRedis, key, value: string): bool = proc setNX*(r: TRedis, key, value: string): bool =
## Set the value of a key, only if the key does not exist. Returns `true` ## Set the value of a key, only if the key does not exist. Returns `true`
@ -389,7 +388,7 @@ proc setBit*(r: TRedis, key: string, offset: int,
proc setEx*(r: TRedis, key: string, seconds: int, value: string): TRedisStatus = proc setEx*(r: TRedis, key: string, seconds: int, value: string): TRedisStatus =
## Set the value and expiration of a key ## Set the value and expiration of a key
r.sendCommand("SETEX", key, $seconds, value) r.sendCommand("SETEX", key, $seconds, value)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc setRange*(r: TRedis, key: string, offset: int, proc setRange*(r: TRedis, key: string, offset: int,
value: string): TRedisInteger = value: string): TRedisInteger =
@ -452,7 +451,7 @@ proc hMSet*(r: TRedis, key: string,
args.add(field) args.add(field)
args.add(value) args.add(value)
r.sendCommand("HMSET", args) r.sendCommand("HMSET", args)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc hSet*(r: TRedis, key, field, value: string): TRedisInteger = proc hSet*(r: TRedis, key, field, value: string): TRedisInteger =
## Set the string value of a hash field ## Set the string value of a hash field
@ -546,12 +545,12 @@ proc lRem*(r: TRedis, key: string, value: string, count: int = 0): TRedisInteger
proc lSet*(r: TRedis, key: string, index: int, value: string) = proc lSet*(r: TRedis, key: string, index: int, value: string) =
## Set the value of an element in a list by its index ## Set the value of an element in a list by its index
r.sendCommand("LSET", key, $index, value) r.sendCommand("LSET", key, $index, value)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc lTrim*(r: TRedis, key: string, start, stop: int) = proc lTrim*(r: TRedis, key: string, start, stop: int) =
## Trim a list to the specified range ## Trim a list to the specified range
r.sendCommand("LTRIM", key, $start, $stop) r.sendCommand("LTRIM", key, $start, $stop)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc rPop*(r: TRedis, key: string): TRedisString = proc rPop*(r: TRedis, key: string): TRedisString =
## Remove and get the last element in a list ## Remove and get the last element in a list
@ -829,7 +828,7 @@ proc unsubscribe*(r: TRedis, [channel: openarray[string], : string): ???? =
proc discardMulti*(r: TRedis) = proc discardMulti*(r: TRedis) =
## Discard all commands issued after MULTI ## Discard all commands issued after MULTI
r.sendCommand("DISCARD") r.sendCommand("DISCARD")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc exec*(r: TRedis): TRedisList = proc exec*(r: TRedis): TRedisList =
## Execute all commands issued after MULTI ## Execute all commands issued after MULTI
@ -842,26 +841,26 @@ proc exec*(r: TRedis): TRedisList =
proc multi*(r: TRedis) = proc multi*(r: TRedis) =
## Mark the start of a transaction block ## Mark the start of a transaction block
r.setPipeline(true) r.startPipelining()
r.sendCommand("MULTI") r.sendCommand("MULTI")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc unwatch*(r: TRedis) = proc unwatch*(r: TRedis) =
## Forget about all watched keys ## Forget about all watched keys
r.sendCommand("UNWATCH") r.sendCommand("UNWATCH")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc watch*(r: TRedis, key: varargs[string]) = proc watch*(r: TRedis, key: varargs[string]) =
## Watch the given keys to determine execution of the MULTI/EXEC block ## Watch the given keys to determine execution of the MULTI/EXEC block
r.sendCommand("WATCH", key) r.sendCommand("WATCH", key)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
# Connection # Connection
proc auth*(r: TRedis, password: string) = proc auth*(r: TRedis, password: string) =
## Authenticate to the server ## Authenticate to the server
r.sendCommand("AUTH", password) r.sendCommand("AUTH", password)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc echoServ*(r: TRedis, message: string): TRedisString = proc echoServ*(r: TRedis, message: string): TRedisString =
## Echo the given string ## Echo the given string
@ -876,7 +875,7 @@ proc ping*(r: TRedis): TRedisStatus =
proc quit*(r: TRedis) = proc quit*(r: TRedis) =
## Close the connection ## Close the connection
r.sendCommand("QUIT") r.sendCommand("QUIT")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc select*(r: TRedis, index: int): TRedisStatus = proc select*(r: TRedis, index: int): TRedisStatus =
## Change the selected database for the current connection ## Change the selected database for the current connection
@ -888,12 +887,12 @@ proc select*(r: TRedis, index: int): TRedisStatus =
proc bgrewriteaof*(r: TRedis) = proc bgrewriteaof*(r: TRedis) =
## Asynchronously rewrite the append-only file ## Asynchronously rewrite the append-only file
r.sendCommand("BGREWRITEAOF") r.sendCommand("BGREWRITEAOF")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc bgsave*(r: TRedis) = proc bgsave*(r: TRedis) =
## Asynchronously save the dataset to disk ## Asynchronously save the dataset to disk
r.sendCommand("BGSAVE") r.sendCommand("BGSAVE")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc configGet*(r: TRedis, parameter: string): TRedisList = proc configGet*(r: TRedis, parameter: string): TRedisList =
## Get the value of a configuration parameter ## Get the value of a configuration parameter
@ -903,12 +902,12 @@ proc configGet*(r: TRedis, parameter: string): TRedisList =
proc configSet*(r: TRedis, parameter: string, value: string) = proc configSet*(r: TRedis, parameter: string, value: string) =
## Set a configuration parameter to the given value ## Set a configuration parameter to the given value
r.sendCommand("CONFIG", "SET", parameter, value) r.sendCommand("CONFIG", "SET", parameter, value)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc configResetStat*(r: TRedis) = proc configResetStat*(r: TRedis) =
## Reset the stats returned by INFO ## Reset the stats returned by INFO
r.sendCommand("CONFIG", "RESETSTAT") r.sendCommand("CONFIG", "RESETSTAT")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc dbsize*(r: TRedis): TRedisInteger = proc dbsize*(r: TRedis): TRedisInteger =
## Return the number of keys in the selected database ## Return the number of keys in the selected database
@ -927,12 +926,12 @@ proc debugSegfault*(r: TRedis) =
proc flushall*(r: TRedis): TRedisStatus = proc flushall*(r: TRedis): TRedisStatus =
## Remove all keys from all databases ## Remove all keys from all databases
r.sendCommand("FLUSHALL") r.sendCommand("FLUSHALL")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc flushdb*(r: TRedis): TRedisStatus = proc flushdb*(r: TRedis): TRedisStatus =
## Remove all keys from the current database ## Remove all keys from the current database
r.sendCommand("FLUSHDB") r.sendCommand("FLUSHDB")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc info*(r: TRedis): TRedisString = proc info*(r: TRedis): TRedisString =
## Get information and statistics about the server ## Get information and statistics about the server
@ -948,13 +947,13 @@ discard """
proc monitor*(r: TRedis) = proc monitor*(r: TRedis) =
## Listen for all requests received by the server in real time ## Listen for all requests received by the server in real time
r.socket.send("MONITOR\c\L") r.socket.send("MONITOR\c\L")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
""" """
proc save*(r: TRedis) = proc save*(r: TRedis) =
## Synchronously save the dataset to disk ## Synchronously save the dataset to disk
r.sendCommand("SAVE") r.sendCommand("SAVE")
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
proc shutdown*(r: TRedis) = proc shutdown*(r: TRedis) =
## Synchronously save the dataset to disk and then shut down the server ## Synchronously save the dataset to disk and then shut down the server
@ -966,7 +965,7 @@ proc shutdown*(r: TRedis) =
proc slaveof*(r: TRedis, host: string, port: string) = proc slaveof*(r: TRedis, host: string, port: string) =
## Make the server a slave of another instance, or promote it as master ## Make the server a slave of another instance, or promote it as master
r.sendCommand("SLAVEOF", host, port) r.sendCommand("SLAVEOF", host, port)
raiseNoOK(r.readStatus()) raiseNoOK(r.readStatus(), r.pipeline.enabled)
iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] = iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] =
## Iterator for keys and values in a hash. ## Iterator for keys and values in a hash.
@ -980,67 +979,72 @@ iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] =
yield (k, i) yield (k, i)
k = "" k = ""
proc someTests(r: TRedis) = proc someTests(r: TRedis, how: string):seq[string] =
#r.auth("pass") var list:seq[string] = @[]
case how
of "pipelined":
r.startPipelining()
of "multi":
r.multi()
r.setk("nim:test", "Testing something.") r.setk("nim:test", "Testing something.")
r.setk("nim:utf8", "こんにちは") r.setk("nim:utf8", "こんにちは")
r.setk("nim:esc", "\\ths ągt\\") r.setk("nim:esc", "\\ths ągt\\")
r.setk("nim:int", "1") r.setk("nim:int", "1")
echo(r.get("nim:esc")) list.add(r.get("nim:esc"))
echo(r.incr("nim:int")) list.add($(r.incr("nim:int")))
echo r.get("nim:int") list.add(r.get("nim:int"))
echo r.get("nim:utf8") list.add(r.get("nim:utf8"))
echo r.hSet("test1", "name", "A Test") list.add($(r.hSet("test1", "name", "A Test")))
var res = r.hGetAll("test1") var res = r.hGetAll("test1")
echo repr(r.get("blahasha")) for r in res:
echo r.randomKey() list.add(r)
discard r.lpush("mylist","itema") list.add(r.get("invalid_key"))
discard r.lpush("mylist","itemb") list.add($(r.lpush("mylist","itema")))
list.add($(r.lpush("mylist","itemb")))
r.ltrim("mylist",0,1) r.ltrim("mylist",0,1)
var p = r.lrange("mylist", 0, -1) var p = r.lrange("mylist", 0, -1)
for i in items(p): for i in items(p):
if not isNil(i): if not isNil(i):
echo(" ", i) list.add(i)
echo(r.debugObject("mylist")) list.add(r.debugObject("mylist"))
r.configSet("timeout", "299") r.configSet("timeout", "299")
for i in items(r.configGet("timeout")): echo ">> ", i var g = r.configGet("timeout")
for i in items(g):
list.add(i)
echo r.echoServ("BLAH") list.add(r.echoServ("BLAH"))
case how
of "normal":
return list
of "pipelined":
return r.flushPipeline()
of "multi":
return r.exec()
when false: proc assertListsIdentical(listA, listB: seq[string]) =
var r = open() assert(listA.len == listB.len)
var i = 0
for item in listA:
assert(item == listB[i])
i = i + 1
# Test with no pipelining when isMainModule:
echo("----------------------------------------------") when false:
echo("Testing without pipelining.") var r = open()
r.someTests()
# Test with pipelining enabled # Test with no pipelining
echo("//////////////////////////////////////////////") var listNormal = r.someTests("normal")
echo()
echo("Testing with pipelining.")
echo()
r.setPipeline(true)
r.someTests()
var list = r.flushPipeline()
r.setPipeline(false)
echo("-- list length is " & $list.len & " --")
for item in list:
if not isNil(item):
echo item
# Test with multi/exec() (automatic pipelining) # Test with pipelining enabled
echo("************************************************") var listPipelined = r.someTests("pipelined")
echo("Testing with transaction (automatic pipelining)") assertListsIdentical(listNormal, listPipelined)
r.multi()
r.someTests() # Test with multi/exec() (automatic pipelining)
list = r.exec() var listMulti = r.someTests("multi")
echo("-- list length is " & $list.len & " --") assertListsIdentical(listNormal, listMulti)
for item in list:
if not isNil(item):
echo item