A couple more questions:

1)  Does "without encryption" mean still using System.Data.SQLite, just
without using a key?
2)  Are you using WAL?
3)  Do you also see the problem with a single process with two connections?
4)  Any chance you could provide a small demo project that demonstrates the
problem?
5)  Are you using standard System.Data.SQLite, or the SqlCipher version?
(http://sqlcipher.net)

I'm pretty fuzzy on this, but here are some (possibly way off base)
thoughts...

The way I understand things, when you do an update inside of a transaction,
the new data is written to the log until it is committed, and then a
rollback involves discarding the log page with the changes.  This means you
expect to see the new data in your "session" but no one else should see the
new data until you commit.  

The question of which version of the data a session sees is, I think, tied
to some global counter-type metadata that tracks changes.  In Oracle, this
would be perhaps a system change number that would determine which undo
blocks your session would see versus other sessions.  I think SQLite has
something similar, and I think this is stored in the database header on the
first database page.  

I also recall that there are a few places in SQLite where it reads data for
page 1 using system-level I/O rather than the pager (e.g., direct reads on
the database file), and this causes issues for page-level encryptors because
they don't get an opportunity to decrypt the encrypted page before SQLite
reads that piece of data, so they typically have to hard-code some value at
that data location or hope that SQLite ignores what would be an obviously
invalid value.  I think the change counter is one of these items.

Maybe folks with much more understanding could make sense of this, but my
thought is that the equivalent of the "system change number" is being mucked
up by the page-level encryptor, causing SQLite to get the old data because
as far as it knows that's the data at the given (invalid) change count.  

Well, something like that.  Again, probably way off base, but I had a couple
of minutes and thought I'd throw it out there...

~Mike

-----Original Message-----
From: sqlite-users-boun...@sqlite.org
[mailto:sqlite-users-boun...@sqlite.org] On Behalf Of Fabrizio Steiner
Sent: Thursday, December 15, 2011 9:01 AM
To: General Discussion of SQLite Database
Subject: Re: [sqlite] SQLite reading old data

I think the following observation, which I made in the meantime, may be very
interesting. Which also gives a hint to a problem with SQLite in combination
with the encryption of System.Data.SQLite.
 
I tried the same test with an unencrypted database, and surprisingly the 3
threads do their work without any exception. I executed several test runs
with the 3 threads and it wasn't reproducable anymore. 
Activating the encryption again it's reproducable and exceptions occur due
to an old stamp that is being retrieved. 

Answers to your questions:
- No connections are shared at all between threads. So every thread creates
2 private connections. 
- Updating always occurs in the second connection.
- The after-update checks will be perfomed in the same transaction as the
update itself. 
- No thread ever deletes a row.

Regards
Fabrizio

> -----Ursprüngliche Nachricht-----
> Von: sqlite-users-boun...@sqlite.org [mailto:sqlite-users- 
> boun...@sqlite.org] Im Auftrag von Igor Tandetnik
> Gesendet: Donnerstag, 15. Dezember 2011 14:26
> An: sqlite-users@sqlite.org
> Betreff: Re: [sqlite] SQLite reading old data
> 
> Fabrizio Steiner <f...@bbtsoftware.ch> wrote:
> > I'm currently facing a problem with the System.Data.SQLite ADO.NET 
> > Provider. I'm using stamps (simple integer column) on the rows in 
> > order to detect if another user has altered the same datarow during 
> > the
> time the first user started the edit and when he invoked the save. 
> Every update increases this stamp and the update statement is 
> constrained with the old stamp. If the update has
> > no affected rows, there are different cases which are checked:
> > - If the datarow has been deleted, the user is informed about this.
> > - If the datarow still exists the current stamp in the database is
retrieved.
> >   - If the db stamp is greater than the reference stamp, the user 
> > will be informed that another user changed the data in the meantime.
> >   - If the db stamp is smaller or equal to the reference stamp, an 
> > exception
> is thrown because the stamp can never get smaller.
> >
> > I tried to simulate 3 users which are updating a datarow in the same 
> > table, every user changes it's own row.  Basically I had 3 threads 
> > each of one first retrieving a row in one connection. Updating the 
> > data and
> storing it back into the table with another connection. Every thread 
> made a loop with several edits and a sleep in between.
> 
> Do you have two connections, each shared by three threads; or six 
> separate connections? Do you ever perform any writing on the 
> connection that reads the original version of the row? Do you perform 
> after-update checks on the same connection as the update itself? Do 
> you wrap the update and the checks together in a transaction?
> 
> How is a particular "datarow" identified? Is it possible for one 
> thread to delete a row, and then for another to insert it back later 
> as new (presumably with the version stamp starting back from zero)?
> --
> Igor Tandetnik
> 
> _______________________________________________
> sqlite-users mailing list
> sqlite-users@sqlite.org
> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

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

Reply via email to