Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLite AccessViolationException in WCF service

We have a .NET Windows Service exposing a WCF service to an user-interface and other parts of our system. It targets .NET Framework 4.5 and uses SQLite 1.0.92 binaries to talk to the underlying SQLite database. However, the windows service crashes (automatically stopped) after running for some time with an AccessViolationException (found via Windows Event Viewer) in SQLite.Interop.dll. I have come across articles that talks about this exception in Connection close, but in all our cases we encounter this exception while querying or writing to the DB using the methods exposed by our WCF service. The stack-trace is as follows:

Application: OurServer.exe
Framework Version: **v4.0.30319**
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
   at System.Data.SQLite.UnsafeNativeMethods.sqlite3_bind_int(IntPtr, Int32, Int32)
   at System.Data.SQLite.UnsafeNativeMethods.sqlite3_bind_int(IntPtr, Int32, Int32)
   at System.Data.SQLite.SQLite3.Bind_Int32(System.Data.SQLite.SQLiteStatement, System.Data.SQLite.SQLiteConnectionFlags, Int32, Int32)
   at System.Data.SQLite.SQLiteStatement.BindParameter(Int32, System.Data.SQLite.SQLiteParameter)
   at System.Data.SQLite.SQLiteStatement.BindParameters()
   at System.Data.SQLite.SQLiteCommand.GetStatement(Int32)
   at System.Data.SQLite.SQLiteDataReader.NextResult()
   at System.Data.SQLite.SQLiteDataReader..ctor(System.Data.SQLite.SQLiteCommand, System.Data.CommandBehavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteReader(System.Data.CommandBehavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(System.Data.CommandBehavior)
   at DataAccess.Sqlite.ExecuteCommand(System.Collections.ObjectModel.Collection`1<System.String>, System.Collections.ObjectModel.Collection`1<System.Data.Common.DbParameter[]>)
   at Data.Settings.Save(System.Collections.ObjectModel.Collection`1<Common.Operation>)
   at DynamicClass.SyncInvokeSaveOperation(System.Object, System.Object[], System.Object[])
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(System.ServiceModel.Dispatcher.MessageRpc ByRef)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean)
   at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult)
   at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult)
   at System.Runtime.AsyncResult.Complete(Boolean)
   at System.Runtime.InputQueue`1+AsyncQueueReader[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Set(Item<System.__Canon>)
   at System.Runtime.InputQueue`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].EnqueueAndDispatch(Item<System.__Canon>, Boolean)
   at System.Runtime.InputQueue`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].EnqueueAndDispatch(System.__Canon, System.Action, Boolean)
   at System.ServiceModel.Channels.SingletonChannelAcceptor`3[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Enqueue(System.__Canon, System.Action, Boolean)
   at System.ServiceModel.Channels.ConnectionDemuxer+CompleteSingletonPreambleAndDispatchRequestAsyncResult.OnPreambleComplete(System.IAsyncResult)
   at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult)
   at System.Runtime.AsyncResult.Complete(Boolean)
   at System.ServiceModel.Channels.ServerSingletonPreambleConnectionReader+CompletePreambleAsyncResult.OnWriteCompleted(System.Object)
   at System.ServiceModel.Channels.SocketConnection.OnSendAsync(System.Object, System.Net.Sockets.SocketAsyncEventArgs)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(System.Net.Sockets.SocketError, Int32, System.Net.Sockets.SocketFlags)
   at System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)

We are using the SQLite assemblies from "sqlite-netFx45-binary-bundle-Win32-2012-1.0.92.0" (downloaded from http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki). The assemblies are bundled as part of the Windows Service and are not in GAC. This behavior is consistent in both 32-bit and 64-bit machines. FYI, we are NOT using the mixed mode assemblies.

Our connection string:

Data Source=ourapp.db;Version=3;New=False;Compress=True;PRAGMA cache_size=20000; PRAGMA page_size=32768; PRAGMA synchronous=off"

and the SQLite database file is the Windows "ProgramData" folder.

The stacktrace shows the Framework version as "v4.0.30319" while we have explicitly set the target version to 4.5 in our service's application config. However, the machine has both the versions installed.

Also, I wrote a simple console application that invokes the same WCF service method from multiple threads, but could not simulate the same AccessViolationException. Hence, I don't think it could be a load or concurrent access related issue. The exception seems random and we have no way of consistently re-producing the issue other than just running the service and waiting for it to happen.

Any pointers to what could be causing this issue is greatly appreciated.

UPDATE:

Code for the two variants of ExecuteCommand being used:

    public int ExecuteCommand(string query, params DbParameter[] parameters)
    {
        try
        {
            this.result = -1;
            this.OpenConnection();
            this.command = new SQLiteCommand(query, this.connnection);
            this.HandleParameters(parameters);
            this.result = this.command.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            this.result = -1;
        }
        finally
        {
            if (this.command != null)
            {
                this.command.Dispose();
            }

            this.CloseConnection();
        }

        return this.result;
    }


    public int ExecuteCommand(Collection<string> queries, Collection<DbParameter[]> parameters)
    {
        try
        {
            this.result = -1;
            this.OpenConnection();
            this.command = new SQLiteCommand();
            this.command.Connection = this.connnection;
            this.transaction = this.connnection.BeginTransaction();

            for (int i = 0; i < queries.Count; i++)
            {
                this.command.Parameters.Clear();
                this.command.CommandText = queries[i];
                this.command.CommandTimeout = this.timeOut;
                this.command.Transaction = this.transaction;

                DbParameter[] cmdParams = new DbParameter[] { };
                if (parameters != null)
                {
                    cmdParams = parameters[i];
                }

                this.HandleParameters(cmdParams);
                this.result += this.command.ExecuteNonQuery();
            }

            this.transaction.Commit();
        }
        catch (Exception ex)
        {
            if (this.transaction != null)
            {
                this.transaction.Rollback();
            }

            this.result = -1;
        }
        finally
        {
            if (this.command != null)
            {
                this.command.Dispose();
            }

            this.CloseConnection();
        }

        return this.result;
    }

UPDATE 2: Code for Save method

    Collection<DbParameter[]> dbparameters = new Collection<DbParameter[]>();
    DbParameter[] dbparams;
    SQLiteParameter sqlparams;
    Collection<string> queries = new Collection<string>();
    int icount = 0;

    foreach (Operation operation in operations)
    {
        icount = 0;
        dbparams = new DbParameter[4];

        queries.Add("UPDATE table1 SET col1 = @Col1, col2 = @col2, " +
                    "Timestamp = @Timestamp WHERE Id = @Id");

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Timestamp";
        sqlparams.Value = string.Format(CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss}", operation.Timestamp);
        dbparams[icount++] = sqlparams;

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Id";
        sqlparams.Value = operation.Id;
        dbparams[icount++] = sqlparams;

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Col1";
        sqlparams.Value = operation.Col1;
        dbparams[icount++] = sqlparams;

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Col2";
        sqlparams.Value = operation.Col2;
        dbparams[icount++] = sqlparams;


        dbparameters.Add(dbparams);
    }


    return (DataAccess.Sqlite.ExecuteCommand(queries, dbparameters) > -1);
like image 705
Ananth Avatar asked May 20 '14 07:05

Ananth


2 Answers

The error is consistent With the provider loosing track of which Connections are open:

  • Trying to Close a Connection that does not exist
  • Trying to use a Connection that does not exist

The error looks like a problem with Connection pooling, but Your Connection string does not use Connection pooling and it is turned off by default.

Does anything else Access the same database With Connection pooling turned on?

The first thing you must do is to log the exception.

like image 187
Shiraz Bhaiji Avatar answered Sep 22 '22 19:09

Shiraz Bhaiji


Your class may not be thread safe. Check that multiple thread does not call the Save method at the same time.

Also make sure you implement IDisposable to dispose of your SQLiteConnection object if it is a private member of your class.

like image 25
Tien Dinh Avatar answered Sep 23 '22 19:09

Tien Dinh