From: drh <> Date: Wed, 12 May 2021 14:17:20 +0000 (+0000) Subject: Add the new threadtest5 test program for stressing multiple database X-Git-Tag: version-3.36.0~108 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=85ffcae177ba896a1875a9ecd8bd6474fb024af0;p=thirdparty%2Fsqlite.git Add the new threadtest5 test program for stressing multiple database connections in the same process hammering on a single database. Primarily designed to test memdb, but works on any database. FossilOrigin-Name: 8db1c06958b8e1691440d4fd392648b74a1940b721852dabd315005efad520fc --- diff --git a/Makefile.in b/Makefile.in index 72be0e4fab..f010cdaa15 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1427,6 +1427,9 @@ threadtest3$(TEXE): sqlite3.lo $(THREADTEST3_SRC) threadtest: threadtest3$(TEXE) ./threadtest3$(TEXE) +threadtest5: sqlite3.c $(TOP)/test/threadtest5.c + $(LTLINK) $(TOP)/test/threadtest5.c sqlite3.c -o $@ $(TLIBS) + releasetest: $(TCLSH_CMD) $(TOP)/test/releasetest.tcl @@ -1480,6 +1483,7 @@ clean: rm -f sqldiff sqldiff.exe rm -f dbhash dbhash.exe rm -f fts5.* fts5parse.* + rm -f threadtest5 distclean: clean rm -f config.h config.log config.status libtool Makefile sqlite3.pc diff --git a/main.mk b/main.mk index 8f1204d386..d8b44f4190 100644 --- a/main.mk +++ b/main.mk @@ -1079,6 +1079,9 @@ rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.o loadfts: $(TOP)/tool/loadfts.c libsqlite3.a $(TCC) $(TOP)/tool/loadfts.c libsqlite3.a -o loadfts $(THREADLIB) +threadtest5: $(TOP)/test/threadtest5.c libsqlite3.a + $(TCC) $(TOP)/test/threadtest5.c libsqlite3.a -o threadtest5 $(THREADLIB) + # This target will fail if the SQLite amalgamation contains any exported # symbols that do not begin with "sqlite3_". It is run as part of the # releasetest.tcl script. @@ -1141,3 +1144,4 @@ clean: rm -f sqldiff sqldiff.exe rm -f fts5.* fts5parse.* rm -f lsm.h lsm1.c + rm -f threadtest5 diff --git a/manifest b/manifest index c6bbea6614..6aa2c423a5 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Enhance\sthe\smemdb\sVFS\sso\sthat\sit\sis\sable\sto\sshare\sdatabases\samong\smultiple\ndatabase\sconnections\sin\sthe\ssame\sprocess,\sas\slong\sas\sthe\sdatabase\sfilename\nbegins\swith\s"/".\s\sThis\sprovides\sa\sway\sfor\sthreads\sto\sshare\san\sin-memory\ndatabase\swithout\sthe\suse\sof\sshared-cache\smode. -D 2021-05-12T11:55:59.991 +C Add\sthe\snew\sthreadtest5\stest\sprogram\sfor\sstressing\smultiple\sdatabase\nconnections\sin\sthe\ssame\sprocess\shammering\son\sa\ssingle\sdatabase.\nPrimarily\sdesigned\sto\stest\smemdb,\sbut\sworks\son\sany\sdatabase. +D 2021-05-12T14:17:20.226 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in b0982835aa57c66a43a618679b278a0e6e0e6ec08cb940b25ca9928b7c409ddc +F Makefile.in 30c6d39386246695e951a676973e0bf57aabbd1e37024c07e657af89dd332555 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc 6443729ba6a013a0fea4f999b22e54760f36e73c2e691554f0c4bfa1dbe4d070 F README.md 2a71913f398ecac5f3e10945fcf438aed425c2e9ed9874de561156ba77fb7023 @@ -465,7 +465,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 6871d77dc5966921c48c50e1a12598a82a900c8b845d4fdf04c56d2a5cc6dc5d +F main.mk d1654e2923a7195603a7d6564dabe037a3a20ea5b3817002db534ffc4ad8cecf F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -1463,6 +1463,7 @@ F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c a70a8e94bef23339d34226eb9521015ef99f4df8 F test/threadtest3.c e63013af10cf236c7610eb06d33bde08c861806dc64be811940ff4d9ddd34a4f F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925 +F test/threadtest5.c 9b4d782c58d8915d7e955ff8051f3d03628bda0d33b82971ea8c0f2f2808c421 w test/memdb-threads-1.c F test/time-wordcount.sh 8e0b0f8109367827ad5d58f5cc849705731e4b90 F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c F test/tkt-18458b1a.test 6a62cb1ee50fa3c620da59e3a6f531eb38fceaf7e2166203816b724524e6f1d6 @@ -1912,8 +1913,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f39666e64d6d32420170c54f874d8314eb8c6f91df625f7b28f9ccffb9474dc0 98dae595d861941bb0bcd12126ee02492587c466e6da579a58b5dc4a4d655917 -R 1441e09faa39fd11227f415ae28549ff -T +closed 98dae595d861941bb0bcd12126ee02492587c466e6da579a58b5dc4a4d655917 +P 533fffc4a39b01c3aba75bd3271fd6ccd9516d9681ed04adbe19bd7de03f4c16 +R 4fefc92ffd76e6c05a1532a9460cad0d U drh -Z bbd64c76e9984e3f1696dac12550f00b +Z d3fbbeb2377b80ce42b7a7b26e8ca8a8 diff --git a/manifest.uuid b/manifest.uuid index 6e05c7e90b..9f7f95d086 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -533fffc4a39b01c3aba75bd3271fd6ccd9516d9681ed04adbe19bd7de03f4c16 \ No newline at end of file +8db1c06958b8e1691440d4fd392648b74a1940b721852dabd315005efad520fc \ No newline at end of file diff --git a/test/threadtest5.c b/test/threadtest5.c new file mode 100644 index 0000000000..6e6610ff66 --- /dev/null +++ b/test/threadtest5.c @@ -0,0 +1,339 @@ +/* +** 2021-05-12 +** +** 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. +** +************************************************************************* +** +** Testing threading behavior when multiple database connections in separate +** threads of the same process are all talking to the same database file. +** +** For best results, ensure that SQLite is compiled with HAVE_USLEEP=1 +** +** Only works on unix platforms. +** +** Usage: +** +** ./threadtest5 ?DATABASE? +** +** If DATABASE is omitted, it defaults to using file:/mem?vfs=memdb. +*/ +#include "sqlite3.h" +#include +#include +#include +#include +#include +#include + +/* Name of the in-memory database */ +static char *zDbName = 0; + +/* True for debugging */ +static int eVerbose = 0; + +/* If rc is not SQLITE_OK, then print an error message and stop +** the test. +*/ +static void error_out(int rc, const char *zCtx, int lineno){ + if( rc!=SQLITE_OK ){ + fprintf(stderr, "error %d at %d in \"%s\"\n", rc, lineno, zCtx); + exit(-1); + } +} + +#if 0 +/* Return the number of milliseconds since the Julian epoch (-4714-11-24). +*/ +static sqlite3_int64 gettime(void){ + sqlite3_int64 tm; + sqlite3_vfs *pVfs = sqlite3_vfs_find(0); + pVfs->xCurrentTimeInt64(pVfs, &tm); + return tm; +} +#endif + +/* Run the SQL in the second argument. +*/ +static int exec( + sqlite3 *db, + const char *zId, + int lineno, + const char *zFormat, + ... +){ + int rc; + va_list ap; + char *zSql; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( eVerbose){ + printf("%s:%d: [%s]\n", zId, lineno, zSql); + fflush(stdout); + } + rc = sqlite3_exec(db, zSql, 0, 0, 0); + if( rc && eVerbose ){ + printf("%s:%d: return-code %d\n", zId, lineno, rc); + fflush(stdout); + } + sqlite3_free(zSql); + return rc; +} + +/* Generate a perpared statement from the input SQL +*/ +static sqlite3_stmt *prepare( + sqlite3 *db, + const char *zId, + int lineno, + const char *zFormat, + ... +){ + int rc; + va_list ap; + char *zSql; + sqlite3_stmt *pStmt = 0; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( eVerbose){ + printf("%s:%d: [%s]\n", zId, lineno, zSql); + fflush(stdout); + } + + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc ){ + printf("%s:%d: ERROR - %s\n", zId, lineno, sqlite3_errmsg(db)); + exit(-1); + } + sqlite3_free(zSql); + return pStmt; +} + +/* +** Wait for table zTable to exist in the schema. +*/ +static void waitOnTable(sqlite3 *db, const char *zWorker, const char *zTable){ + while(1){ + int eFound = 0; + sqlite3_stmt *q = prepare(db, zWorker, __LINE__, + "SELECT 1 FROM sqlite_schema WHERE name=%Q", zTable); + if( sqlite3_step(q)==SQLITE_ROW && sqlite3_column_int(q,0)!=0 ){ + eFound = 1; + } + sqlite3_finalize(q); + if( eFound ) return; + sqlite3_sleep(1); + } +} + +/* +** Return true if x is a prime number +*/ +static int isPrime(int x){ + int i; + if( x<2 ) return 1; + for(i=2; i*i<=x; i++){ + if( (x%i)==0 ) return 0; + } + return 1; +} + +/* Each worker thread runs an instance of the following */ +static void *worker(void *pArg){ + int rc; + const char *zName = (const char*)pArg; + sqlite3 *db = 0; + + if( eVerbose ){ + printf("%s: startup\n", zName); + fflush(stdout); + } + + rc = sqlite3_open(zDbName, &db); + error_out(rc, "sqlite3_open", __LINE__); + sqlite3_busy_timeout(db, 2000); + + while( 1 ){ + sqlite3_stmt *q1; + int tid = -1; + q1 = prepare(db, zName, __LINE__, + "UPDATE task SET doneby=%Q" + " WHERE tid=(SELECT tid FROM task WHERE doneby IS NULL LIMIT 1)" + "RETURNING tid", zName + ); + if( sqlite3_step(q1)==SQLITE_ROW ){ + tid = sqlite3_column_int(q1,0); + } + sqlite3_finalize(q1); + if( tid<0 ) break; + if( eVerbose ){ + printf("%s: starting task %d\n", zName, tid); + fflush(stdout); + } + if( tid==1 ){ + exec(db, zName, __LINE__, + "CREATE TABLE IF NOT EXISTS p1(x INTEGER PRIMARY KEY);" + ); + }else if( tid>=2 && tid<=51 ){ + int a, b, i; + waitOnTable(db, zName, "p1"); + a = (tid-2)*200 + 1; + b = a+200; + for(i=a; i=53 && tid<=62 ){ + int a, b, i; + waitOnTable(db, zName, "p2"); + a = (tid-53)*10 + 2; + b = a+9; + for(i=a; i<=b; i++){ + exec(db, zName, __LINE__, + "DELETE FROM p2 WHERE x>%d AND (x %% %d)==0", i, i); + } + } + if( eVerbose ){ + printf("%s: completed task %d\n", zName, tid); + fflush(stdout); + } + sqlite3_sleep(1); + } + + sqlite3_close(db); + + if( eVerbose ){ + printf("%s: exit\n", zName); + fflush(stdout); + } + return 0; +} + +/* Print a usage comment and die */ +static void usage(const char *argv0){ + printf("Usage: %s [options]\n", argv0); + printf( + " -num-workers N Run N worker threads\n" + " -v Debugging output\n" + ); + exit(1); +} + +/* Maximum number of threads */ +#define MX_WORKER 100 + +/* +** Main routine +*/ +int main(int argc, char **argv){ + int i; + int nWorker = 4; + int rc; + sqlite3 *db = 0; + sqlite3_stmt *q; + pthread_t aWorker[MX_WORKER]; + char aWorkerName[MX_WORKER][8]; + + for(i=1; iMX_WORKER ){ + printf("number of threads must be between 1 and %d\n", MX_WORKER); + exit(1); + } + continue; + } + printf("unknown option: %s\n", argv[i]); + usage(argv[0]); + } + if( zDbName==0 ) zDbName = "file:/mem?vfs=memdb"; + + sqlite3_config(SQLITE_CONFIG_URI, (int)1); + rc = sqlite3_open(zDbName, &db); + error_out(rc, "sqlite3_open", __LINE__); + + rc = exec(db, "SETUP", __LINE__, + "DROP TABLE IF EXISTS task;\n" + "DROP TABLE IF EXISTS p1;\n" + "DROP TABLE IF EXISTS p2;\n" + "DROP TABLE IF EXISTS verify;\n" + "CREATE TABLE IF NOT EXISTS task(\n" + " tid INTEGER PRIMARY KEY,\n" + " doneby TEXT\n" + ");\n" + "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100)" + "INSERT INTO task(tid) SELECT x FROM c;\n" + ); + error_out(rc, "sqlite3_exec", __LINE__); + + for(i=0; i