Unverified Commit 2667c412 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge pull request #10829 from oranagra/release-7.0.1

Release 7.0.1
parents d375595d 595e725d
......@@ -7,14 +7,14 @@ start_server {tags {"pubsubshard external:skip"}} {
assert_equal {2} [ssubscribe $rd1 {chan2}]
assert_equal 1 [r SPUBLISH chan1 hello]
assert_equal 1 [r SPUBLISH chan2 world]
assert_equal {message chan1 hello} [$rd1 read]
assert_equal {message chan2 world} [$rd1 read]
assert_equal {smessage chan1 hello} [$rd1 read]
assert_equal {smessage chan2 world} [$rd1 read]
# unsubscribe from one of the channels
sunsubscribe $rd1 {chan1}
assert_equal 0 [r SPUBLISH chan1 hello]
assert_equal 1 [r SPUBLISH chan2 world]
assert_equal {message chan2 world} [$rd1 read]
assert_equal {smessage chan2 world} [$rd1 read]
# unsubscribe from the remaining channel
sunsubscribe $rd1 {chan2}
......@@ -32,8 +32,8 @@ start_server {tags {"pubsubshard external:skip"}} {
assert_equal {1} [ssubscribe $rd1 {chan1}]
assert_equal {1} [ssubscribe $rd2 {chan1}]
assert_equal 2 [r SPUBLISH chan1 hello]
assert_equal {message chan1 hello} [$rd1 read]
assert_equal {message chan1 hello} [$rd2 read]
assert_equal {smessage chan1 hello} [$rd1 read]
assert_equal {smessage chan1 hello} [$rd2 read]
# clean up clients
$rd1 close
......@@ -58,7 +58,7 @@ start_server {tags {"pubsubshard external:skip"}} {
set rd1 [redis_deferring_client]
assert_equal {1 1 1} [ssubscribe $rd1 {chan1 chan1 chan1}]
assert_equal 1 [r SPUBLISH chan1 hello]
assert_equal {message chan1 hello} [$rd1 read]
assert_equal {smessage chan1 hello} [$rd1 read]
# clean up clients
$rd1 close
......@@ -129,9 +129,9 @@ start_server {tags {"pubsubshard external:skip"}} {
assert_equal {1} [ssubscribe $rd1 {chan1}]
$rd0 SPUBLISH chan1 hello
assert_equal {message chan1 hello} [$rd1 read]
assert_equal {smessage chan1 hello} [$rd1 read]
$rd0 SPUBLISH chan1 world
assert_equal {message chan1 world} [$rd1 read]
assert_equal {smessage chan1 world} [$rd1 read]
}
}
}
\ No newline at end of file
......@@ -1416,18 +1416,49 @@ start_server {tags {"scripting"}} {
] {123}
# Fail to execute regardless of script content when we use default flags in OOM condition
assert_error {OOM allow-oom flag is not set on the script, can not run it when used memory > 'maxmemory'} {
assert_error {OOM *} {
r eval {#!lua flags=
return 1
} 0
}
# Script with allow-oom can write despite being in OOM state
assert_equal [
r eval {#!lua flags=allow-oom
redis.call('set','x',1)
return 1
} 1 x
] 1
# read-only scripts implies allow-oom
assert_equal [
r eval {#!lua flags=no-writes
redis.call('get','x')
return 1
} 0
] 1
assert_equal [
r eval_ro {#!lua flags=no-writes
redis.call('get','x')
return 1
} 1 x
] 1
# Script with no shebang can read in OOM state
assert_equal [
r eval {
redis.call('get','x')
return 1
} 1 x
] 1
# Script with no shebang can read in OOM state (eval_ro variant)
assert_equal [
r eval_ro {
redis.call('get','x')
return 1
} 1 x
] 1
r config set maxmemory 0
} {OK} {needs:config-maxmemory}
......@@ -1458,12 +1489,85 @@ start_server {tags {"scripting"}} {
} 1 x
] "some value"
assert_error {ERR Can not run script with write flag on readonly replica} {
assert_error {READONLY You can't write against a read only replica.} {
r eval {#!lua
return redis.call('get','x')
} 1 x
}
# test no-write inside multi-exec
r multi
r eval {#!lua flags=no-writes
redis.call('get','x')
return 1
} 1 x
assert_equal [r exec] 1
# test no shebang without write inside multi-exec
r multi
r eval {
redis.call('get','x')
return 1
} 1 x
assert_equal [r exec] 1
# temporarily set the server to master, so it doesn't block the queuing
# and we can test the evaluation of the flags on exec
r replicaof no one
set rr [redis_client]
set rr2 [redis_client]
$rr multi
$rr2 multi
# test write inside multi-exec
# we don't need to do any actual write
$rr eval {#!lua
return 1
} 0
# test no shebang with write inside multi-exec
$rr2 eval {
redis.call('set','x',1)
return 1
} 1 x
r replicaof [srv -1 host] [srv -1 port]
assert_error {EXECABORT Transaction discarded because of: READONLY *} {$rr exec}
assert_error {READONLY You can't write against a read only replica. script: *} {$rr2 exec}
$rr close
$rr2 close
}
}
test "not enough good replicas" {
r set x "some value"
r config set min-replicas-to-write 1
assert_equal [
r eval {#!lua flags=no-writes
return redis.call('get','x')
} 1 x
] "some value"
assert_equal [
r eval {
return redis.call('get','x')
} 1 x
] "some value"
assert_error {NOREPLICAS *} {
r eval {#!lua
return redis.call('get','x')
} 1 x
}
assert_error {NOREPLICAS *} {
r eval {
return redis.call('set','x', 1)
} 1 x
}
r config set min-replicas-to-write 0
}
test "allow-stale shebang flag" {
......@@ -1476,7 +1580,7 @@ start_server {tags {"scripting"}} {
} 1 x
}
assert_error {*'allow-stale' flag is not set on the script*} {
assert_error {MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'.} {
r eval {#!lua flags=no-writes
return 1
} 0
......@@ -1517,7 +1621,7 @@ start_server {tags {"scripting"}} {
test "reject script do not cause a Lua stack leak" {
r config set maxmemory 1
for {set i 0} {$i < 50} {incr i} {
assert_error {OOM allow-oom flag is not set on the script, can not run it when used memory > 'maxmemory'} {r eval {#!lua
assert_error {OOM *} {r eval {#!lua
return 1
} 0}
}
......
......@@ -208,6 +208,33 @@ start_server {tags {"tracking network"}} {
assert {$res eq {key1}}
}
test {Tracking only occurs for scripts when a command calls a read-only command} {
r CLIENT TRACKING off
r CLIENT TRACKING on
$rd_sg MSET key2{t} 1 key2{t} 1
# If a script doesn't call any read command, don't track any keys
r EVAL "redis.call('set', 'key3{t}', 'bar')" 2 key1{t} key2{t}
$rd_sg MSET key2{t} 2 key1{t} 2
# If a script calls a read command, track all declared keys
r EVAL "redis.call('get', 'key3{t}')" 2 key1{t} key2{t}
$rd_sg MSET key2{t} 2 key1{t} 2
assert_equal {invalidate key2{t}} [r read]
assert_equal {invalidate key1{t}} [r read]
# RO variants work like the normal variants
r EVAL_RO "redis.call('ping')" 2 key1{t} key2{t}
$rd_sg MSET key2{t} 2 key1{t} 2
r EVAL_RO "redis.call('get', 'key1{t}')" 2 key1{t} key2{t}
$rd_sg MSET key2{t} 3 key1{t} 3
assert_equal {invalidate key2{t}} [r read]
assert_equal {invalidate key1{t}} [r read]
assert_equal "PONG" [r ping]
}
test {RESP3 Client gets tracking-redir-broken push message after cached key changed when rediretion client is terminated} {
r CLIENT TRACKING on REDIRECT $redir_id
$rd_sg SET key1 1
......@@ -369,7 +396,7 @@ start_server {tags {"tracking network"}} {
$r CLIENT TRACKING OFF
}
test {hdel deliver invlidate message after response in the same connection} {
test {hdel deliver invalidate message after response in the same connection} {
r CLIENT TRACKING off
r HELLO 3
r CLIENT TRACKING on
......
......@@ -389,12 +389,13 @@ start_server {
assert {[r LPOS mylist c] == 2}
}
test {LPOS RANK (positive and negative rank) option} {
test {LPOS RANK (positive, negative and zero rank) option} {
assert {[r LPOS mylist c RANK 1] == 2}
assert {[r LPOS mylist c RANK 2] == 6}
assert {[r LPOS mylist c RANK 4] eq ""}
assert {[r LPOS mylist c RANK -1] == 7}
assert {[r LPOS mylist c RANK -2] == 6}
assert_error "*RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start*" {r LPOS mylist c RANK 0}
}
test {LPOS COUNT option} {
......
......@@ -285,6 +285,13 @@ start_server {
}
assert_equal {1 2 3 4} [lsort [r smembers setres{t}]]
}
test "SINTER/SUNION/SDIFF with three same sets - $type" {
set expected [lsort "[r smembers set1{t}]"]
assert_equal $expected [lsort [r sinter set1{t} set1{t} set1{t}]]
assert_equal $expected [lsort [r sunion set1{t} set1{t} set1{t}]]
assert_equal {} [lsort [r sdiff set1{t} set1{t} set1{t}]]
}
}
test "SDIFF with first set empty" {
......
......@@ -2065,6 +2065,33 @@ start_server {tags {"zset"}} {
close_replication_stream $repl
} {} {needs:repl}
test "BZMPOP should not blocks on non key arguments - #10762" {
set rd1 [redis_deferring_client]
set rd2 [redis_deferring_client]
r del myzset myzset2 myzset3
$rd1 bzmpop 0 1 myzset min count 10
wait_for_blocked_clients_count 1
$rd2 bzmpop 0 2 myzset2 myzset3 max count 10
wait_for_blocked_clients_count 2
# These non-key keys will not unblock the clients.
r zadd 0 100 timeout_value
r zadd 1 200 numkeys_value
r zadd min 300 min_token
r zadd max 400 max_token
r zadd count 500 count_token
r zadd 10 600 count_value
r zadd myzset 1 zset
r zadd myzset3 1 zset3
assert_equal {myzset {{zset 1}}} [$rd1 read]
assert_equal {myzset3 {{zset3 1}}} [$rd2 read]
$rd1 close
$rd2 close
} {0} {cluster:skip}
test {ZSET skiplist order consistency when elements are moved} {
set original_max [lindex [r config get zset-max-ziplist-entries] 1]
r config set zset-max-ziplist-entries 0
......@@ -2181,6 +2208,15 @@ start_server {tags {"zset"}} {
assert_match "*syntax*" $err
}
test {ZRANGESTORE with zset-max-listpack-entries 0 dst key should use skiplist encoding} {
set original_max [lindex [r config get zset-max-listpack-entries] 1]
r config set zset-max-listpack-entries 0
r del z1{t} z2{t}
r zadd z1{t} 1 a
assert_equal 1 [r zrangestore z2{t} z1{t} 0 -1]
r config set zset-max-listpack-entries $original_max
}
test {ZRANGE invalid syntax} {
catch {r zrange z1{t} 0 -1 limit 1 2} err
assert_match "*syntax*" $err
......
#!/usr/bin/env python3
import os
import glob
import json
import os
ARG_TYPES = {
"string": "ARG_TYPE_STRING",
......@@ -68,6 +67,55 @@ def get_optional_desc_string(desc, field, force_uppercase=False):
return ret.replace("\n", "\\n")
def check_command_args_key_specs(args, command_key_specs_index_set, command_arg_key_specs_index_set):
if not args:
return True
for arg in args:
if arg.key_spec_index is not None:
assert isinstance(arg.key_spec_index, int)
if arg.key_spec_index not in command_key_specs_index_set:
print("command: %s arg: %s key_spec_index error" % (command.fullname(), arg.name))
return False
command_arg_key_specs_index_set.add(arg.key_spec_index)
if not check_command_args_key_specs(arg.subargs, command_key_specs_index_set, command_arg_key_specs_index_set):
return False
return True
def check_command_key_specs(command):
if not command.key_specs:
return True
assert isinstance(command.key_specs, list)
for cmd_key_spec in command.key_specs:
if "flags" not in cmd_key_spec:
print("command: %s key_specs missing flags" % command.fullname())
return False
if "NOT_KEY" in cmd_key_spec["flags"]:
# Like SUNSUBSCRIBE / SPUBLISH / SSUBSCRIBE
return True
command_key_specs_index_set = set(range(len(command.key_specs)))
command_arg_key_specs_index_set = set()
# Collect key_spec used for each arg, including arg.subarg
if not check_command_args_key_specs(command.args, command_key_specs_index_set, command_arg_key_specs_index_set):
return False
# Check if we have key_specs not used
if command_key_specs_index_set != command_arg_key_specs_index_set:
print("command: %s may have unused key_spec" % command.fullname())
return False
return True
# Globals
subcommands = {} # container_name -> dict(subcommand_name -> Subcommand) - Only subcommands
commands = {} # command_name -> Command - Only commands
......@@ -132,6 +180,7 @@ class Argument(object):
self.desc = desc
self.name = self.desc["name"].lower()
self.type = self.desc["type"]
self.key_spec_index = self.desc.get("key_spec_index", None)
self.parent_name = parent_name
self.subargs = []
self.subargs_name = None
......@@ -200,6 +249,7 @@ class Command(object):
self.name = name.upper()
self.desc = desc
self.group = self.desc["group"]
self.key_specs = self.desc.get("key_specs", [])
self.subcommands = []
self.args = []
for arg_desc in self.desc.get("arguments", []):
......@@ -271,7 +321,7 @@ class Command(object):
def _key_specs_code():
s = ""
for spec in self.desc.get("key_specs", []):
for spec in self.key_specs:
s += "{%s}," % KeySpec(spec).struct_code()
return s[:-1]
......@@ -398,6 +448,17 @@ for command in commands.values():
subcommand.group = command.group
command.subcommands.append(subcommand)
check_command_error_counter = 0 # An error counter is used to count errors in command checking.
print("Checking all commands...")
for command in commands.values():
if not check_command_key_specs(command):
check_command_error_counter += 1
if check_command_error_counter != 0:
print("Error: There are errors in the commands check, please check the above logs.")
exit(1)
print("Generating commands.c...")
with open("%s/commands.c" % srcdir, "w") as f:
f.write("/* Automatically generated by %s, do not edit. */\n\n" % os.path.basename(__file__))
......
......@@ -45,7 +45,11 @@ def argument arg
name = arg["name"].is_a?(Array) ? arg["name"].join(" ") : arg["name"]
end
if arg["multiple"]
name = "#{name} [#{name} ...]"
if arg["multiple_token"]
name = "#{name} [#{arg["token"]} #{name} ...]"
else
name = "#{name} [#{name} ...]"
end
end
if arg["token"]
name = [arg["token"], name].compact.join " "
......
#!/usr/bin/env python3
import argparse
import json
import os
import subprocess
from collections import OrderedDict
from sys import argv, stdin
import os
from sys import argv
def convert_flags_to_boolean_dict(flags):
......@@ -118,7 +118,8 @@ if __name__ == '__main__':
stdout, stderr = p.communicate()
commands = json.loads(stdout)
p = subprocess.Popen([args.cli, '-h', args.host, '-p', str(args.port), '--json', 'command', 'docs'], stdout=subprocess.PIPE)
p = subprocess.Popen([args.cli, '-h', args.host, '-p', str(args.port), '--json', 'command', 'docs'],
stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
docs = json.loads(stdout)
......
#!/usr/bin/env ruby
# coding: utf-8
# gendoc.rb -- Converts the top-comments inside module.c to modules API
# reference documentation in markdown format.
......@@ -80,7 +79,7 @@ def docufy(src,i)
puts "<span id=\"#{name}\"></span>\n\n"
puts "### `#{name}`\n\n"
puts " #{proto}\n"
puts "**Available since:** #{$since[name]}\n\n" if $since[name]
puts "**Available since:** #{$since[name] or "unreleased"}\n\n"
comment = ""
while true
i = i-1
......@@ -137,7 +136,16 @@ def is_func_line(src, i)
src[i-1] =~ /\*\//
end
puts "# Modules API reference\n\n"
puts "---\n"
puts "title: \"Modules API reference\"\n"
puts "linkTitle: \"API reference\"\n"
puts "weight: 1\n"
puts "description: >\n"
puts " Reference for the Redis Modules API\n"
puts "aliases:\n"
puts " - /topics/modules-api-ref\n"
puts "---\n"
puts "\n"
puts "<!-- This file is generated from module.c using\n"
puts " utils/generate-module-api-doc.rb -->\n\n"
src = File.open(File.dirname(__FILE__) ++ "/../src/module.c").to_a
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment