5e0ce87a3f
FossilOrigin-Name: 25f85f68723e56c18e44b094d85f67b99912dc86
259 lines
6.5 KiB
Plaintext
259 lines
6.5 KiB
Plaintext
# 2010 April 13
|
|
#
|
|
# The author disclaims copyright to this source code. In place of
|
|
# a legal notice, here is a blessing:
|
|
#
|
|
# May you do good and not evil.
|
|
# May you find forgiveness for yourself and forgive others.
|
|
# May you share freely, never taking more than you give.
|
|
#
|
|
#***********************************************************************
|
|
# This file implements regression tests for SQLite library. The
|
|
# focus of this file is testing the operation of the library in
|
|
# "PRAGMA journal_mode=WAL" mode with multiple threads.
|
|
#
|
|
|
|
set testdir [file dirname $argv0]
|
|
|
|
source $testdir/tester.tcl
|
|
source $testdir/lock_common.tcl
|
|
if {[run_thread_tests]==0} { finish_test ; return }
|
|
|
|
set sqlite_walsummary_mmap_incr 64
|
|
|
|
# The number of threads to start. And the amount of time to run the test
|
|
# for. Respectively.
|
|
#
|
|
set NTHREAD 10
|
|
set SECONDS 5
|
|
|
|
# The parameter is the name of a variable in the callers context. The
|
|
# variable may or may not exist when this command is invoked.
|
|
#
|
|
# If the variable does exist, its value is returned. Otherwise, this
|
|
# command uses [vwait] to wait until it is set, then returns the value.
|
|
# In other words, this is a version of the [set VARNAME] command that
|
|
# blocks until a variable exists.
|
|
#
|
|
proc wait_for_var {varname} {
|
|
if {0==[uplevel [list info exists $varname]]} {
|
|
uplevel [list vwait $varname]
|
|
}
|
|
uplevel [list set $varname]
|
|
}
|
|
|
|
proc lshift {lvar} {
|
|
upvar $lvar L
|
|
set ret [lindex $L 0]
|
|
set L [lrange $L 1 end]
|
|
return $ret
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
# do_thread_test TESTNAME OPTIONS...
|
|
#
|
|
# where OPTIONS are:
|
|
#
|
|
# -seconds SECONDS How many seconds to run the test for
|
|
# -init SCRIPT Script to run before test.
|
|
# -thread NAME COUNT SCRIPT Scripts to run in threads (or processes).
|
|
# -processes BOOLEAN True to use processes instead of threads.
|
|
#
|
|
proc do_thread_test {args} {
|
|
set A $args
|
|
|
|
set P(testname) [lshift A]
|
|
set P(seconds) 5
|
|
set P(init) ""
|
|
set P(threads) [list]
|
|
set P(processes) 0
|
|
|
|
unset -nocomplain ::done
|
|
|
|
while {[llength $A]>0} {
|
|
set a [lshift A]
|
|
switch -glob -- $a {
|
|
-seconds {
|
|
set P(seconds) [lshift A]
|
|
}
|
|
|
|
-init {
|
|
set P(init) [lshift A]
|
|
}
|
|
|
|
-processes {
|
|
set P(processes) [lshift A]
|
|
}
|
|
|
|
-thread {
|
|
set name [lshift A]
|
|
set count [lshift A]
|
|
set prg [lshift A]
|
|
lappend P(threads) [list $name $count $prg]
|
|
}
|
|
|
|
default {
|
|
error "Unknown option: $a"
|
|
}
|
|
}
|
|
}
|
|
|
|
puts "Running $P(testname) for $P(seconds) seconds..."
|
|
|
|
catch { db close }
|
|
file delete -force test.db test.db-journal test.db-wal
|
|
|
|
sqlite3 db test.db
|
|
eval $P(init)
|
|
db close
|
|
|
|
foreach T $P(threads) {
|
|
set name [lindex $T 0]
|
|
set count [lindex $T 1]
|
|
set prg [lindex $T 2]
|
|
|
|
for {set i 1} {$i <= $count} {incr i} {
|
|
set program [string map [list %TEST% $prg %SECONDS% $P(seconds) %I% $i] {
|
|
|
|
set tid %I%
|
|
|
|
proc usleep {ms} {
|
|
set ::usleep 0
|
|
after $ms {set ::usleep 1}
|
|
vwait ::usleep
|
|
}
|
|
proc busyhandler {n} { usleep 10 ; return 0 }
|
|
|
|
sqlite3 db test.db
|
|
db busy busyhandler
|
|
db eval { SELECT randomblob($tid*5) }
|
|
|
|
set ::finished 0
|
|
after [expr %SECONDS% * 1000] {set ::finished 1}
|
|
proc tt_continue {} { expr ($::finished==0) }
|
|
|
|
set rc [catch { %TEST% } msg]
|
|
|
|
db close
|
|
list $rc $msg
|
|
}]
|
|
|
|
if {$P(processes)==0} {
|
|
sqlthread spawn ::done($name,$i) $program
|
|
} else {
|
|
testfixture_nb ::done($name,$i) $program
|
|
}
|
|
}
|
|
}
|
|
|
|
set report " Results:"
|
|
foreach T $P(threads) {
|
|
set name [lindex $T 0]
|
|
set count [lindex $T 1]
|
|
set prg [lindex $T 2]
|
|
|
|
set reslist [list]
|
|
for {set i 1} {$i <= $count} {incr i} {
|
|
set res [wait_for_var ::done($name,$i)]
|
|
lappend reslist [lindex $res 1]
|
|
do_test $P(testname).$name.$i [list lindex $res 0] 0
|
|
}
|
|
|
|
append report " $name $reslist"
|
|
}
|
|
puts $report
|
|
}
|
|
|
|
#--------------------------------------------------------------------------
|
|
# Start NTHREAD threads. Each thread performs both read and write
|
|
# transactions. Each read transaction consists of:
|
|
#
|
|
# 1) Reading the md5sum of all but the last table row,
|
|
# 2) Running integrity check.
|
|
# 3) Reading the value stored in the last table row,
|
|
# 4) Check that the values read in steps 1 and 3 are the same, and that
|
|
# the md5sum of all but the last table row has not changed.
|
|
#
|
|
# Each write transaction consists of:
|
|
#
|
|
# 1) Modifying the contents of t1 (inserting, updating, deleting rows).
|
|
# 2) Appending a new row to the table containing the md5sum() of all
|
|
# rows in the table.
|
|
#
|
|
# Each of the N threads runs N read transactions followed by a single write
|
|
# transaction in a loop as fast as possible.
|
|
#
|
|
# Ther is also a single checkpointer thread. It runs the following loop:
|
|
#
|
|
# 1) Execute "PRAGMA checkpoint"
|
|
# 2) Sleep for 500 ms.
|
|
#
|
|
|
|
foreach {mode name} {
|
|
0 walthread-1-threads
|
|
1 walthread-1-processes
|
|
} {
|
|
do_thread_test $name -processes $mode -seconds $SECONDS -init {
|
|
execsql {
|
|
PRAGMA journal_mode = WAL;
|
|
CREATE TABLE t1(x PRIMARY KEY);
|
|
PRAGMA lock_status;
|
|
INSERT INTO t1 VALUES(randomblob(100));
|
|
INSERT INTO t1 VALUES(randomblob(100));
|
|
INSERT INTO t1 SELECT md5sum(x) FROM t1;
|
|
}
|
|
} -thread main $NTHREAD {
|
|
|
|
proc read_transaction {} {
|
|
set results [db eval {
|
|
BEGIN;
|
|
PRAGMA integrity_check;
|
|
SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1);
|
|
SELECT x FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1);
|
|
SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1);
|
|
COMMIT;
|
|
}]
|
|
|
|
if {[llength $results]!=4
|
|
|| [lindex $results 0] != "ok"
|
|
|| [lindex $results 1] != [lindex $results 2]
|
|
|| [lindex $results 2] != [lindex $results 3]
|
|
} {
|
|
error "Failed read transaction: $results"
|
|
}
|
|
}
|
|
|
|
proc write_transaction {} {
|
|
db eval {
|
|
BEGIN;
|
|
INSERT INTO t1 VALUES(randomblob(100));
|
|
INSERT INTO t1 VALUES(randomblob(100));
|
|
INSERT INTO t1 SELECT md5sum(x) FROM t1;
|
|
COMMIT;
|
|
}
|
|
}
|
|
|
|
set nRun 0
|
|
while {[tt_continue]} {
|
|
read_transaction
|
|
write_transaction
|
|
usleep 1
|
|
incr nRun
|
|
}
|
|
set nRun
|
|
|
|
} -thread ckpt 1 {
|
|
set nRun 0
|
|
while {[tt_continue]} {
|
|
db eval "PRAGMA checkpoint"
|
|
usleep 500
|
|
incr nRun
|
|
}
|
|
set nRun
|
|
}
|
|
}
|
|
|
|
finish_test
|
|
|