On Wed, 2013-11-20 at 13:23 +0200, RSmith wrote:
> Hi Tristan,
> 
> Do you honestly have a use-case where you do not know whether a transaction 
> is going to be writing to the DB or not?
> 
> I would imagine the only way this is possible is that you are doing some form 
> of select query, and then based on the outcome, decide 
> whether or not to start writing values or not.
> 
> If this is the case, the solution is simple - Do the select query before 
> starting the transaction. Only when you KNOW you are going 
> to need to write to the DB, THEN start a transaction - and start it 
> explicitely Immediate - if this fails, feel free to retry as 
> many times as ppossible.
> 
> If you insist on starting a transaction without immediate, and later maybe 
> upgrade it to a writing transaction (i.e. obtain a 
> lock)... then it may fail (if the db is Busy) and if it does you can end and 
> retry the entire transaction because no writes would 
> have been effected yet, although it will also rerun the selects which is not 
> efficient, but might be needed if the other thread 
> writing to the DB (which made your current thread fail with SQLITE_BUSY) 
> changed some of the data you depend on for the write 
> decisions. This is easy in DB terms, but might complicate your App code - I 
> would go with the first suggestion.
> 
> As an aside - you mention "Nested Transaction"... there is no such thing, 
> unless you are using Savepoints. You can only ever begin 1 
> single transaction, the next "BEGIN TRANSACTION" in the same DB connection 
> will fail hard.
> http://www.sqlite.org/lang_transaction.html
> http://www.sqlite.org/lang_savepoint.html
> 
> To get back to Simon's advice about failing loud and you suggesting you wish 
> to suppress this....
> if you set a sufficient time-out, you should never get Busy replies - unless 
> you have something that updates the database for, say, 
> an hour or so - some long-running update preventing your thread from doing 
> its thing - In which case you have to ask yourself, do I 
> really want to keep my user hanging-on for an hour waiting while I retry? - 
> just so I gracefully suppress the message? If you think 
> that is a good idea, you have not had many users. Let them KNOW what is going 
> on, it's always safer. A user that doesn't know what 
> is going on will use the OS managers to simply stop your task/app from 
> running and then blame you for the broken database 
> (rightfully so in IMHO) and various other nefarious things.
> http://www.sqlite.org/pragma.html#pragma_busy_timeout
> 
> 

Thanks for both of your detailed replies.

In this case I have a one writer / many reader setup, and the writing is
performed in a dedicated process, so it really doesnt make sense to
report to a calling process that it was busy and it should try again.

The routines are asynchronous already with user/IPC system defined
timeouts, so it should certainly retry if say, the DB lock could not
be immediately obtained, possibly because of some VFS subsystem.

I was unaware of the automatic timeout system that I can use
with SQLite, that sounds much better than handling it manually.

And finally yes, it's possible that we might run a transaction
that initially starts with selects and only might insert/delete
depending on, for instance, what happens during a schema upgrade
after running "PRAGMA table_info (folder_id)".

While there will never be more than one writer, it has been known
to happen in my concurrency tests that SQLITE_BUSY is returned,
certainly not because someone else actually has a writer's lock.
(in this case we retry, and so far that doesn't cause problems,
I raised this issue because of the documentation in
http://www.sqlite.org/c3ref/step.html ... which I found to be
alarming, but now I understand a bit better, thanks to your
replies).

There are cases where readers will run multiple selects inside
a transaction, to ensure that the overall DB revision string
was stable before accessing data (in some cases it helps to
avoid races to just wait for the writer process to send us
a change notification with the new revision string before
allowing the direct reader to read again).

I had been considering optionally using IMMEDIATE though
just to make sure that I can retry at the right place when
SQLITE_BUSY is returned, and I might do that as well for
the cases where I know there will be a write coming along.

Of course, this propagates some responsibility up the call
chain, I am implementing a sort of "convenient" API to
interact with a vCard database, so I should be as flexible
as possible and allow the caller to lock / fetch settings / 
add contacts / unlock the addressbook at will, without exposing
gruesome implementation details of whatever backing DB I am
using.

So, I will look into this timeout setting as it seems interesting,
and also consider using IMMEDIATE for cases where I know there
will be a write.

Regarding nested transactions, that was my mistake, some of
my C code allows nesting of transactions by keeping a counter
of the nesting level, but only issuing SQLite commands at the
outer most frame (I forgot this detail, hence my confusion).

Thanks again.

Cheers,
    -Tristan


_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to