Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct usage for sqlite on locking or async

We are using Xamarin to write C# code with SQLite for android and ios. However about how to use sqlite, I seem to have a conceptual misunderstanding:

What are the best practices for SQLite on Android?

According to the stackoverflow answer it says - one helper and one db connection. Use lock around it to make sure only one thread is accessing sqlite db at any time.

My question is - if that is the case - what is the use for async?

I tried to use async with synchronized code - and the code gave me compile errors rightfully to avoid deadlocks.
Why can't I use the 'await' operator within the body of a lock statement?

    public async Task InsertAsync<T> (T item){
        lock (mutex) {
            await asyncConnection.InsertAsync (item);
        }
    }

    public async Task InsertOrUpdateAsync<T> (T item){
        lock (mutex) {
            int count = await asyncConnection.UpdateAsync (item);
            if (0 == count) {
                await asyncConnection.InsertAsync (item);
            }
        }
    }

fails. However - if I am going to use locks to ensure I am using one connection one thread at a time as a best practice in sqlite - why is there an async sqlite library at all?

And why are some threads promoting async sqlite usage on the web.

What is the real best practice for sqlite in android and iphone? Just using sync version?

like image 854
user3141326 Avatar asked Jun 12 '14 14:06

user3141326


People also ask

Why SQLite database is locked Code 5?

As the new file isn’t locked up, so the SQLite Database is locked code 5 is no longer there. Whenever we attempt to make the database handle concurrency more than the default configuration, we face an error. In this case, rewriting a more efficient version of the code statement can help solve the error and unlock the database.

What is a lock time out in SQLite?

If a transaction takes more time to execute, it can prevent other threads from accessing the database, thus creating a deadlock. (A scenario in which each thread is waiting for a resource while holding a resource needed by the other thread) To resolve this issue, SQLite has a lock time out.

How SQLite works to maximize concurrency?

In order to maximize concurrency, SQLite works to minimize the amount of time that EXCLUSIVE locks are held. The operating system interface layer understands and tracks all five locking states described above.

What is SQLite used for?

Appropriate Uses For SQLite. SQLite is not directly comparable to client/server SQL database engines such as MySQL, Oracle, PostgreSQL, or SQL Server since SQLite is trying to solve a different problem. Client/server SQL database engines strive to implement a shared repository of enterprise data.


2 Answers

There's a big difference between synchronous vs asynchronous and single vs concurrent and there are all 4 combinations of them.

In the single asynchronous case you access the DB using a single thread at most, but it doesn't need to be the same thread throughout the operation, and when you don't need a thread (when you are waiting for the IO operation to complete) you don't need any threads at all.

The most basic way to limit the async usage to a single operation at a time is by using a SemaphoreSlim with initialCount = 1. A nicer way would be to use an AsyncLock (Building Async Coordination Primitives, Part 6: AsyncLock by Stephen Toub):

private readonly AsyncLock _lock = new AsyncLock(); 

public async Task InsertAsync<T> (T item)
{
    using(await _lock.LockAsync())
    {
        await asyncConnection.InsertAsync (item);
    }
}

public async Task InsertOrUpdateAsync<T> (T item)
{
    using(await _lock.LockAsync())
    {
        if (0 == await asyncConnection.UpdateAsync (item))
        {
            await asyncConnection.InsertAsync (item);
        }
    }
}

Note: My implementation of AsyncLock

like image 98
i3arnon Avatar answered Oct 06 '22 01:10

i3arnon


The fact that your Database doesn't accept multiple accesses (insertions, updates, etc..) doesn't mean that the single thread doing work against it has to do so using a blocking api.

If you don't have to do cross-process locking, you can use SemaphoreSlim.WaitAsync instead of your Mutex inside your async method to await the lock asynchrnously:

private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(initialCount: 1);

public async Task InsertAsync<T>(T item)
{
   await semaphoreSlim.WaitAsync();
   try
   {
      await asyncConnection.InsertAsync(item);
   }
   finally
   { 
      semaphoreSlim.Release();
   }
}

public async Task InsertOrUpdateAsync<T>(T item)
{
   await semaphoreSlim.WaitAsync();
   try
   {      
      int count = await asyncConnection.UpdateAsync(item);
      if (0 == count) 
      {
         await asyncConnection.InsertAsync(item);
      }
   }
   finally
   {
      semaphoreSlim.Release();
   }
}
like image 38
Yuval Itzchakov Avatar answered Oct 05 '22 23:10

Yuval Itzchakov