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 函数。

来自SQLite C Interface

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