regression_test.sh 14.6 KB
Newer Older
1
#!/bin/bash
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# The RocksDB regression test script.
# REQUIREMENT: must be able to run make db_bench in the current directory
#
# This script will do the following things in order:
#
# 1. check out the specified rocksdb commit.
# 2. build db_bench using the specified commit
# 3. setup test directory $TEST_PATH.  If not specified, then the test directory
#    will be "/tmp/rocksdb/regression_test"
# 4. run set of benchmarks on the specified host
#    (can be either locally or remotely)
# 5. generate report in the $RESULT_PATH.  If RESULT_PATH is not specified,
#    RESULT_PATH will be set to $TEST_PATH/current_time
#
# = Examples =
# * Run the regression test using rocksdb commit abcdef that outputs results
18 19
#   and temp files in "/my/output/dir"
#r
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
#   TEST_PATH=/my/output/dir COMMIT_ID=abcdef ./tools/regression_test.sh
#
# * Run the regression test on a remost host under "/my/output/dir" directory
#   and stores the result locally in "/my/benchmark/results" using commit
#   abcdef and with the rocksdb options specified in /my/path/to/OPTIONS-012345
#   with 1000000000 keys in each benchmark in the regression test where each
#   key and value are 100 and 900 bytes respectively:
#
#   REMOTE_USER_AT_HOST=yhchiang@my.remote.host \
#       TEST_PATH=/my/output/dir \
#       RESULT_PATH=/my/benchmark/results \
#       COMMIT_ID=abcdef \
#       OPTIONS_FILE=/my/path/to/OPTIONS-012345 \
#       NUM_KEYS=1000000000 \
#       KEY_SIZE=100 \
#       VALUE_SIZE=900 \
#       ./tools/regression_test.sh
#
# = Regression test environmental parameters =
39 40 41
#   DEBUG: If true, then the script will not checkout master and build db_bench
#       if db_bench already exists
#       Default: 0
A
Aaron Gao 已提交
42 43 44 45
#   TEST_MODE: If 1, run fillseqdeterminstic and benchmarks both
#       if 0, only run fillseqdeterministc
#       if 2, only run benchmarks
#       Default: 1
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#   TEST_PATH: the root directory of the regression test.
#       Default: "/tmp/rocksdb/regression_test"
#   RESULT_PATH: the directory where the regression results will be generated.
#       Default: "$TEST_PATH/current_time"
#   REMOTE_USER_AT_HOST: If set, then test will run on the specified host under
#       TEST_PATH directory and outputs test results locally in RESULT_PATH
#       The REMOTE_USER_AT_HOST should follow the format user-id@host.name
#   DB_PATH: the path where the rocksdb database will be created during the
#       regression test.  Default:  $TEST_PATH/db
#   WAL_PATH: the path where the rocksdb WAL will be outputed.
#       Default:  $TEST_PATH/wal
#   OPTIONS_FILE:  If specified, then the regression test will use the specified
#       file to initialize the RocksDB options in its benchmarks.  Note that
#       this feature only work for commits after 88acd93 or rocksdb version
#       later than 4.9.
Y
Yueh-Hsuan Chiang 已提交
61 62 63
#   DELETE_TEST_PATH: If true, then the test directory will be deleted
#       after the script ends.
#       Default: 0
64 65 66 67
#
# = db_bench parameters =
#   NUM_THREADS:  The number of concurrent foreground threads that will issue
#       database operations in the benchmark.  Default: 16.
Y
Yueh-Hsuan Chiang 已提交
68
#   NUM_KEYS:  The key range that will be used in the entire regression test.
69
#       Default: 1G.
Y
Yueh-Hsuan Chiang 已提交
70 71 72
#   NUM_OPS:  The number of operations (reads, writes, or deletes) that will
#       be issued in EACH thread.
#       Default: $NUM_KEYS / $NUM_THREADS
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
#   KEY_SIZE:  The size of each key in bytes in db_bench.  Default: 100.
#   VALUE_SIZE:  The size of each value in bytes in db_bench.  Default: 900.
#   CACHE_SIZE:  The size of RocksDB block cache used in db_bench.  Default: 1G
#   STATISTICS:  If 1, then statistics is on in db_bench.  Default: 0.
#   COMPRESSION_RATIO:  The compression ratio of the key generated in db_bench.
#       Default: 0.5.
#   HISTOGRAM:  If 1, then the histogram feature on performance feature is on.
#   STATS_PER_INTERVAL:  If 1, then the statistics will be reported for every
#       STATS_INTERVAL_SECONDS seconds.  Default 1.
#   STATS_INTERVAL_SECONDS:  If STATS_PER_INTERVAL is set to 1, then statistics
#       will be reported for every STATS_INTERVAL_SECONDS.  Default 60.
#   MAX_BACKGROUND_FLUSHES:  The maxinum number of concurrent flushes in
#       db_bench.  Default: 4.
#   MAX_BACKGROUND_COMPACTIONS:  The maximum number of concurrent compactions
#       in db_bench.  Default: 16.
#   SEEK_NEXTS:  Controls how many Next() will be called after seek.
#       Default: 10.
#   SEED:  random seed that controls the randomness of the benchmark.
#       Default: $( date +%s )
92

