###############################################################################
#
# 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