1d3f8f946Sdan# 2010 April 13 27c24610eSdan# 37c24610eSdan# The author disclaims copyright to this source code. In place of 47c24610eSdan# a legal notice, here is a blessing: 57c24610eSdan# 67c24610eSdan# May you do good and not evil. 77c24610eSdan# May you find forgiveness for yourself and forgive others. 87c24610eSdan# May you share freely, never taking more than you give. 97c24610eSdan# 107c24610eSdan#*********************************************************************** 11d3f8f946Sdan# This file implements regression tests for SQLite library. The 12d3f8f946Sdan# focus of this file is testing the operation of the library in 13cd11fb28Sdan# "PRAGMA journal_mode=WAL" mode with multiple threads. 147c24610eSdan# 157c24610eSdan 167c24610eSdanset testdir [file dirname $argv0] 177c24610eSdan 187c24610eSdansource $testdir/tester.tcl 195e0ce87aSdansource $testdir/lock_common.tcl 207c24610eSdanif {[run_thread_tests]==0} { finish_test ; return } 215cf53537Sdanifcapable !wal { finish_test ; return } 227c24610eSdan 23cd11fb28Sdanset sqlite_walsummary_mmap_incr 64 24cd11fb28Sdan 25b4e3a6f7Sdan# How long, in seconds, to run each test for. If a test is set to run for 26b4e3a6f7Sdan# 0 seconds, it is omitted entirely. 27cd11fb28Sdan# 28529b1870Sdrhunset -nocomplain seconds 2931c03907Sdanset seconds(walthread-1) 20 3031c03907Sdanset seconds(walthread-2) 20 31b4e3a6f7Sdanset seconds(walthread-3) 20 3231c03907Sdanset seconds(walthread-4) 20 33bb16ff75Sdanset seconds(walthread-5) 1 345e0ce87aSdan 355e0ce87aSdan# The parameter is the name of a variable in the callers context. The 365e0ce87aSdan# variable may or may not exist when this command is invoked. 375e0ce87aSdan# 385e0ce87aSdan# If the variable does exist, its value is returned. Otherwise, this 395e0ce87aSdan# command uses [vwait] to wait until it is set, then returns the value. 405e0ce87aSdan# In other words, this is a version of the [set VARNAME] command that 415e0ce87aSdan# blocks until a variable exists. 425e0ce87aSdan# 435e0ce87aSdanproc wait_for_var {varname} { 445e0ce87aSdan if {0==[uplevel [list info exists $varname]]} { 455e0ce87aSdan uplevel [list vwait $varname] 467c24610eSdan } 475e0ce87aSdan uplevel [list set $varname] 487c24610eSdan} 495e0ce87aSdan 50b4e3a6f7Sdan# The argument is the name of a list variable in the callers context. The 51b4e3a6f7Sdan# first element of the list is removed and returned. For example: 52b4e3a6f7Sdan# 53b4e3a6f7Sdan# set L {a b c} 54b4e3a6f7Sdan# set x [lshift L] 55b4e3a6f7Sdan# assert { $x == "a" && $L == "b c" } 56b4e3a6f7Sdan# 575e0ce87aSdanproc lshift {lvar} { 585e0ce87aSdan upvar $lvar L 595e0ce87aSdan set ret [lindex $L 0] 605e0ce87aSdan set L [lrange $L 1 end] 615e0ce87aSdan return $ret 625e0ce87aSdan} 635e0ce87aSdan 645e0ce87aSdan 655e0ce87aSdan#------------------------------------------------------------------------- 665e0ce87aSdan# do_thread_test TESTNAME OPTIONS... 675e0ce87aSdan# 685e0ce87aSdan# where OPTIONS are: 695e0ce87aSdan# 705e0ce87aSdan# -seconds SECONDS How many seconds to run the test for 715e0ce87aSdan# -init SCRIPT Script to run before test. 725e0ce87aSdan# -thread NAME COUNT SCRIPT Scripts to run in threads (or processes). 735e0ce87aSdan# -processes BOOLEAN True to use processes instead of threads. 74b4e3a6f7Sdan# -check SCRIPT Script to run after test. 755e0ce87aSdan# 765e0ce87aSdanproc do_thread_test {args} { 77e45d4426Sdan 785e0ce87aSdan set A $args 795e0ce87aSdan 805e0ce87aSdan set P(testname) [lshift A] 815e0ce87aSdan set P(seconds) 5 825e0ce87aSdan set P(init) "" 835e0ce87aSdan set P(threads) [list] 845e0ce87aSdan set P(processes) 0 85b4e3a6f7Sdan set P(check) { 86b4e3a6f7Sdan set ic [db eval "PRAGMA integrity_check"] 87b4e3a6f7Sdan if {$ic != "ok"} { error $ic } 88b4e3a6f7Sdan } 895e0ce87aSdan 905e0ce87aSdan unset -nocomplain ::done 915e0ce87aSdan 925e0ce87aSdan while {[llength $A]>0} { 935e0ce87aSdan set a [lshift A] 945e0ce87aSdan switch -glob -- $a { 955e0ce87aSdan -seconds { 965e0ce87aSdan set P(seconds) [lshift A] 975e0ce87aSdan } 985e0ce87aSdan 995e0ce87aSdan -init { 1005e0ce87aSdan set P(init) [lshift A] 1015e0ce87aSdan } 1025e0ce87aSdan 1035e0ce87aSdan -processes { 1045e0ce87aSdan set P(processes) [lshift A] 1055e0ce87aSdan } 1065e0ce87aSdan 107b4e3a6f7Sdan -check { 108b4e3a6f7Sdan set P(check) [lshift A] 109b4e3a6f7Sdan } 110b4e3a6f7Sdan 1115e0ce87aSdan -thread { 1125e0ce87aSdan set name [lshift A] 1135e0ce87aSdan set count [lshift A] 1145e0ce87aSdan set prg [lshift A] 1155e0ce87aSdan lappend P(threads) [list $name $count $prg] 1165e0ce87aSdan } 1175e0ce87aSdan 1185e0ce87aSdan default { 1195e0ce87aSdan error "Unknown option: $a" 1205e0ce87aSdan } 1215e0ce87aSdan } 1225e0ce87aSdan } 1235e0ce87aSdan 124b4e3a6f7Sdan if {$P(seconds) == 0} { 125b4e3a6f7Sdan puts "Skipping $P(testname)" 126b4e3a6f7Sdan return 127b4e3a6f7Sdan } 128b4e3a6f7Sdan 1295e0ce87aSdan puts "Running $P(testname) for $P(seconds) seconds..." 1305e0ce87aSdan 1315e0ce87aSdan catch { db close } 132fda06befSmistachkin forcedelete test.db test.db-journal test.db-wal 1335e0ce87aSdan 1345e0ce87aSdan sqlite3 db test.db 1355e0ce87aSdan eval $P(init) 136bb16ff75Sdan catch { db close } 1375e0ce87aSdan 1385e0ce87aSdan foreach T $P(threads) { 1395e0ce87aSdan set name [lindex $T 0] 1405e0ce87aSdan set count [lindex $T 1] 1415e0ce87aSdan set prg [lindex $T 2] 1425e0ce87aSdan 1435e0ce87aSdan for {set i 1} {$i <= $count} {incr i} { 144b4e3a6f7Sdan set vars " 145b4e3a6f7Sdan set E(pid) $i 146b4e3a6f7Sdan set E(nthread) $count 147b4e3a6f7Sdan set E(seconds) $P(seconds) 148b4e3a6f7Sdan " 149b4e3a6f7Sdan set program [string map [list %TEST% $prg %VARS% $vars] { 1505e0ce87aSdan 151b4e3a6f7Sdan %VARS% 1525e0ce87aSdan 1535e0ce87aSdan proc usleep {ms} { 1545e0ce87aSdan set ::usleep 0 1555e0ce87aSdan after $ms {set ::usleep 1} 1565e0ce87aSdan vwait ::usleep 1575e0ce87aSdan } 158b4e3a6f7Sdan 159b4e3a6f7Sdan proc integrity_check {{db db}} { 160b4e3a6f7Sdan set ic [$db eval {PRAGMA integrity_check}] 161b4e3a6f7Sdan if {$ic != "ok"} {error $ic} 162b4e3a6f7Sdan } 163b4e3a6f7Sdan 1645e0ce87aSdan proc busyhandler {n} { usleep 10 ; return 0 } 1655e0ce87aSdan 1665e0ce87aSdan sqlite3 db test.db 1675e0ce87aSdan db busy busyhandler 168b4e3a6f7Sdan db eval { SELECT randomblob($E(pid)*5) } 1695e0ce87aSdan 1705e0ce87aSdan set ::finished 0 171b4e3a6f7Sdan after [expr $E(seconds) * 1000] {set ::finished 1} 172e45d4426Sdan proc tt_continue {} { update ; expr ($::finished==0) } 1735e0ce87aSdan 1745e0ce87aSdan set rc [catch { %TEST% } msg] 1755e0ce87aSdan 176e45d4426Sdan catch { db close } 1775e0ce87aSdan list $rc $msg 1785e0ce87aSdan }] 1795e0ce87aSdan 1805e0ce87aSdan if {$P(processes)==0} { 1815e0ce87aSdan sqlthread spawn ::done($name,$i) $program 1825e0ce87aSdan } else { 1835e0ce87aSdan testfixture_nb ::done($name,$i) $program 1845e0ce87aSdan } 1855e0ce87aSdan } 1865e0ce87aSdan } 1875e0ce87aSdan 1885e0ce87aSdan set report " Results:" 1895e0ce87aSdan foreach T $P(threads) { 1905e0ce87aSdan set name [lindex $T 0] 1915e0ce87aSdan set count [lindex $T 1] 1925e0ce87aSdan set prg [lindex $T 2] 1935e0ce87aSdan 1945e0ce87aSdan set reslist [list] 1955e0ce87aSdan for {set i 1} {$i <= $count} {incr i} { 1965e0ce87aSdan set res [wait_for_var ::done($name,$i)] 1975e0ce87aSdan lappend reslist [lindex $res 1] 1985e0ce87aSdan do_test $P(testname).$name.$i [list lindex $res 0] 0 1995e0ce87aSdan } 2005e0ce87aSdan 2015e0ce87aSdan append report " $name $reslist" 2025e0ce87aSdan } 2035e0ce87aSdan puts $report 204b4e3a6f7Sdan 205b4e3a6f7Sdan sqlite3 db test.db 206b4e3a6f7Sdan set res "" 207b4e3a6f7Sdan if {[catch $P(check) msg]} { set res $msg } 208b4e3a6f7Sdan do_test $P(testname).check [list set {} $res] "" 2095e0ce87aSdan} 2107c24610eSdan 211b4e3a6f7Sdan# A wrapper around [do_thread_test] which runs the specified test twice. 212b4e3a6f7Sdan# Once using processes, once using threads. This command takes the same 213b4e3a6f7Sdan# arguments as [do_thread_test], except specifying the -processes switch 214b4e3a6f7Sdan# is illegal. 215b4e3a6f7Sdan# 216b4e3a6f7Sdanproc do_thread_test2 {args} { 217b4e3a6f7Sdan set name [lindex $args 0] 218b4e3a6f7Sdan if {[lsearch $args -processes]>=0} { error "bad option: -processes"} 219b4e3a6f7Sdan uplevel [lreplace $args 0 0 do_thread_test "$name-threads" -processes 0] 220b4e3a6f7Sdan uplevel [lreplace $args 0 0 do_thread_test "$name-processes" -processes 1] 221b4e3a6f7Sdan} 222b4e3a6f7Sdan 2237c24610eSdan#-------------------------------------------------------------------------- 224b4e3a6f7Sdan# Start 10 threads. Each thread performs both read and write 2255e0ce87aSdan# transactions. Each read transaction consists of: 2267c24610eSdan# 2277c24610eSdan# 1) Reading the md5sum of all but the last table row, 2287c24610eSdan# 2) Running integrity check. 2297c24610eSdan# 3) Reading the value stored in the last table row, 2307c24610eSdan# 4) Check that the values read in steps 1 and 3 are the same, and that 2317c24610eSdan# the md5sum of all but the last table row has not changed. 2327c24610eSdan# 2337c24610eSdan# Each write transaction consists of: 2347c24610eSdan# 2357c24610eSdan# 1) Modifying the contents of t1 (inserting, updating, deleting rows). 2367c24610eSdan# 2) Appending a new row to the table containing the md5sum() of all 2377c24610eSdan# rows in the table. 2387c24610eSdan# 2397c24610eSdan# Each of the N threads runs N read transactions followed by a single write 2407c24610eSdan# transaction in a loop as fast as possible. 2417c24610eSdan# 242e45d4426Sdan# There is also a single checkpointer thread. It runs the following loop: 2437c24610eSdan# 2445a299f91Sdan# 1) Execute "PRAGMA wal_checkpoint" 2457c24610eSdan# 2) Sleep for 500 ms. 2467c24610eSdan# 247b4e3a6f7Sdando_thread_test2 walthread-1 -seconds $seconds(walthread-1) -init { 2485e0ce87aSdan execsql { 2495e0ce87aSdan PRAGMA journal_mode = WAL; 2505e0ce87aSdan CREATE TABLE t1(x PRIMARY KEY); 2515e0ce87aSdan PRAGMA lock_status; 2525e0ce87aSdan INSERT INTO t1 VALUES(randomblob(100)); 2535e0ce87aSdan INSERT INTO t1 VALUES(randomblob(100)); 2545e0ce87aSdan INSERT INTO t1 SELECT md5sum(x) FROM t1; 2555e0ce87aSdan } 256b4e3a6f7Sdan} -thread main 10 { 2575e0ce87aSdan 2585e0ce87aSdan proc read_transaction {} { 2595e0ce87aSdan set results [db eval { 2605e0ce87aSdan BEGIN; 2615e0ce87aSdan PRAGMA integrity_check; 2625e0ce87aSdan SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1); 2635e0ce87aSdan SELECT x FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1); 2645e0ce87aSdan SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1); 2655e0ce87aSdan COMMIT; 2665e0ce87aSdan }] 2675e0ce87aSdan 2685e0ce87aSdan if {[llength $results]!=4 2695e0ce87aSdan || [lindex $results 0] != "ok" 2705e0ce87aSdan || [lindex $results 1] != [lindex $results 2] 2715e0ce87aSdan || [lindex $results 2] != [lindex $results 3] 2725e0ce87aSdan } { 2735e0ce87aSdan error "Failed read transaction: $results" 2747c24610eSdan } 2757c24610eSdan } 2767c24610eSdan 2775e0ce87aSdan proc write_transaction {} { 2785e0ce87aSdan db eval { 2795e0ce87aSdan BEGIN; 280809badc9Sdan INSERT INTO t1 VALUES(randomblob(101 + $::E(pid))); 281809badc9Sdan INSERT INTO t1 VALUES(randomblob(101 + $::E(pid))); 2825e0ce87aSdan INSERT INTO t1 SELECT md5sum(x) FROM t1; 2835e0ce87aSdan COMMIT; 2847c24610eSdan } 2857c24610eSdan } 2867c24610eSdan 287f9b76710Sdan # Turn off auto-checkpoint. Otherwise, an auto-checkpoint run by a 288f9b76710Sdan # writer may cause the dedicated checkpoint thread to return an 289f9b76710Sdan # SQLITE_BUSY error. 290f9b76710Sdan # 291f9b76710Sdan db eval { PRAGMA wal_autocheckpoint = 0 } 292f9b76710Sdan 2935e0ce87aSdan set nRun 0 2945e0ce87aSdan while {[tt_continue]} { 2955e0ce87aSdan read_transaction 2965e0ce87aSdan write_transaction 2975e0ce87aSdan incr nRun 2985e0ce87aSdan } 2995e0ce87aSdan set nRun 3005e0ce87aSdan 3015e0ce87aSdan} -thread ckpt 1 { 3025e0ce87aSdan set nRun 0 3035e0ce87aSdan while {[tt_continue]} { 3045a299f91Sdan db eval "PRAGMA wal_checkpoint" 3055e0ce87aSdan usleep 500 3065e0ce87aSdan incr nRun 3075e0ce87aSdan } 3085e0ce87aSdan set nRun 3097c24610eSdan} 3107c24610eSdan 311e45d4426Sdan#-------------------------------------------------------------------------- 312b4e3a6f7Sdan# This test has clients run the following procedure as fast as possible 313b4e3a6f7Sdan# in a loop: 314e45d4426Sdan# 315b4e3a6f7Sdan# 1. Open a database handle. 316b4e3a6f7Sdan# 2. Execute a read-only transaction on the db. 317b4e3a6f7Sdan# 3. Do "PRAGMA journal_mode = XXX", where XXX is one of WAL or DELETE. 318b4e3a6f7Sdan# Ignore any SQLITE_BUSY error. 319b4e3a6f7Sdan# 4. Execute a write transaction to insert a row into the db. 320b4e3a6f7Sdan# 5. Run "PRAGMA integrity_check" 321b4e3a6f7Sdan# 322b4e3a6f7Sdan# At present, there are 4 clients in total. 2 do "journal_mode = WAL", and 323b4e3a6f7Sdan# two do "journal_mode = DELETE". 324b4e3a6f7Sdan# 325b4e3a6f7Sdan# Each client returns a string of the form "W w, R r", where W is the 326b4e3a6f7Sdan# number of write-transactions performed using a WAL journal, and D is 327b4e3a6f7Sdan# the number of write-transactions performed using a rollback journal. 328b4e3a6f7Sdan# For example, "192 w, 185 r". 329b4e3a6f7Sdan# 330*b8fff29cSdanif {[atomic_batch_write test.db]==0} { 331b4e3a6f7Sdan do_thread_test2 walthread-2 -seconds $seconds(walthread-2) -init { 332e45d4426Sdan execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE) } 333e45d4426Sdan } -thread RB 2 { 334e45d4426Sdan 335e45d4426Sdan db close 336e45d4426Sdan set nRun 0 337e45d4426Sdan set nDel 0 338e45d4426Sdan while {[tt_continue]} { 339e45d4426Sdan sqlite3 db test.db 340e45d4426Sdan db busy busyhandler 341e45d4426Sdan db eval { SELECT * FROM sqlite_master } 342e45d4426Sdan catch { db eval { PRAGMA journal_mode = DELETE } } 343e45d4426Sdan db eval { 344e45d4426Sdan BEGIN; 345b4e3a6f7Sdan INSERT INTO t1 VALUES(NULL, randomblob(100+$E(pid))); 346e45d4426Sdan } 347e45d4426Sdan incr nRun 1 348e45d4426Sdan incr nDel [file exists test.db-journal] 349b4e3a6f7Sdan if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { 350b4e3a6f7Sdan error "File-system looks bad..." 351b4e3a6f7Sdan } 352e45d4426Sdan db eval COMMIT 353e45d4426Sdan 354b4e3a6f7Sdan integrity_check 355e45d4426Sdan db close 356e45d4426Sdan } 357e45d4426Sdan list $nRun $nDel 358b4e3a6f7Sdan set {} "[expr $nRun-$nDel] w, $nDel r" 359e45d4426Sdan 360e45d4426Sdan } -thread WAL 2 { 361e45d4426Sdan db close 362e45d4426Sdan set nRun 0 363b4e3a6f7Sdan set nDel 0 364e45d4426Sdan while {[tt_continue]} { 365e45d4426Sdan sqlite3 db test.db 366e45d4426Sdan db busy busyhandler 367e45d4426Sdan db eval { SELECT * FROM sqlite_master } 368e45d4426Sdan catch { db eval { PRAGMA journal_mode = WAL } } 369e45d4426Sdan db eval { 370e45d4426Sdan BEGIN; 371b4e3a6f7Sdan INSERT INTO t1 VALUES(NULL, randomblob(110+$E(pid))); 372e45d4426Sdan } 373e45d4426Sdan incr nRun 1 374b4e3a6f7Sdan incr nDel [file exists test.db-journal] 375b4e3a6f7Sdan if {[file exists test.db-journal] + [file exists test.db-wal] != 1} { 376b4e3a6f7Sdan error "File-system looks bad..." 377b4e3a6f7Sdan } 378e45d4426Sdan db eval COMMIT 379e45d4426Sdan 380b4e3a6f7Sdan integrity_check 381e45d4426Sdan db close 382e45d4426Sdan } 383b4e3a6f7Sdan set {} "[expr $nRun-$nDel] w, $nDel r" 384b4e3a6f7Sdan } 385*b8fff29cSdan} 386b4e3a6f7Sdan 38731c03907Sdando_thread_test walthread-3 -seconds $seconds(walthread-3) -init { 388b4e3a6f7Sdan execsql { 389b4e3a6f7Sdan PRAGMA journal_mode = WAL; 390b4e3a6f7Sdan CREATE TABLE t1(cnt PRIMARY KEY, sum1, sum2); 391b4e3a6f7Sdan CREATE INDEX i1 ON t1(sum1); 392b4e3a6f7Sdan CREATE INDEX i2 ON t1(sum2); 393b4e3a6f7Sdan INSERT INTO t1 VALUES(0, 0, 0); 394b4e3a6f7Sdan } 395b4e3a6f7Sdan} -thread t 10 { 396b4e3a6f7Sdan 397b4e3a6f7Sdan set nextwrite $E(pid) 398b4e3a6f7Sdan 399b4e3a6f7Sdan proc wal_hook {zDb nEntry} { 400f9b76710Sdan if {$nEntry>10} { 401f9b76710Sdan set rc [catch { db eval {PRAGMA wal_checkpoint} } msg] 402f9b76710Sdan if {$rc && $msg != "database is locked"} { error $msg } 403f9b76710Sdan } 404b4e3a6f7Sdan return 0 405b4e3a6f7Sdan } 406b4e3a6f7Sdan db wal_hook wal_hook 407b4e3a6f7Sdan 408b4e3a6f7Sdan while {[tt_continue]} { 409b4e3a6f7Sdan set max 0 410b4e3a6f7Sdan while { $max != ($nextwrite-1) && [tt_continue] } { 411b4e3a6f7Sdan set max [db eval { SELECT max(cnt) FROM t1 }] 412b4e3a6f7Sdan } 413b4e3a6f7Sdan 414b4e3a6f7Sdan if {[tt_continue]} { 415b4e3a6f7Sdan set sum1 [db eval { SELECT sum(cnt) FROM t1 }] 416b4e3a6f7Sdan set sum2 [db eval { SELECT sum(sum1) FROM t1 }] 417b4e3a6f7Sdan db eval { INSERT INTO t1 VALUES($nextwrite, $sum1, $sum2) } 418b4e3a6f7Sdan incr nextwrite $E(nthread) 419b4e3a6f7Sdan integrity_check 420b4e3a6f7Sdan } 421b4e3a6f7Sdan } 422b4e3a6f7Sdan 423b4e3a6f7Sdan set {} ok 424b4e3a6f7Sdan} -check { 425b4e3a6f7Sdan puts " Final db contains [db eval {SELECT count(*) FROM t1}] rows" 426b4e3a6f7Sdan puts " Final integrity-check says: [db eval {PRAGMA integrity_check}]" 427b4e3a6f7Sdan 428b4e3a6f7Sdan # Check that the contents of the database are Ok. 429b4e3a6f7Sdan set c 0 430b4e3a6f7Sdan set s1 0 431b4e3a6f7Sdan set s2 0 432b4e3a6f7Sdan db eval { SELECT cnt, sum1, sum2 FROM t1 ORDER BY cnt } { 433b4e3a6f7Sdan if {$c != $cnt || $s1 != $sum1 || $s2 != $sum2} { 434b4e3a6f7Sdan error "database content is invalid" 435b4e3a6f7Sdan } 436b4e3a6f7Sdan incr s2 $s1 437b4e3a6f7Sdan incr s1 $c 438b4e3a6f7Sdan incr c 1 439e45d4426Sdan } 440e45d4426Sdan} 441e45d4426Sdan 44231c03907Sdando_thread_test2 walthread-4 -seconds $seconds(walthread-4) -init { 44331c03907Sdan execsql { 44431c03907Sdan PRAGMA journal_mode = WAL; 44531c03907Sdan CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE); 44631c03907Sdan } 44731c03907Sdan} -thread r 1 { 44831c03907Sdan # This connection only ever reads the database. Therefore the 44931c03907Sdan # busy-handler is not required. Disable it to check that this is true. 450fa408adaSdan # 451fa408adaSdan # UPDATE: That is no longer entirely true - as we don't use a blocking 452fa408adaSdan # lock to enter RECOVER state. Which means there is a small chance a 453fa408adaSdan # reader can see an SQLITE_BUSY. 454fa408adaSdan # 455fa408adaSdan while {[tt_continue]} { 456fa408adaSdan integrity_check 457fa408adaSdan } 45831c03907Sdan set {} ok 45931c03907Sdan} -thread w 1 { 46031c03907Sdan 46131c03907Sdan proc wal_hook {zDb nEntry} { 4625def0843Sdrh if {$nEntry>15} {db eval {PRAGMA wal_checkpoint}} 46331c03907Sdan return 0 46431c03907Sdan } 46531c03907Sdan db wal_hook wal_hook 46631c03907Sdan set row 1 46731c03907Sdan while {[tt_continue]} { 46831c03907Sdan db eval { REPLACE INTO t1 VALUES($row, randomblob(300)) } 46931c03907Sdan incr row 47031c03907Sdan if {$row == 10} { set row 1 } 47131c03907Sdan } 47231c03907Sdan 47331c03907Sdan set {} ok 47431c03907Sdan} 47531c03907Sdan 476bb16ff75Sdan 477bb16ff75Sdan# This test case attempts to provoke a deadlock condition that existed in 478bb16ff75Sdan# the unix VFS at one point. The problem occurred only while recovering a 479bb16ff75Sdan# very large wal file (one that requires a wal-index larger than the 480bb16ff75Sdan# initial default allocation of 64KB). 481bb16ff75Sdan# 482bb16ff75Sdando_thread_test walthread-5 -seconds $seconds(walthread-5) -init { 483bb16ff75Sdan 484bb16ff75Sdan proc log_file_size {nFrame pgsz} { 485bb16ff75Sdan expr {12 + ($pgsz+16)*$nFrame} 486bb16ff75Sdan } 487bb16ff75Sdan 488bb16ff75Sdan execsql { 489bb16ff75Sdan PRAGMA page_size = 1024; 490bb16ff75Sdan PRAGMA journal_mode = WAL; 491bb16ff75Sdan CREATE TABLE t1(x); 492bb16ff75Sdan BEGIN; 493bb16ff75Sdan INSERT INTO t1 VALUES(randomblob(900)); 494bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ 495bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ 496bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */ 497bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */ 498bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */ 499bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */ 500bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 128 */ 501bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */ 502bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */ 503bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */ 504bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */ 505bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */ 506bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ 507bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ 508bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32768 */ 509bb16ff75Sdan INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 65536 */ 510bb16ff75Sdan COMMIT; 511bb16ff75Sdan } 512bb16ff75Sdan 513fda06befSmistachkin forcecopy test.db-wal bak.db-wal 514fda06befSmistachkin forcecopy test.db bak.db 515bb16ff75Sdan db close 516bb16ff75Sdan 517fda06befSmistachkin forcecopy bak.db-wal test.db-wal 518fda06befSmistachkin forcecopy bak.db test.db 519bb16ff75Sdan 520bb16ff75Sdan if {[file size test.db-wal] < [log_file_size [expr 64*1024] 1024]} { 521bb16ff75Sdan error "Somehow failed to create a large log file" 522bb16ff75Sdan } 523bb16ff75Sdan puts "Database with large log file recovered. Now running clients..." 524bb16ff75Sdan} -thread T 5 { 525bb16ff75Sdan db eval { SELECT count(*) FROM t1 } 526bb16ff75Sdan} 527d0b2677bSdrhunset -nocomplain seconds 528bb16ff75Sdan 5297c24610eSdanfinish_test 530