93 94 95 96 97 98
#==============================================================================
#  CONSTANT
#==============================================================================
TITLE_FORMAT="%40s,%25s,%30s,%7s,%9s,%8s,"
TITLE_FORMAT+="%10s,%13s,%14s,%11s,%12s,"
TITLE_FORMAT+="%7s,%11s,"
99
TITLE_FORMAT+="%9s,%10s,%10s,%10s,%10s,%10s,%5s"
100 101 102 103 104
TITLE_FORMAT+="\n"

DATA_FORMAT="%40s,%25s,%30s,%7s,%9s,%8s,"
DATA_FORMAT+="%10s,%13.0f,%14s,%11s,%12s,"
DATA_FORMAT+="%7s,%11s,"
105
DATA_FORMAT+="%9.0f,%10.0f,%10.0f,%10.0f,%10.0f,%10.0f,%5.0f"
106 107 108 109 110 111 112
DATA_FORMAT+="\n"

MAIN_PATTERN="$1""[[:blank:]]+:.*[[:blank:]]+([0-9\.]+)[[:blank:]]+ops/sec"
PERC_PATTERN="Percentiles: P50: ([0-9\.]+) P75: ([0-9\.]+) "
PERC_PATTERN+="P99: ([0-9\.]+) P99.9: ([0-9\.]+) P99.99: ([0-9\.]+)"
#==============================================================================

113 114
function main {
  commit=${1:-"origin/master"}
115 116
  test_root_dir=${TEST_PATH:-"/tmp/rocksdb/regression_test"}
  init_arguments $test_root_dir
117

118 119
  if [ $DEBUG -eq 0 ]; then
      checkout_rocksdb $commit
A
Aaron Gao 已提交
120 121 122
      build_db_bench_and_ldb
  elif [[ ! -f db_bench ]] || [[ ! -f ldb ]]; then
      build_db_bench_and_ldb
123
  fi
124

125
  setup_test_directory
A
Aaron Gao 已提交
126 127 128
  if [ $TEST_MODE -le 1 ]; then
      tmp=$DB_PATH
      DB_PATH=$ORIGIN_PATH
129
      test_remote "test -d $DB_PATH"
A
Aaron Gao 已提交
130 131
      if [[ $? -ne 0 ]] || [[ $(run_remote 'date +%u') -eq 7 &&
      $(run_remote 'echo $(( $(date +"%s") - $(stat -c "%Y" '"$DB_PATH"') ))') -gt "86400" ]]; then
132
          run_remote "rm -rf $DB_PATH"
A
Aaron Gao 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145
          echo "Building DB..."
          run_db_bench "fillseqdeterministic" $NUM_KEYS 1 0
      fi
      DB_PATH=$tmp
  fi
  if [ $TEST_MODE -ge 1 ]; then
      build_checkpoint
      run_db_bench "readrandom"
      run_db_bench "readwhilewriting"
      run_db_bench "deleterandom" $((NUM_KEYS / 10 / $NUM_THREADS))
      run_db_bench "seekrandom"
      run_db_bench "seekrandomwhilewriting"
  fi
146

Y
Yueh-Hsuan Chiang 已提交
147
  cleanup_test_directory $test_root_dir
148 149 150 151 152 153 154 155 156 157 158
  echo ""
  echo "Benchmark completed!  Results are available in $RESULT_PATH"
}

