We're running into a lot of very slow queries and db locks when running with multiple processes accessing the same database. As a test we created a small application that has only two threads and a small single table database.
RunRead reads all data from the one table (100 records). RunWrite updates all the records in the table (no add/delete, just update). When run by itself with just one process, each read/write concurrent operation runs in 500ms. It's synchronized to start both read/write at the same time and then wait for each to finish before starting the next test, and then loops. It's pretty consistent around 500ms. When I run the same app multiple times (multiple concurrent processes), the operations degrade very quickly. It starts off taking 1-5 seconds for each read/write concurrent operation but after a few minutes it often takes 20-30 seconds for a a single operation and sometimes 45 seconds. The transactions are all BEGIN IMMEDIATE and the noticable time taken is during COMMIT. The reads are not run within a transaction. I'm using sqlite 3.5.9 in SQLite.NET. Full C# test code follows. Is there something I'm doing wrong that is causing this lock contention? Is there anything I can do to improve performance in a multi-process application? Thanks, Sam using System; using System.Data; using System.Data.SQLite; using System.Diagnostics; using System.IO; using System.Threading; namespace test { public class DbLockTest { private static readonly Random _random = new Random(); private readonly ManualResetEvent _start = new ManualResetEvent(false); private readonly ManualResetEvent _readDone = new ManualResetEvent(false); private readonly ManualResetEvent _writeDone = new ManualResetEvent(false); private Stopwatch _timer; public static void Run() { if (!File.Exists("DbLockTest.dat")) { using (SQLiteConnection cnn = CreateConnection()) { using (SQLiteTransaction trans = cnn.BeginTransaction()) { using (SQLiteCommand cmd = cnn.CreateCommand()) { cmd.CommandText = "CREATE TABLE Data (id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT);"; cmd.ExecuteNonQuery(); } for (int i = 0; i < 100; i++) { using (SQLiteCommand cmd = cnn.CreateCommand()) { cmd.CommandText = "INSERT INTO Data (text) VALUES (@text);"; cmd.Parameters.AddWithValue("@text", new string((char)(65 + i), i * 100)); cmd.ExecuteNonQuery(); } } trans.Commit(); } } } for (int i = 0; i < 50; i++) { new DbLockTest().RunImpl(); Thread.Sleep(1); Console.WriteLine(); Console.WriteLine(); Console.WriteLine(); } Console.WriteLine("Done. Hit any key."); Console.ReadKey(); } public void RunImpl() { _timer = Stopwatch.StartNew(); Console.WriteLine("{0:0,000} - MAIN - Queuing threads", _timer.ElapsedMilliseconds); ThreadPool.QueueUserWorkItem(RunRead, _random.Next(15)); ThreadPool.QueueUserWorkItem(RunWrite, _random.Next(15)); Thread.Sleep(100); Console.WriteLine("{0:0,000} - MAIN - Signaling threads", _timer.ElapsedMilliseconds); _start.Set(); _readDone.WaitOne(); Console.WriteLine("{0:0,000} - MAIN - Read done received", _timer.ElapsedMilliseconds); _writeDone.WaitOne(); Console.WriteLine("{0:0,000} - MAIN - Write done received", _timer.ElapsedMilliseconds); } private void RunRead(object state) { try { Console.WriteLine("{0:0,000} - READ - Waiting for signal", _timer.ElapsedMilliseconds); _start.WaitOne(); /* int wait = (int) state; Console.WriteLine("{0:0,000} - READ - Sleeping {1} ms", _timer.ElapsedMilliseconds, wait); Thread.Sleep(wait); */ IDataReader reader; Console.WriteLine("{0:0,000} - READ - Opening connection", _timer.ElapsedMilliseconds); SQLiteConnection cnn = CreateConnection(); using(SQLiteCommand cmd = cnn.CreateCommand()) { cmd.CommandText = "SELECT * FROM Data"; Console.WriteLine("{0:0,000} - READ - Getting reader", _timer.ElapsedMilliseconds); reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); } Console.WriteLine("{0:0,000} - READ - Looping through data", _timer.ElapsedMilliseconds); int i = 0; while (reader.Read()) { if (i++ % 10 == 0) { Thread.Sleep(1); } } Console.WriteLine("{0:0,000} - READ - Closing reader (and thus connection)", _timer.ElapsedMilliseconds); reader.Close(); Console.WriteLine("{0:0,000} - READ - Signaling done", _timer.ElapsedMilliseconds); } catch (Exception ex) { Console.WriteLine("{0:0,000} - READ - ERROR\r\n\r\n" + ex, _timer.ElapsedMilliseconds); } _readDone.Set(); } private void RunWrite(object state) { try { Console.WriteLine("{0:0,000} - WRITE - Waiting for signal", _timer.ElapsedMilliseconds); _start.WaitOne(); /* int wait = (int)state; Console.WriteLine("{0:0,000} - WRITE - Sleeping {1} ms", _timer.ElapsedMilliseconds, wait); Thread.Sleep(wait); */ Console.WriteLine("{0:0,000} - WRITE - Opening connection", _timer.ElapsedMilliseconds); using (SQLiteConnection cnn = CreateConnection()) { Console.WriteLine("{0:0,000} - WRITE - Starting transaction", _timer.ElapsedMilliseconds); using (SQLiteTransaction trans = cnn.BeginTransaction()) { Console.WriteLine("{0:0,000} - WRITE - Updating data", _timer.ElapsedMilliseconds); for (int i = 0; i < 10; i++) { using (SQLiteCommand cmd = cnn.CreateCommand()) { cmd.CommandText = "UPDATE Data SET text = " + i; cmd.ExecuteNonQuery(); } } Console.WriteLine("{0:0,000} - WRITE - Committing transaction", _timer.ElapsedMilliseconds); trans.Commit(); } Console.WriteLine("{0:0,000} - WRITE - Closing connection", _timer.ElapsedMilliseconds); } Console.WriteLine("{0:0,000} - WRITE - Signaling done", _timer.ElapsedMilliseconds); } catch (Exception ex) { Console.WriteLine("{0:0,000} - WRITE - ERROR\r\n\r\n" + ex, _timer.ElapsedMilliseconds); } _writeDone.Set(); } private static SQLiteConnection CreateConnection() { SQLiteConnection cnn = new SQLiteConnection("Data Source=DbLockTest.dat"); cnn.Open(); return cnn; } } } -- ----------------------------------------------------------------- We're Hiring! Seeking passionate Flex, C#, or C++ (RTSP, H264) developer. Position is in the Washington D.C. metro area. Contact [EMAIL PROTECTED] _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users