Pregunta System.Data.SQLite no admite múltiples transacciones


Así que estoy teniendo un problema interesante con System.Data.SQLite y estoy usando múltiples transacciones. Básicamente tengo el siguiente código que falla:

using (IDbConnection connection1 = new SQLiteConnection("connectionstring"), connection2 = new SQLiteConnection("connectionstring"))
{
    connection1.Open();
    connection2.Open();

    IDbTransaction transaction1 = connection1.BeginTransaction();
    IDbTransaction transaction2 = connection2.BeginTransaction();    // Fails!

    using(IDbCommand command = new SQLiteCommand())
    {
        command.Text = "CREATE TABLE artist(artistid int, artistname text);";
        command.CommandType = CommandType.Text;
        command.Connection = connection1;
        command.ExecuteNonQuery();
    }

    using (IDbCommand command = new SQLiteCommand())
    {
        command.Text = "CREATE TABLE track(trackid int, trackname text);";
        command.CommandType = CommandType.Text;
        command.Connection = connection2;                    
        command.ExecuteNonQuery();
    }

    transaction1.Commit();
    transaction2.Commit();

}

Por lo que he leído, parece que System.Data.SQLite debe admitir transacciones secuenciales anidadas y por extensión. El código falla en la línea 7 (donde se declara la segunda transacción) con la siguiente excepción:

System.Data.SQLite.SQLiteException: The database file is locked

System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
System.Data.SQLite.SQLiteDataReader.NextResult()
System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock)
System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction()

¿Alguien sabe cuál es el problema o cómo evitarlo? Siento que tener transacciones simultáneas es esencial para cualquier sistema de base de datos, por lo que debe haber alguna forma de hacerlo.

¡Gracias!


14
2017-07-19 14:57


origen


Respuestas:


OP está iniciando transacciones en 2 conexiones, ahí es donde comienzan los problemas, no múltiples transacciones per se.

SQLiteConnection conn = new SQLiteConnection("data source=:memory:");
conn.Open();

var command = conn.CreateCommand();
command.CommandText = "create table a (b integer primary key autoincrement, c text)";
command.ExecuteNonQuery();

var tran1 = conn.BeginTransaction();
var tran2 = conn.BeginTransaction();

var command1 = conn.CreateCommand();
var command2 = conn.CreateCommand();

command1.Transaction = tran1;
command2.Transaction = tran2;

command1.CommandText = "insert into a VALUES (NULL, 'bla1')";
command2.CommandText = "insert into a VALUES (NULL, 'bla2')";

command1.ExecuteNonQuery();
command2.ExecuteNonQuery();

tran1.Commit();
tran2.Commit();

command.CommandText = "select count(*) from a";
Console.WriteLine(command.ExecuteScalar());

14
2017-07-19 15:23



SQLite está diseñado como una base de datos liviana para cosas como los marcadores en un navegador o las fotos en un programa de catálogo de fotos. SQLite tiene un sistema de bloqueo muy granular que está optimizado para escenarios con muchos lectores o un único hilo lector / escritor. Como tal, se sabe que tiene problemas de rendimiento con escrituras concurrentes - simplemente no está diseñado para su uso en aplicaciones con muchos escritores concurent. Usted puede hacer escrituras concurrentes, pero no escalará bien.

En este caso, el problema se debe a que está intentando realizar cambios de esquema concurrentes, si en cambio hizo múltiples SELECT o múltiple INSERT declaraciones, entonces esto será trabajar con éxito (como lo muestra la respuesta de sixfeetsix).

Si su aplicación tiene muchos lectores y no muchos escritores, entonces SQLite bien puede estar bien, sin embargo, si tiene una aplicación con muchos lectores simultáneos y escritores entonces puede encontrar que un servidor de base de datos completamente desarrollado es más adecuado.

Ver Bloqueo de archivos y concurrencia en SQLite versión 3 para más detalles.


3
2017-07-19 15:06



Tratar de usar:

((SQLiteConnection)connection).BeginTransaction(true)-

El parámetro bool habla sobre el bloqueo diferido. Pero recuerde que se debe llamar a ExecuteNonScalar solo después de que se haya comprometido la primera transacción.


1
2018-01-03 09:16



Por lo que vale, no es compatible con múltiples transacciones por conexión única parece ser común para los proveedores de datos (sé con certeza ODP.NET, probablemente otros).

Una forma de evitarlo es tener un IDbConnection para cada uno por separado IDbTransaction.

- EDITAR ---

Doh! Me acabo de dar cuenta de que hacer tener múltiples conexiones. Lo siento.


-1
2017-07-19 15:04



SQLite no admite transacciones múltiples: bloquea la base de datos completa cuando se realiza una transacción (consulte www.sqlite.org).

EDITAR: Se admiten varias transacciones, pero no cuando se usa DDL en más de una transacción.


-1
2017-07-19 15:04