############################################################################
function init_arguments {
  K=1024
  M=$((1024 * K))
  G=$((1024 * M))

  current_time=$(date +"%F-%H:%M:%S")
159
  RESULT_PATH=${RESULT_PATH:-"$1/results/$current_time"}
160 161 162
  COMMIT_ID=`git log | head -n1 | cut -c 8-`
  SUMMARY_FILE="$RESULT_PATH/SUMMARY.csv"

163
  DB_PATH=${3:-"$1/db"}
A
Aaron Gao 已提交
164
  ORIGIN_PATH=${ORIGIN_PATH:-"$(dirname $(dirname $DB_PATH))/db"}
165
  WAL_PATH=${4:-""}
166
  if [ -z "$REMOTE_USER_AT_HOST" ]; then
167 168 169 170 171
    DB_BENCH_DIR=${5:-"."}
  else
    DB_BENCH_DIR=${5:-"$1/db_bench"}
  fi

172
  DEBUG=${DEBUG:-0}
A
Aaron Gao 已提交
173
  TEST_MODE=${TEST_MODE:-1}
174 175
  SCP=${SCP:-"scp"}
  SSH=${SSH:-"ssh"}
176
  NUM_THREADS=${NUM_THREADS:-16}
Y
Yueh-Hsuan Chiang 已提交
177 178
  NUM_KEYS=${NUM_KEYS:-$((1 * G))}  # key range
  NUM_OPS=${NUM_OPS:-$(($NUM_KEYS / $NUM_THREADS))}
179 180 181 182 183 184
  KEY_SIZE=${KEY_SIZE:-100}
  VALUE_SIZE=${VALUE_SIZE:-900}
  CACHE_SIZE=${CACHE_SIZE:-$((1 * G))}
  STATISTICS=${STATISTICS:-0}
  COMPRESSION_RATIO=${COMPRESSION_RATIO:-0.5}
  HISTOGRAM=${HISTOGRAM:-1}
185
  NUM_MULTI_DB=${NUM_MULTI_DB:-1}
186
  STATS_PER_INTERVAL=${STATS_PER_INTERVAL:-1}
Y
Yueh-Hsuan Chiang 已提交
187
  STATS_INTERVAL_SECONDS=${STATS_INTERVAL_SECONDS:-600}
188
  MAX_BACKGROUND_FLUSHES=${MAX_BACKGROUND_FLUSHES:-4}
189
  MAX_BACKGROUND_COMPACTIONS=${MAX_BACKGROUND_COMPACTIONS:-16}
Y
Yueh-Hsuan Chiang 已提交
190
  DELETE_TEST_PATH=${DELETE_TEST_PATH:-0}
191 192 193 194 195
  SEEK_NEXTS=${SEEK_NEXTS:-10}
  SEED=${SEED:-$( date +%s )}
}

# $1 --- benchmark name
Y
Yueh-Hsuan Chiang 已提交
196 197 198
# $2 --- number of operations.  Default: $NUM_KEYS
# $3 --- number of threads.  Default $NUM_THREADS
# $4 --- use_existing_db.  Default: 1
199
function run_db_bench {
200 201 202
  # this will terminate all currently-running db_bench
  find_db_bench_cmd="ps aux | grep db_bench | grep -v grep | grep -v aux | awk '{print \$2}'"

Y
Yueh-Hsuan Chiang 已提交
203 204 205
  USE_EXISTING_DB=${4:-1}
  ops=${2:-$NUM_OPS}
  threads=${3:-$NUM_THREADS}
206 207 208 209 210 211
  echo ""
  echo "======================================================================="
  echo "Benchmark $1"
  echo "======================================================================="
  echo ""
  db_bench_error=0
212
  options_file_arg=$(setup_options_file)
Y
Yueh-Hsuan Chiang 已提交
213
  echo "$options_file_arg"
214
  db_bench_cmd="$DB_BENCH_DIR/db_bench \
215
      --benchmarks=$1 --db=$DB_PATH --wal_dir=$WAL_PATH \
216
      --use_existing_db=$USE_EXISTING_DB \
217
      --disable_auto_compactions \
Y
Yueh-Hsuan Chiang 已提交
218
      --threads=$threads \
219
      --num=$NUM_KEYS \
Y
Yueh-Hsuan Chiang 已提交
220 221 222
      --reads=$ops \
      --writes=$ops \
      --deletes=$ops \
223 224 225 226
      --key_size=$KEY_SIZE \
      --value_size=$VALUE_SIZE \
      --cache_size=$CACHE_SIZE \
      --statistics=$STATISTICS \
227
      $options_file_arg \
228 229 230 231 232 233
      --compression_ratio=$COMPRESSION_RATIO \
      --histogram=$HISTOGRAM \
      --seek_nexts=$SEEK_NEXTS \
      --stats_per_interval=$STATS_PER_INTERVAL \
      --stats_interval_seconds=$STATS_INTERVAL_SECONDS \
      --max_background_flushes=$MAX_BACKGROUND_FLUSHES \
234
      --num_multi_db=$NUM_MULTI_DB \
235
      --max_background_compactions=$MAX_BACKGROUND_COMPACTIONS \
236
      --seed=$SEED 2>&1"
237
  ps_cmd="ps aux"
238 239
  if ! [ -z "$REMOTE_USER_AT_HOST" ]; then
    echo "Running benchmark remotely on $REMOTE_USER_AT_HOST"
240
    db_bench_cmd="$SSH $REMOTE_USER_AT_HOST $db_bench_cmd"
241
    ps_cmd="$SSH $REMOTE_USER_AT_HOST $ps_cmd"
242 243 244 245 246 247 248 249 250 251 252
  fi

  ## make sure no db_bench is running
  # The following statement is necessary make sure "eval $ps_cmd" will success.
  # Otherwise, if we simply check whether "$(eval $ps_cmd | grep db_bench)" is
  # successful or not, then it will always be false since grep will return
  # non-zero status when there's no matching output.
  ps_output="$(eval $ps_cmd)"
  exit_on_error $? "$ps_cmd"

  # perform the actual command to check whether db_bench is running
253
  grep_output="$(eval $ps_cmd | grep db_bench | grep -v grep)"
254 255 256
  if [ "$grep_output" != "" ]; then
    echo "Stopped regression_test.sh as there're still db_bench processes running:"
    echo $grep_output
257
    exit 2
258 259 260 261 262
  fi

  ## run the db_bench
  cmd="($db_bench_cmd || db_bench_error=1) | tee -a $RESULT_PATH/$1"
  exit_on_error $?
263 264 265 266
  echo $cmd
  eval $cmd
  exit_on_error $db_bench_error

Y
Yueh-Hsuan Chiang 已提交
267 268 269
  update_report "$1" "$RESULT_PATH/$1" $ops $threads
}

