Is it necessary to lock simultaneous SQLite access for SELECT statements? - ios

I am using FMDB to access the standard iOS internal SQLite database, with one db connection shared among multiple threads.
To make it thread safe I'm locking access to the db to one block of code at a time. All works well, although the access to the db is now a bit of a bottleneck, obviously.
My question is: Can I ease this up a bit by allowing simultaneous queries from multiple threads, as long as they are all readonly SELECT statements?
I can't find an answer anywhere.

You cannot use the same connection to execute multiple queries at the same time.
However, for purely read-only accesses, you can use multiple connections.

You can have one FMDatabase object for each thread. You might have to write code to test for genuine busy conditions and handle them properly. For example set busyRetryTimeout appropriate for your situation (e.g. how long do you want it to retry in contention situations). Also gracefully handle if the timeout expires and your database query fails.
Clearly, using a shared FMDatabaseQueue is the easiest way to do database interactions from multiple threads. See the Using FMDatabaseQueue and Thread Safety section of the FMDB README.

Related

Can statement prepared with sqlite3_prepare_v2 be used with a different connection?

I am using multiple read-only database connections to the same database within the same process (from different threads). However, I have static sqlte3_stmt statements that I use with the passed database connection.
Is it safe to re-use statements prepared this way with different database connection instances (to the same read-only database)? In practice this seems to work, but I'm not sure if this is the right thing to do.
A statement always uses the connection that it was prepared for (in the call to sqlite3_prepare*(). It is not possible to change a statement's connection.
When you use the same statement from multiple threads, all threads share the same cursor, and the same connection, and the same transaction. This will blow up eventually.
If you want to save statements, you must use a list of statements per connection.

FMDB + SQLCipher Performance issue

We are making use of FMDB SQLCipher wrapper for handling the encryption of sqlite DB. We are having a single FMDataBaseQueue to handle read and write from multiple threads. Since the time we introduced SQLCipher , the application performance has taken a hit. On every transaction, we are setting the key.
self.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback){
[db setKey:"dummykey"];
//Do Some DataBase Updates or Queries
}
So, we had to abstract out all the methods since it has been used by multiple controllers. How can we address this issue? And Sometimes we have to perform this main queue(Most of the time we do it in Global queue) since we wait for some processing to complete.
How can I improve the performance of the execution of sqlite statements since I feel that encrypting DB is a costly operation.
The DBConnection is opened throughout the lifecycle of app. Should I use [db setKey:"dummykey"];every time I want to interact with the DB ?
Please review the SQLCipher performance guidance in comparison to your application usage.

Multi-thread daata access issue, #synchronized & serial queue

As you may have experienced, access none-thread safe variables is a big headache. For iOS one simple solution is to use keyword #synchronized, which will add NSLock to insure the data can be accessed by unique one thread, the disadvantage is as below:
Lock too many will reduce app performance greatly, especially when invoked by main thread.
Dead lock will occur when logic becomes complex.
Based on the above considerations, we prefer to use serial queue to handle, each thread safe critical operation will append to the end of the queue, it is a great solution, but the problem is that all access interfaces should by designed in asyn style, see the following one.
-(id)objectForKey:(NSString *)key;
The people who invoke this class aren't reluctant to design in this way. Anyone who has experience on this field please share and discuss together.
The final solution is using NSUserDefault to store small data, for large cache data put them in file maintained by ourselves.
Per Apple doc the advantage of NSUserDefault is thread safe and will do synchronize work periodically.

Sqlite3 Concurrency Issue in IOS

I am using SQLite 3 in my iOS application. It requires lots of read/write operation and many of them attempted concurrently. Most of these functions are running in separate threads, however accessing same database.
Here are few methods I have thought of:
At every database access ( read/write ) I put a flag as database_open=YES, and if it is found true I retry again in few seconds. I can put the function at the database layer itself.
Run all database operations in same thread. However, my use case is such that I need to wait for some HTTP calls to finish and store the retreived data into SQLite. For this method, I will have to make all these server calls as well synchronous. Not the best idea.
Please suggest if I am thinking in wrong direction.
SQLite supports concurrent access from multiple threads. Simply configure the SQLITE_CONFIG_MULTITHREAD mode, and separately open the database from each of the threads.

Interprocess SQLite Thread Safety (on iOS)

I'm trying to determine if my sqlite access to a database is thread-safe on iOS. I'm writing a non App Store app (or possibly a launch daemon), so Apple's approval isn't an issue. The database in question is the built-in sms.db, so for sure the OS is also accessing this database for reading and writing. I only want to be able to safely read it.
I've read this about reading from multiple processes with sqlite:
Multiple processes can have the same database open at the same time.
Multiple processes can be doing a SELECT at the same time. But only
one process can be making changes to the database at any moment in
time, however.
I understand that thread-safety can be compiled out of sqlite, and that sqlite3_threadsafe() can be used to test for this. Running this on iOS 5.0.1
int safe = sqlite3_threadsafe();
yields a result of 2. According to this, that means mutex locking is available. But, that doesn't necessarily mean it's in use.
I'm not entirely clear on whether thread-safety is dynamically enabled on a per connection, per database, or global basis.
I have also read this. It looks like sqlite3_config() can be used to enable safe multi-threading, but of course, I have no control, or visibility into how the OS itself may have used this call (do I?). If I were to make that call again in my app, would it make it safe to read the database, or would it only deconflict concurrent access for multiple threads in my app that used the same sqlite3 database handle?
Anyway, my question is ...
can I safely read this database that's also accessed by iOS, and if so, how?
I've never used SQLite, but I've spent a decent amount of time reading its docs because I plan on using it in the future (and the docs are interesting). I'd say that thread safety is independent of whether multiple processes can access the same database file at once. SQLite, regardless of what threading mode it is in, will lock the database file, so that multiple processes can read from the database at once but only one can write.
Thread safety only affects how your process can use SQLite. Without any thread safety, you can only call SQLite functions from one thread. But it should still, say, take an EXCLUSIVE lock before writing, so that other processes can't corrupt the database file. Thread safety just protects data in your process's memory from getting corrupted if you use multiple threads. So I don't think you ever need to worry about what another process (in this case iOS) is doing with an SQLite database.
Edit: To clarify, any time you write to the database, including a plain INSERT/UPDATE/DELETE, it will automatically take an EXCLUSIVE lock, write to the database, then release the lock. (And it actually takes a SHARED lock, then a RESERVED lock, then a PENDING lock, then an EXCLUSIVE lock before writing.) By default, if the database is already locked (say from another process), then SQLite will return SQLITE_BUSY without waiting. You can call sqlite3_busy_timeout() to tell it to wait longer.
I don't think any of this is news to you, but a few thoughts:
In terms of enabling multi-threading (either serialized or multi-threaded), the general counsel is that one can invoke sqlite3_config() (but you may have to do a shutdown first as suggested in the docs or as discussed on SO here) to enable the sort of multi-threading you want. That may be of diminished usefulness here, though, where you have no control over what sort of access iOS is requesting of sqlite and/or this database.
Thus, I would have thought that, from an academic perspective, it would not be safe to read this system database (because as you say, you have no assurance of what the OS is doing). But I wouldn't be surprised if iOS is opening the database using whatever the default mode is, so from a more pragmatic perspective, you might be fine.
Clearly, for most users concerned about multi-threaded access within a single app, the best counsel would be to bypass the sqlite3_config() silliness and just simply ensure coordinated access through your own GCD serial queue (i.e., have a dedicated queue through which all database interactions go through, gracefully eliminating the multi-thread issue altogether). Sadly, that's not an option here because you're trying to coordinate database interaction with iOS itself.

Resources