mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
e2641e09cc
networking related stuff moved into networking.c moved more code more work on layout of source code SDS instantaneuos memory saving. By Pieter and Salvatore at VMware ;) cleanly compiling again after the first split, now splitting it in more C files moving more things around... work in progress split replication code splitting more Sets split Hash split replication split even more splitting more splitting minor change
269 lines
7.7 KiB
Tcl
269 lines
7.7 KiB
Tcl
set ::global_overrides {}
|
|
set ::tags {}
|
|
|
|
proc error_and_quit {config_file error} {
|
|
puts "!!COULD NOT START REDIS-SERVER\n"
|
|
puts "CONFIGURATION:"
|
|
puts [exec cat $config_file]
|
|
puts "\nERROR:"
|
|
puts [string trim $error]
|
|
exit 1
|
|
}
|
|
|
|
proc check_valgrind_errors stderr {
|
|
set fd [open $stderr]
|
|
set buf [read $fd]
|
|
close $fd
|
|
|
|
if {![regexp -- {ERROR SUMMARY: 0 errors} $buf] ||
|
|
![regexp -- {definitely lost: 0 bytes} $buf]} {
|
|
puts "*** VALGRIND ERRORS ***"
|
|
puts $buf
|
|
puts "--- press enter to continue ---"
|
|
gets stdin
|
|
}
|
|
}
|
|
|
|
proc kill_server config {
|
|
# nothing to kill when running against external server
|
|
if {$::external} return
|
|
|
|
# nevermind if its already dead
|
|
if {![is_alive $config]} { return }
|
|
set pid [dict get $config pid]
|
|
|
|
# check for leaks
|
|
if {![dict exists $config "skipleaks"]} {
|
|
catch {
|
|
if {[string match {*Darwin*} [exec uname -a]]} {
|
|
tags {"leaks"} {
|
|
test "Check for memory leaks (pid $pid)" {
|
|
exec leaks $pid
|
|
} {*0 leaks*}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# kill server and wait for the process to be totally exited
|
|
while {[is_alive $config]} {
|
|
if {[incr wait 10] % 1000 == 0} {
|
|
puts "Waiting for process $pid to exit..."
|
|
}
|
|
catch {exec kill $pid}
|
|
after 10
|
|
}
|
|
|
|
# Check valgrind errors if needed
|
|
if {$::valgrind} {
|
|
check_valgrind_errors [dict get $config stderr]
|
|
}
|
|
}
|
|
|
|
proc is_alive config {
|
|
set pid [dict get $config pid]
|
|
if {[catch {exec ps -p $pid} err]} {
|
|
return 0
|
|
} else {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
proc ping_server {host port} {
|
|
set retval 0
|
|
if {[catch {
|
|
set fd [socket $::host $::port]
|
|
fconfigure $fd -translation binary
|
|
puts $fd "PING\r\n"
|
|
flush $fd
|
|
set reply [gets $fd]
|
|
if {[string range $reply 0 4] eq {+PONG} ||
|
|
[string range $reply 0 3] eq {-ERR}} {
|
|
set retval 1
|
|
}
|
|
close $fd
|
|
} e]} {
|
|
puts "Can't PING server at $host:$port... $e"
|
|
}
|
|
return $retval
|
|
}
|
|
|
|
# doesn't really belong here, but highly coupled to code in start_server
|
|
proc tags {tags code} {
|
|
set ::tags [concat $::tags $tags]
|
|
uplevel 1 $code
|
|
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
|
}
|
|
|
|
proc start_server {options {code undefined}} {
|
|
# If we are runnign against an external server, we just push the
|
|
# host/port pair in the stack the first time
|
|
if {$::external} {
|
|
if {[llength $::servers] == 0} {
|
|
set srv {}
|
|
dict set srv "host" $::host
|
|
dict set srv "port" $::port
|
|
set client [redis $::host $::port]
|
|
dict set srv "client" $client
|
|
$client select 9
|
|
|
|
# append the server to the stack
|
|
lappend ::servers $srv
|
|
}
|
|
uplevel 1 $code
|
|
return
|
|
}
|
|
|
|
# setup defaults
|
|
set baseconfig "default.conf"
|
|
set overrides {}
|
|
set tags {}
|
|
|
|
# parse options
|
|
foreach {option value} $options {
|
|
switch $option {
|
|
"config" {
|
|
set baseconfig $value }
|
|
"overrides" {
|
|
set overrides $value }
|
|
"tags" {
|
|
set tags $value
|
|
set ::tags [concat $::tags $value] }
|
|
default {
|
|
error "Unknown option $option" }
|
|
}
|
|
}
|
|
|
|
set data [split [exec cat "tests/assets/$baseconfig"] "\n"]
|
|
set config {}
|
|
foreach line $data {
|
|
if {[string length $line] > 0 && [string index $line 0] ne "#"} {
|
|
set elements [split $line " "]
|
|
set directive [lrange $elements 0 0]
|
|
set arguments [lrange $elements 1 end]
|
|
dict set config $directive $arguments
|
|
}
|
|
}
|
|
|
|
# use a different directory every time a server is started
|
|
dict set config dir [tmpdir server]
|
|
|
|
# start every server on a different port
|
|
dict set config port [incr ::port]
|
|
|
|
# apply overrides from global space and arguments
|
|
foreach {directive arguments} [concat $::global_overrides $overrides] {
|
|
dict set config $directive $arguments
|
|
}
|
|
|
|
# write new configuration to temporary file
|
|
set config_file [tmpfile redis.conf]
|
|
set fp [open $config_file w+]
|
|
foreach directive [dict keys $config] {
|
|
puts -nonewline $fp "$directive "
|
|
puts $fp [dict get $config $directive]
|
|
}
|
|
close $fp
|
|
|
|
set stdout [format "%s/%s" [dict get $config "dir"] "stdout"]
|
|
set stderr [format "%s/%s" [dict get $config "dir"] "stderr"]
|
|
|
|
if {$::valgrind} {
|
|
exec valgrind src/redis-server $config_file > $stdout 2> $stderr &
|
|
after 2000
|
|
} else {
|
|
exec src/redis-server $config_file > $stdout 2> $stderr &
|
|
after 500
|
|
}
|
|
|
|
# check that the server actually started
|
|
if {$code ne "undefined" && ![ping_server $::host $::port]} {
|
|
error_and_quit $config_file [exec cat $stderr]
|
|
}
|
|
|
|
# find out the pid
|
|
while {![info exists pid]} {
|
|
regexp {^\[(\d+)\]} [exec head -n1 $stdout] _ pid
|
|
after 100
|
|
}
|
|
|
|
# setup properties to be able to initialize a client object
|
|
set host $::host
|
|
set port $::port
|
|
if {[dict exists $config bind]} { set host [dict get $config bind] }
|
|
if {[dict exists $config port]} { set port [dict get $config port] }
|
|
|
|
# setup config dict
|
|
dict set srv "config" $config_file
|
|
dict set srv "pid" $pid
|
|
dict set srv "host" $host
|
|
dict set srv "port" $port
|
|
dict set srv "stdout" $stdout
|
|
dict set srv "stderr" $stderr
|
|
|
|
# if a block of code is supplied, we wait for the server to become
|
|
# available, create a client object and kill the server afterwards
|
|
if {$code ne "undefined"} {
|
|
set line [exec head -n1 $stdout]
|
|
if {[string match {*already in use*} $line]} {
|
|
error_and_quit $config_file $line
|
|
}
|
|
|
|
while 1 {
|
|
# check that the server actually started and is ready for connections
|
|
if {[exec cat $stdout | grep "ready to accept" | wc -l] > 0} {
|
|
break
|
|
}
|
|
after 10
|
|
}
|
|
|
|
set client [redis $host $port]
|
|
dict set srv "client" $client
|
|
|
|
# select the right db when we don't have to authenticate
|
|
if {![dict exists $config requirepass]} {
|
|
$client select 9
|
|
}
|
|
|
|
# append the server to the stack
|
|
lappend ::servers $srv
|
|
|
|
# execute provided block
|
|
set curnum $::testnum
|
|
catch { uplevel 1 $code } err
|
|
if {$curnum == $::testnum} {
|
|
# don't check for leaks when no tests were executed
|
|
dict set srv "skipleaks" 1
|
|
}
|
|
|
|
# pop the server object
|
|
set ::servers [lrange $::servers 0 end-1]
|
|
|
|
# allow an exception to bubble up the call chain but still kill this
|
|
# server, because we want to reuse the ports when the tests are re-run
|
|
if {$err eq "exception"} {
|
|
puts [format "Logged warnings (pid %d):" [dict get $srv "pid"]]
|
|
set warnings [warnings_from_file [dict get $srv "stdout"]]
|
|
if {[string length $warnings] > 0} {
|
|
puts "$warnings"
|
|
} else {
|
|
puts "(none)"
|
|
}
|
|
# kill this server without checking for leaks
|
|
dict set srv "skipleaks" 1
|
|
kill_server $srv
|
|
error "exception"
|
|
} elseif {[string length $err] > 0} {
|
|
puts "Error executing the suite, aborting..."
|
|
puts $err
|
|
exit 1
|
|
}
|
|
|
|
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
|
kill_server $srv
|
|
} else {
|
|
set ::tags [lrange $::tags 0 end-[llength $tags]]
|
|
set _ $srv
|
|
}
|
|
}
|