A
Aaron Gao 已提交
270 271 272 273 274 275 276 277 278 279 280 281 282 283
function build_checkpoint {
    cmd_prefix=""
    if ! [ -z "$REMOTE_USER_AT_HOST" ]; then
        cmd_prefix="$SSH $REMOTE_USER_AT_HOST "
    fi
    dirs=$($cmd_prefix find $ORIGIN_PATH -type d -links 2)
    for dir in $dirs; do
        db_index=$(basename $dir)
        echo "Building checkpoint: $ORIGIN_PATH/$db_index -> $DB_PATH/$db_index ..."
        $cmd_prefix $DB_BENCH_DIR/ldb checkpoint --checkpoint_dir=$DB_PATH/$db_index \
                      --db=$ORIGIN_PATH/$db_index 2>&1
    done
}

Y
Yueh-Hsuan Chiang 已提交
284 285
function multiply {
  echo "$1 * $2" | bc
286 287 288 289 290 291
}

# $1 --- name of the benchmark
# $2 --- the filename of the output log of db_bench
function update_report {
  main_result=`cat $2 | grep $1`
292
  exit_on_error $?
293
  perc_statement=`cat $2 | grep Percentile`
294
  exit_on_error $?
295 296

  # Obtain micros / op
297

298 299
  [[ $main_result =~ $MAIN_PATTERN ]]
  ops_per_s=${BASH_REMATCH[1]}
300

301 302
  # Obtain percentile information
  [[ $perc_statement =~ $PERC_PATTERN ]]
303 304 305 306 307 308
  perc[0]=${BASH_REMATCH[1]}  # p50
  perc[1]=${BASH_REMATCH[2]}  # p75
  perc[2]=${BASH_REMATCH[3]}  # p99
  perc[3]=${BASH_REMATCH[4]}  # p99.9
  perc[4]=${BASH_REMATCH[5]}  # p99.99

309 310
  (printf "$DATA_FORMAT" \
    $COMMIT_ID $1 $REMOTE_USER_AT_HOST $NUM_MULTI_DB $NUM_KEYS $KEY_SIZE $VALUE_SIZE \
Y
Yueh-Hsuan Chiang 已提交
311 312 313
       $(multiply $COMPRESSION_RATIO 100) \
       $3 $4 $CACHE_SIZE \
       $MAX_BACKGROUND_FLUSHES $MAX_BACKGROUND_COMPACTIONS \
314
       $ops_per_s \
Y
Yueh-Hsuan Chiang 已提交
315 316 317 318 319
       $(multiply ${perc[0]} 1000) \
       $(multiply ${perc[1]} 1000) \
       $(multiply ${perc[2]} 1000) \
       $(multiply ${perc[3]} 1000) \
       $(multiply ${perc[4]} 1000) \
320
       $DEBUG \
Y
Yueh-Hsuan Chiang 已提交
321
       >> $SUMMARY_FILE)
322 323 324 325 326 327
  exit_on_error $?
}

