hi, Despite the threads-are-evil claim...
What happens with respect to the page cache if I have an application (= one multi-threaded process) opening two DB handles (sqlite3_open()) - one DB handle dedicated to one thread. Do multilpe db handles in the same process cause sqlite to reload the whole data set from disk after another db handle in the same process has written to the database. Note: Semantically, I always translate DB handle in the context of sqlite to "connection". However, let's use the term DB handle to make things maybe a little less confusing. I hope I stick to db handle for the rest of the e-mail. Also assume "standard" Posix threads (pthreads) semantics under Unix/Linux. thread t1 -> db_handle1 thread t2 -> db_handle2 Now I start a "normal write transaction" (BEGIN, not BEGIN IMMEDIATE, not BEGIN EXCLUSIVE) on the db_handle1: thread 1{ sqlite3_open ("/mnt/ext3/mydatabase", &dbhandle1) sqlite3_exec (dbhandle1, "BEGIN", ..) sqlite3_exec (dbhandle1, "insert into table1 values (1)", ..) sqlite3_exec (dbhandle1, "COMMIT", ..) } and read transactions in thread 2 thread 2{ sqlite3_open ("/mnt/ext3/mydatabase", &dbhandle2) sqlite3_exec (dbhandle2, "BEGIN", ..) sqlite3_exec (dbhandle2, "select * from table1", ..) sqlite3_exec (dbhandle2, "COMMIT", ..) } and they get eventually executed like this: 1: t1: sqlite3_open ("/mnt/ext3/mydatabase", &dbhandle1) 2: t2: sqlite3_open ("/mnt/ext3/mydatabase", &dbhandle2) 3: t2: sqlite3_exec (dbhandle2, "BEGIN", ..) 4: t2: sqlite3_exec (dbhandle2, "select * from table1", ..) 5: t2: sqlite3_exec (dbhandle2, "COMMIT", ..) 6: t1: sqlite3_exec (dbhandle1, "BEGIN", ..) 7: t1: sqlite3_exec (dbhandle1, "insert into table1 values (1)", ..) 8: t1: sqlite3_exec (dbhandle1, "COMMIT", ..) 9: t2: sqlite3_exec (dbhandle2, "BEGIN", ..) 10: t2: sqlite3_exec (dbhandle2, "select * from table1", ..) 11: t2: sqlite3_exec (dbhandle2, "COMMIT", ..) My understanding of the documentation regarding the locking protocol (http://www.sqlite.org/lockingv3.html) is that threads are not treated differently than processes. Which would mean that eventhough both threads could have access to same page cache (in the same process) they don't use it. However when running the test as described below against sqlite 3.5.9 (amalgamation), noweher between step 8 and 11 does it do any file access (not even fcntl()s). Which makes me suspect that thread1's db_handle1 and thread2's db_handle2 use the same page cache as thread2 clearly sees the changes done in steps 6-8. So the question is: is the page cache actually shared even with an explicit sqlite3_enable_shared_cache(0) before any other sqlite3 operation? Naturally with sqlite3_enable_shared_cache(1) sqlite3 goes into table-locking mode which is not want I'm loooking for. My bigger concern here is the reloading of the page cache from disk (or OS's buffer cache) after write transactions in the same process. Do read transactions on different db handles in the same process context cause a reload of the page cache? I tried to dig through the source code, but that's, eventhough nicely documented, still to confusing to easily follow up what's going on. Could you point out some key points in the source code to help in understanding what's going on. Markus PS: Below some sample code. Simply compile and run with "sampleapp db-file". Note that it deletes the file sfirst if it exists. Run the application with e.g. "strace" to see system calls. The application creates two db handles to the same db (one in the main thread and one in a newly started thread). The first one write and the second one reads only. #include <stdio.h> #include <stdlib.h> #include <sqlite3.h> #include <pthread.h> #include <unistd.h> /* * Sample code. No guarantees for correctness */ /* * some global variables that are quick'n'dirty but do the job */ char** argv_global; /* * A callback function needed for sqlite3_exec. Copied from sample code. */ static int callback(void *NotUsed, int argc, char **argv, char **azColName){ int i; for(i=0; i<argc; i++){ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } /* * A function that serves as thread entry point */ void * thread_body(void * x) { char *zErrMsg = 0; int rc; int status; sqlite3 *db_thread; printf ("%d: entered into thread\n", pthread_self()); // // open DB // rc = sqlite3_open(argv_global[1], &db_thread); if( rc ){ fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db_thread)); sqlite3_close(db_thread); exit(1); } // // double check that table1 is there. This is executed still in autocommit mode // rc = sqlite3_exec(db_thread, "select count(*) from table1", callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "BEGIN TRANSACTION: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } while (1) { printf ("\n%d: reader to sleep before start transactions\n",pthread_self()); sleep(6); printf("\n%d: reader woke up and starts transaction\n",pthread_self()); // // Begin transaction // rc = sqlite3_exec(db_thread, "BEGIN", callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "BEGIN TRANSACTION: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } // // execute statement // rc = sqlite3_exec(db_thread, "select * from table1", callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "Statement: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } // // Commit transaction // rc = sqlite3_exec(db_thread, "COMMIT", callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "BEGIN TRANSACTION: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } printf ("\n%d: reader after commit\n", pthread_self()); } return (void*)0; } int main(int argc, char **argv){ char *zErrMsg = 0; int rc; int status; sqlite3 *db_main; pthread_t t1; int i; char* buffer=(char*)malloc(100); argv_global = argv; rc=0; if( argc!=2 ){ fprintf(stderr, "Usage: %s db-file \n", argv[0]); exit(1); } printf("%d: is threasafe sqlite library: %d\n",pthread_self(), sqlite3_threadsafe() ); printf("%d: deleting old DB file", pthread_self()); unlink(argv[1]); // 0 = turn off shared cache (false) // seems to be the default // 1 = turn on shared cache (true) rc = sqlite3_enable_shared_cache(0); if( rc ){ fprintf(stderr, "Unable to open db in shared cache mode: %s\n", \ sqlite3_errmsg(db_main)); exit(1); } // // open DB // rc = sqlite3_open_v2(argv[1], &db_main, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if( rc ){ fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db_main)); sqlite3_close(db_main); exit(1); } // // create table // rc = sqlite3_exec(db_main, "create table table1 (int i)", callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "Could not create table: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } // // start new thread // status = pthread_create(&t1, (pthread_attr_t*)0, thread_body, (void*)0); if (0 != status ) printf ("Could not start thread\n"); i=0; while (1){ printf("\n%d: starting write transaction\n", pthread_self()); // // Begin transaction // normal BEGIN DEFERRED, also try BEGIN IMMEDIATE and BEGIN EXCLUSIVE rc = sqlite3_exec(db_main, "BEGIN", callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "BEGIN TRANSACTION: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } // // some insert/delete/update/select statements // sprintf(buffer, "insert into table1 values (%d)", i); printf ("%s", buffer); rc = sqlite3_exec(db_main, buffer, callback, 0, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } printf ("\n%d: writer going to sleep before commit\n", pthread_self()); sleep(3); printf("\n%d: writer going to commit\n", pthread_self()); rc=5; // // Commit transaction ("COMMIT" or "ROLLBACK") // while (rc==5){ //not sure what the constant should be here (5=???) rc = sqlite3_exec(db_main, "COMMIT" , callback, 0, &zErrMsg); printf ("\n%d: commit returned with %d\n", pthread_self(), rc); sleep (1); } if( rc!=SQLITE_OK ){ fprintf(stderr, "COMMIT TRANSACTION: SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } printf("\n%d: inserted value (after commit): %d\n",pthread_self(), i); i++; } //while sqlite3_close(db_main); return 0; } <img src="http://www.bigstring.com/refer.php?img=60" width="1" height="1">BigString.com, a smarter way to email. Signup for a free email account today. _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users