Fatal error: Database methods are not reentrant

I have looked at the examples of how to prevent this error, but the examples don't work without a significant lot of opening and closing database reads.

I have a routine that checks whether a stock code exists which obviously uses the database. The routine is called from a formatter from getObjectValue( for string: String, errorDescription error: ) -> Bool and this the formatter is called also when anything is written to that field. There is no way I can see of altering getObjectValue to get passed a database parameter. This means that if I read a database and fill in a stock code, I have to close the database read, write the stock code and then re-open it again which is very messy. And I cannot remove the validation, as I want to be able to alter the stock code.

Hello @newtimber.

This may well be a use case for unsafeReentrantRead. Use this method instead of read in methods that can't be refactored so that a database connection (db) is passed along.

Please carefully read the fine print of the documentation of this method. As most "unsafe" APIs, it does not provide any concurrency guarantees, and leaves the burden of this task on the application's shoulders. Particularly, it your app uses a DatabasePool, and performs several requests in this formatter, I strongly suggest you wrap all of them in one savepoint. This will prevent eventual concurrent writes from altering your results.

Thanks. The warning about using it put me off!

This is intended ;-) If unsafe methods were more inviting, too many users would use them and lose data integrity, break database invariants, without noticing.

Now we do not always control the dispatch queue from which the database is invoked: reentrant methods exist because we sometimes need them.

You made a good point explaining your particular situation: the straight answer was the best option!

Another option would have been to build a formatter from a database connection:

let formatter = try dbQueue.read { db in
    // Have the formatter fetch and store everything it needs...
    try Formatter(db)

// ... so that it can format later, without any db access:

This would remove the need for any unsafe access method. But in order to "check whether a stock code exists", this particular formatter would potentially have to fetch too much stuff. This did not look like a good advice to give.

Yet, you may want to remember this alternative pattern when such a need happens again as you are developing your apps. Avoid unsafe methods as much as you can!

Terms of Service

Privacy Policy

Cookie Policy