############################################################################### # # stress.eagle -- # # Written by Joe Mistachkin. # Released to the public domain, use at your own risk! # ############################################################################### package require Eagle package require Eagle.Library package require Eagle.Test runTestPrologue ############################################################################### package require System.Data.SQLite.Test runSQLiteTestPrologue ############################################################################### runTest {test stress-1.1 {multithreaded stress testing} -setup { unset -nocomplain result thread index workload noWorkload srcDb db \ fileName compiled options count ############################################################################# proc expectedError { error } { return [expr {[regexp -- {\sno such table: t1\s} $error] || \ [regexp -- {\sdatabase is locked\s} $error]}] } ############################################################################# proc showTest { indicator } { tputs $::test_channel $indicator after [expr {int(rand() * 1000 + $::count(2))}] } ############################################################################# proc failTest { error } { set level [expr {[info level] - 1}] tputs $::test_channel [appendArgs \ \n [info level $level] ": " $error \n] exit Failure; # halt all testing now. } ############################################################################# set count(0) 1 set count(1) 5 set count(2) 300 set noWorkload [list] if {[info exists argv] && [llength $argv] > 0} then { parse options -flags \ {-StopOnUnknown +IgnoreOnUnknown SkipOnUnknown} -- [list \ [list null MustHaveIntegerValue -1 -1 -count0 $count(0)] \ [list null MustHaveIntegerValue -1 -1 -count1 $count(1)] \ [list null MustHaveIntegerValue -1 -1 -count2 $count(2)] \ [list null MustHaveListValue -1 -1 -noWorkload $noWorkload]] $argv set count(0) $options(-count0,value) set count(1) $options(-count1,value) set count(2) $options(-count2,value) set noWorkload $options(-noWorkload,value) } ############################################################################# tputs $test_channel [appendArgs \ "---- workloads will repeat " $count(0) " time(s)...\n"] tputs $test_channel [appendArgs \ "---- workloads will have " $count(1) " iteration(s)...\n"] tputs $test_channel [appendArgs \ "---- workloads will wait at least " $count(2) " millisecond(s)...\n"] if {[llength $noWorkload] > 0} then { tputs $test_channel [appendArgs \ "---- workloads to be skipped... " $noWorkload \n] } ############################################################################# set compiled(12) "" set compiled(13) "" ############################################################################# set fileName(1) "file::memory:?cache=shared" set fileName(2) [file join [getDatabaseDirectory] stress.db] ############################################################################# # # NOTE: Make sure to remove any stale database from previous test runs. # cleanupFile $fileName(2) ############################################################################# setupDb $fileName(1) "" "" "" "" "" false false true srcDb setupDb $fileName(2) ############################################################################# set workload(1) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #1, CREATE TABLE statements. # lappend ::times(1) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 2} {$index <= $count} {incr index} { sql execute $db [appendArgs \ "CREATE TABLE IF NOT EXISTS t" $index "(x PRIMARY KEY, y, z);"] showTest 1 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(2) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #2, DROP TABLE statements. # lappend ::times(2) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 2} {$index <= $count} {incr index} { sql execute $db [appendArgs \ "DROP TABLE IF EXISTS t" $index \;] showTest 2 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(3) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #3, "small" SELECT statements. # lappend ::times(3) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute -execute reader $db [appendArgs \ "SELECT x, y FROM " $table " WHERE z = 'small';"] showTest 3 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(4) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #4, "big" SELECT statements. # lappend ::times(4) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute -execute reader $db [appendArgs \ "SELECT x, y FROM " $table " WHERE z = 'big';"] showTest 4 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(5) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #5, "small" INSERT statements. # lappend ::times(5) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db [appendArgs \ "INSERT INTO " $table "(x, y, z) VALUES('" \ [format %lX [expr {random()}]] "', '" \ [base64 encode -- [expr {randstr(10000)}]] \ "', 'small');"] showTest 5 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(6) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #6, "big" INSERT statements. # lappend ::times(6) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db [appendArgs \ "INSERT INTO " $table "(x, y, z) VALUES('" \ [format %lX [expr {random()}]] \ "', RANDOMBLOB(10000000), 'big');"] showTest 6 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(7) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #7, "small" UPDATE statements. # lappend ::times(7) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db [appendArgs "UPDATE " $table \ " SET y = '" [base64 encode -- [expr {randstr(10000)}]] \ "' WHERE x LIKE '" [format %X $index] "%' AND z = 'small';"] showTest 7 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(8) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #8, "big" UPDATE statements. # lappend ::times(8) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db [appendArgs "UPDATE " $table \ " SET y = RANDOMBLOB(10000000) WHERE x LIKE '" \ [format %X $index] "%' AND z = 'big';"] showTest 8 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(9) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #9, "small" DELETE statements. # lappend ::times(9) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db [appendArgs "DELETE FROM " $table \ " WHERE x LIKE '" [format %X $index] "%' AND z = 'small';"] showTest 9 } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(10) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #10, "big" DELETE statements. # lappend ::times(10) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db [appendArgs "DELETE FROM " $table \ " WHERE x LIKE '" [format %X $index] "%' AND z = 'big';"] showTest A } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(11) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #11, VACUUM statement. # lappend ::times(11) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { sql execute $db "VACUUM;" showTest B } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(12) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #12, backup to in-memory database. # lappend ::times(12) [lindex [time { for {set index 1} {$index <= $count} {incr index} { if {[string is integer -strict $::compiled(12)]} then { set id $::compiled(12); # NOTE: Already compiled. object invoke _Dynamic${id}.Test${id} BackupAndGetData } else { set id [object invoke Interpreter.GetActive NextId] set code [compileCSharpWith [subst { using System; using System.Data.SQLite; using System.Text; namespace _Dynamic${id} { public static class Test${id} { public static void BackupAndGetData() { using (SQLiteConnection source = new SQLiteConnection( "FullUri=${dstFileName};")) { source.Open(); using (SQLiteConnection destination = new SQLiteConnection( "FullUri=${srcFileName};")) { destination.Open(); source.BackupDatabase( destination, "main", "main", -1, null, 0); } } } /////////////////////////////////////////////////////////////// public static void Main() { // do nothing. } } } }] true true true results errors System.Data.SQLite.dll] if {$code eq "Ok"} then { set ::compiled(12) $id; # NOTE: Compiled OK. object invoke _Dynamic${id}.Test${id} BackupAndGetData } else { error $errors } } showTest C } }] 0] }] ############################################################################# set workload(13) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #13, backup from an in-memory database. # lappend ::times(13) [lindex [time { for {set index 1} {$index <= $count} {incr index} { if {[string is integer -strict $::compiled(13)]} then { set id $::compiled(13); # NOTE: Already compiled. object invoke _Dynamic${id}.Test${id} BackupAndGetData } else { set id [object invoke Interpreter.GetActive NextId] set code [compileCSharpWith [subst { using System; using System.Data.SQLite; using System.Text; namespace _Dynamic${id} { public static class Test${id} { public static void BackupAndGetData() { using (SQLiteConnection source = new SQLiteConnection( "FullUri=${srcFileName};")) { source.Open(); using (SQLiteConnection destination = new SQLiteConnection( "FullUri=${dstFileName};")) { destination.Open(); source.BackupDatabase( destination, "main", "main", -1, null, 0); } } } /////////////////////////////////////////////////////////////// public static void Main() { // do nothing. } } } }] true true true results errors System.Data.SQLite.dll] if {$code eq "Ok"} then { set ::compiled(13) $id; # NOTE: Compiled OK. object invoke _Dynamic${id}.Test${id} BackupAndGetData } else { error $errors } } showTest D } }] 0] }] ############################################################################# set workload(14) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #14, PRAGMA integrity check statement. # lappend ::times(14) [lindex [time { setupDb $dstFileName "" "" "" "" "" true false if {[catch { for {set index 1} {$index <= $count} {incr index} { set result [sql execute -execute scalar $db \ "PRAGMA integrity_check;"] if {$result eq "ok"} then { showTest E } else { error [appendArgs "integrity check failed: " $result] } } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } cleanupDb $dstFileName db false true false }] 0] }] ############################################################################# set workload(15) [list [list srcFileName dstFileName table count] { # # NOTE: Workload #15, force managed garbage collection # lappend ::times(15) [lindex [time { if {[catch { for {set index 1} {$index <= $count} {incr index} { collectGarbage $::test_channel showTest F } } error]} then { if {[expectedError $error]} then { showTest * } else { failTest $error } } }] 0] }] } -body { for {set index(0) 0} {$index(0) < $count(0)} {incr index(0)} { sql execute $db "CREATE TABLE IF NOT EXISTS t1(x PRIMARY KEY, y, z);" unset -nocomplain thread foreach index(1) [lsort -integer [array names workload]] { if {[lsearch -exact $noWorkload $index(1)] == -1} then { set thread($index(1)) [object create -alias System.Threading.Thread \ [list apply $workload($index(1)) $fileName(1) $fileName(2) t1 \ $count(1)]] } } foreach index(1) [list Start Join] { foreach index(2) [array names thread] { $thread($index(2)) $index(1) } } unset -nocomplain thread } tputs $test_channel \n foreach index(0) [lsort -integer [array names times]] { set times(length) [llength $times($index(0))] if {$times(length) > 0} then { tputs $test_channel [appendArgs \ "---- average time for workload (" $index(0) ") is about " \ [expr int(([join $times($index(0)) +])/$times(length)/1000.0)] \ " milliseconds\n"] } else { tputs $test_channel [appendArgs \ "---- no times for workload (" $index(0) ")\n"] } } set result [sql execute -execute scalar $srcDb "PRAGMA integrity_check;"] if {$result eq "ok"} then { tputs $test_channel "---- integrity check ok (srcDb)\n" } else { error [appendArgs "integrity check failed (srcDb): " $result] } set result [sql execute -execute scalar $db "PRAGMA integrity_check;"] if {$result eq "ok"} then { tputs $test_channel "---- integrity check ok (db)\n" } else { error [appendArgs "integrity check failed (db): " $result] } } -cleanup { rename failTest "" rename showTest "" rename expectedError "" cleanupDb $fileName(2) cleanupDb $fileName(1) srcDb foreach index(0) [array names workload] { catch { object removecallback [list apply $workload($index(0)) $fileName(1) \ $fileName(2) t1 $count(1)] } } unset -nocomplain result thread index workload noWorkload srcDb db \ fileName compiled options count times } -constraints \ {eagle monoBug28 command.sql compile.DATA SQLite System.Data.SQLite} -result {}} ############################################################################### runSQLiteTestEpilogue runTestEpilogue