function exit_on_error {
  if [ $1 -ne 0 ]; then
    echo ""
328 329 330 331 332
    echo "ERROR: Benchmark did not complete successfully."
    if ! [ -z "$2" ]; then
      echo "Failure command: $2"
    fi
    echo "Partial results are output to $RESULT_PATH"
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    echo "ERROR" >> $SUMMARY_FILE
    exit $1
  fi
}

function checkout_rocksdb {
  echo "Checking out commit $1 ..."

  git fetch --all
  exit_on_error $?

  git checkout $1
  exit_on_error $?
}

A
Aaron Gao 已提交
348 349
function build_db_bench_and_ldb {
  echo "Building db_bench & ldb ..."
350 351 352 353

  make clean
  exit_on_error $?

A
Aaron Gao 已提交
354
  DEBUG_LEVEL=0 make db_bench ldb -j32
355 356 357
  exit_on_error $?
}

358
function run_remote {
359 360 361 362 363
  test_remote "$1"
  exit_on_error $? "$1"
}

function test_remote {
364
  if ! [ -z "$REMOTE_USER_AT_HOST" ]; then
A
Aaron Gao 已提交
365
      cmd="$SSH $REMOTE_USER_AT_HOST '$1'"
366
  else
367
      cmd="$1"
368
  fi
369
  eval "$cmd"
370
}
371

372
function run_local {
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
  eval "$1"
  exit_on_error $?
}

function setup_options_file {
  if ! [ -z "$OPTIONS_FILE" ]; then
    if ! [ -z "$REMOTE_USER_AT_HOST" ]; then
      options_file="$DB_BENCH_DIR/OPTIONS_FILE"
      run_local "$SCP $OPTIONS_FILE $REMOTE_USER_AT_HOST:$options_file"
    else
      options_file="$OPTIONS_FILE"
    fi
    echo "--options_file=$options_file"
  fi
  echo ""
388
}
389

390 391
function setup_test_directory {
  echo "Deleting old regression test directories and creating new ones"
392

393
  run_remote "rm -rf $DB_PATH"
394 395 396 397 398 399
  run_remote "rm -rf $DB_BENCH_DIR"
  run_local "rm -rf $RESULT_PATH"

  if ! [ -z "$WAL_PATH" ]; then
    run_remote "rm -rf $WAL_PATH"
    run_remote "mkdir -p $WAL_PATH"
400
  fi
401

402
  run_remote "mkdir -p $DB_PATH"
403 404 405 406

  run_remote "mkdir -p $DB_BENCH_DIR"
  run_remote "ls -l $DB_BENCH_DIR"

407
  if ! [ -z "$REMOTE_USER_AT_HOST" ]; then
A
Aaron Gao 已提交
408 409
      run_local "$SCP ./db_bench $REMOTE_USER_AT_HOST:$DB_BENCH_DIR/db_bench"
      run_local "$SCP ./ldb $REMOTE_USER_AT_HOST:$DB_BENCH_DIR/ldb"
410
  fi
411

412
  run_local "mkdir -p $RESULT_PATH"
413

414 415 416
  (printf $TITLE_FORMAT \
      "commit id" "benchmark" "user@host" "num-dbs" "key-range" "key-size" \
      "value-size" "compress-rate" "ops-per-thread" "num-threads" "cache-size" \
Y
Yueh-Hsuan Chiang 已提交
417
      "flushes" "compactions" \
418
      "ops-per-s" "p50" "p75" "p99" "p99.9" "p99.99" "debug" \
Y
Yueh-Hsuan Chiang 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432
      >> $SUMMARY_FILE)
  exit_on_error $?
}

function cleanup_test_directory {

  if [ $DELETE_TEST_PATH -ne 0 ]; then
    echo "Clear old regression test directories and creating new ones"
    run_remote "rm -rf $DB_PATH"
    run_remote "rm -rf $WAL_PATH"
    if ! [ -z "$REMOTE_USER_AT_HOST" ]; then
      run_remote "rm -rf $DB_BENCH_DIR"
    fi
    run_remote "rm -rf $1"
433 434 435 436
  else
    echo "------------ DEBUG MODE ------------"
    echo "DB  PATH: $DB_PATH"
    echo "WAL PATH: $WAL_PATH"
Y
Yueh-Hsuan Chiang 已提交
437
  fi
438 439 440 441 442
}

############################################################################

main $@