Quantcast
Channel: Recent Threads — Xamarin Community Forums
Viewing all articles
Browse latest Browse all 204402

SQLite.NET, background thread and application crashing.

$
0
0

Hello,

I have spent a fair amount of time and I keep failing when it comes to prevent my app from crashing due to heavy use of SQLite.NET.

This is the scenario:

My app is supposed to work both connected and disconnected. When disconnected, the app uses a outbox service to queue the http operations against my rest endpoints. So every minute or when the background thread is signaled, the app tries processing that queue and process the messages.

The skeleton of the outbox service in [1].

The code that process the queue runs in a background thread that checks every minute for items to process, or if it is signaled when an item is queued up.

The underline db is SQLite, and the problem I have is the app crash -IMHO- when both the main thread and the background thread try to access the same db.

The main thread might be trying to queue something while the background thread is trying to read/write from the db.

What is the recommended pattern? I'm sharing a single connection per db, but this is obviously failing. Any ideas?

Thanks, R.

[1] Outbox service concept...

    public static class QueueService
    {
        //private
        static readonly AutoResetEvent _itemAdded;
        static bool _stopping;
        static bool _started;
        static readonly SQLiteConnection _connection;
        static readonly object _padlock = new object ();
        //queues
        static readonly Data.Queue<EntityA> _entityAQueue;
                //... as many other queues as needed
        static readonly Data.Queue<Breadcrumb> _breadcrumbQueue;

        static QueueService ()
        {
            //make it thread safe
            lock (_padlock) {
                AppLogger.WriteLine ("Initializing Queue Service");
                _itemAdded = new AutoResetEvent (false);
                _connection = SetupConnection ();
                _entityAQueue = new Data.Queue<EntityA> (_connection);
                _breadcrumbQueue = new Data.Queue<Breadcrumb> (_connection);
            }
        }

        public static void Start ()
        {
            if (!_started) {
                AppLogger.WriteLine ("Starting Queue Service");

                Task.Factory.StartNew (Run, TaskCreationOptions.LongRunning);

                _started = true;
            }
        }

        public static void Stop ()
        {
            AppLogger.WriteLine ("Stopping Queue Service");
            _stopping = true;
        }

        public static void QueueEntityA (Queueable<EntityA> queueableEntityA)
        {
            lock (_padlock) {
                bool queued = false;
                int attemps = 0;
                while (queued == false && attemps < 10) {
                    try {
                        _entityAQueue.Enqueue (queueableEntityA);
                        queued = true;
                        _itemAdded.Set ();
                        AppLogger.WriteLine ("Signaling EntityA Queue for operation");
                    } catch (Exception ex) {
                        AppLogger.WriteLine ("Error queueing entity. Error: " + ex.Message, true);
                        AppLogger.WriteLine ("Attemp #" + attemps);
                    } finally {
                        attemps++;
                    }
                }
            }
        }

        public static void QueueBreadcrumb (Queueable<Breadcrumb> queueableBreadcrumb)
        {
            lock (_padlock) {
                try {
                    _breadcrumbQueue.Enqueue (queueableBreadcrumb, consolidate: false);
                    _itemAdded.Set ();
                    AppLogger.WriteLine ("Signaling Breadcrumb Queue for breadcrumb operation");
                } catch (Exception ex) {
                    AppLogger.WriteLine ("Error queueing breadcrumb. Error: " + ex.Message, true);
                }
            }
        }

        #region private methods

        static void Run ()
        {
            while (!_stopping) {
                Process (_entityAQueue);
                Process (_breadcrumbQueue);

                //do I have the certainty here that there are no more messages in the queue?
                _itemAdded.WaitOne (TimeSpan.FromMinutes (1));
                AppLogger.WriteLine ("Resume queue processing");
            }
        }

        static void Process<T> (Data.Queue<T> queue)
            where T:IIdable, IStampable, new()
        {
            lock (_padlock) {
                try {
                    Queueable<T> item;
                    while ((item = queue.Dequeue ()) != null) {
                        try {
                            switch (item.Verb) {
                            case eHttpVerb.Post:
                                Post (item);
                                break;
                            case eHttpVerb.Put:
                                Put (item);
                                break;
                            case eHttpVerb.Delete:
                                Delete (item);
                                break;
                            default:
                                throw new Exception ("Unknown verb. I don't know what to do here.");
                            }

                            //remove if succeed
                            queue.Delete (item);
                        } catch (Exception ex) {
                            AppLogger.WriteLine ("Error processing queue: " + ex.Message, true);
                            //save for retry...
                            queue.SaveForRetry (item);
                        }
                    }
                } catch (Exception ex) {
                    AppLogger.WriteLine ("Queue Error: " + ex.Message, true);
                }
            }
        }

        static SQLiteConnection SetupConnection ()
        {
            var path = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), Global.QueuesDbName);
            var connection = new SQLiteConnection (path, true);
            connection.EnableWAL ();

            connection.CreateTable<Queueable<EntityA>> ();
            connection.CreateTable<Queueable<Breadcrumb>> ();

            return connection;
        }

        #endregion
    }

Viewing all articles
Browse latest Browse all 204402

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>