xref: /sqlite-3.40.0/test/walthread.test (revision b8fff29c)
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