SQLiteConnection.BeginTransaction 是否在数据库锁定时重试?
Does SQLiteConnection.BeginTransaction retry when database is locked?
我在多线程 Windows 桌面程序中使用 SQLite (System.Data.SQLite.dll)。我使用此代码:
using (var cn = new SQLiteConnection(connectionString))
{
cn.Open();
using (var tx = cn.BeginTransaction())
{
// do some work here
tx.Commit();
}
}
我强调程序使用 8 个并发线程同时写入数据库。有时我会在 Visual Studio 输出 window:
上看到这条消息
SQLite error (517): statement aborts at 1: [BEGIN IMMEDIATE] database is locked
但完全没有抛出异常,一切都按预期进行。所以我猜 SQLConnection.BeginTrasaction() 在收到 SQLite 错误 (517) 数据库被锁定时重试。
我说得对吗?
如果是,SQLConnection.BeginTransaction 在抛出异常之前重试了多少次。这是可配置的吗?
这由BUSY TIMEOUT处理:
PRAGMA busy_timeout;
PRAGMA busy_timeout = milliseconds;
Query or change the setting of the busy timeout. This pragma is an
alternative to the sqlite3_busy_timeout() C-language interface which
is made available as a pragma for use with language bindings that do
not provide direct access to sqlite3_busy_timeout().
Each database connection can only have a single busy handler. This
PRAGMA sets the busy handler for the process, possibly overwriting any
previously set busy handler.
超时间隔在SQLite.dll
中默认定义为30秒。原来你不能改变它,只能通过调用不安全的 C 函数。
int sqlite3_busy_timeout(sqlite3*, int ms);
Calling this routine with an argument less than or equal to zero turns off all busy handlers.
我终于找到了System.Data.SQLite.dll的源代码。
SQLite3.Prepare 方法(在执行任何命令之前调用)包含以下代码:
while ((n == SQLiteErrorCode.Schema || n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) && retries < 3)
{
try
...
因此,System.Data.SQLite 将在抛出异常之前重试任何命令最多 3 次。
编辑:
SQLite3.Prepare 方法有一段不言自明的有趣代码:
else if (n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) // Locked -- delay a small amount before retrying
{
// Keep trying
if (rnd == null) // First time we've encountered the lock
rnd = new Random();
// If we've exceeded the command's timeout, give up and throw an error
if ((uint)Environment.TickCount - starttick > timeoutMS)
{
throw new SQLiteException(n, GetLastError());
}
else
{
// Otherwise sleep for a random amount of time up to 150ms
System.Threading.Thread.Sleep(rnd.Next(1, 150));
}
}
请注意,锁定和繁忙错误不会增加重试计数器,因此 Prepare 方法将继续重试,直到命令超时到期。
我创建了一张票,要求配置最大重试次数:
Configurable number of retries and sleep time of SQLite3.Prepare
我在多线程 Windows 桌面程序中使用 SQLite (System.Data.SQLite.dll)。我使用此代码:
using (var cn = new SQLiteConnection(connectionString))
{
cn.Open();
using (var tx = cn.BeginTransaction())
{
// do some work here
tx.Commit();
}
}
我强调程序使用 8 个并发线程同时写入数据库。有时我会在 Visual Studio 输出 window:
上看到这条消息SQLite error (517): statement aborts at 1: [BEGIN IMMEDIATE] database is locked
但完全没有抛出异常,一切都按预期进行。所以我猜 SQLConnection.BeginTrasaction() 在收到 SQLite 错误 (517) 数据库被锁定时重试。
我说得对吗?
如果是,SQLConnection.BeginTransaction 在抛出异常之前重试了多少次。这是可配置的吗?
这由BUSY TIMEOUT处理:
PRAGMA busy_timeout;
PRAGMA busy_timeout = milliseconds;Query or change the setting of the busy timeout. This pragma is an alternative to the sqlite3_busy_timeout() C-language interface which is made available as a pragma for use with language bindings that do not provide direct access to sqlite3_busy_timeout().
Each database connection can only have a single busy handler. This PRAGMA sets the busy handler for the process, possibly overwriting any previously set busy handler.
超时间隔在SQLite.dll
中默认定义为30秒。原来你不能改变它,只能通过调用不安全的 C 函数。
int sqlite3_busy_timeout(sqlite3*, int ms);
Calling this routine with an argument less than or equal to zero turns off all busy handlers.
我终于找到了System.Data.SQLite.dll的源代码。
SQLite3.Prepare 方法(在执行任何命令之前调用)包含以下代码:
while ((n == SQLiteErrorCode.Schema || n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) && retries < 3)
{
try
...
因此,System.Data.SQLite 将在抛出异常之前重试任何命令最多 3 次。
编辑:
SQLite3.Prepare 方法有一段不言自明的有趣代码:
else if (n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) // Locked -- delay a small amount before retrying
{
// Keep trying
if (rnd == null) // First time we've encountered the lock
rnd = new Random();
// If we've exceeded the command's timeout, give up and throw an error
if ((uint)Environment.TickCount - starttick > timeoutMS)
{
throw new SQLiteException(n, GetLastError());
}
else
{
// Otherwise sleep for a random amount of time up to 150ms
System.Threading.Thread.Sleep(rnd.Next(1, 150));
}
}
请注意,锁定和繁忙错误不会增加重试计数器,因此 Prepare 方法将继续重试,直到命令超时到期。
我创建了一张票,要求配置最大重试次数:
Configurable number of retries and sleep time of SQLite3.Prepare