Hi Dan.

> On 12/15/2014 11:59 PM, Dan Kennedy wrote:
> > On 12/15/2014 11:11 PM, Paul wrote:
> >> Hello, dear developers
> >>
> >> Recently I've stumbled upon a very rare and strange bug.
> >> The result of this is abnormal memory usage, that does not allow us 
> >> to remove
> >> fair number of rows from a table due to the limit of memory, 
> >> available for 32bit
> >> process. This is strange, because database size is somewhat small: 79M.
> >> Digging around I finally managed to pinpoint when exactly does this 
> >> issue occur.
> >> Another stange thing, though is that memory is successfully deallocated,
> >> bacause no matter what, valgrind does not report definitely lost memory.
> >>
> >> I want to present you my test case. You have two options to 
> >> manipulate it.
> >> Two defines:
> >> - NO_NESTED_TRANSACTION desables nested transaction.
> >> - NO_CHILD_RECORDS disables population of 'child' table with data.
> >>
> >> To compile:
> >> # clang -o test -L/usr/local/lib -lsqlite3 test.c
> >>
> >> My results
> >>
> >> Without defines:
> >> # clang -o test -L/usr/local/lib -lsqlite3 test.c
> >> # ./test
> >> Current mem: 0
> >> Hi mem: 1294136920
> >>
> >> Not okay, 1.2GiB peak memory usage.
> >>
> >>
> >> With NO_CHILD_RECORDS
> >> # clang -o test -DNO_CHILD_RECORDS -L/usr/local/lib -lsqlite3 test.c
> >> # ./test
> >> Current mem: 0
> >> Hi mem: 421141176
> >>
> >> A bit better, but still not ok.
> >>
> >>
> >> With NO_NESTED_TRANSACTION:
> >> # clang -o test -DNO_NESTED_TRANSACTION -L/usr/local/lib -lsqlite3 
> >> test.c
> >> # ./test
> >> Current mem: 0
> >> Hi mem: 15100760
> >>
> >> Seems ok.
> >>
> >>
> >> With NO_NESTED_TRANSACTION and NO_CHILD_RECORDS:
> >> # clang -o test -DNO_NESTED_TRANSACTION -DNO_CHILD_RECORDS 
> >> -L/usr/local/lib -lsqlite3 test.c
> >> # ./test
> >> Current mem: 0
> >> Hi mem: 2554168
> >>
> >> No doubt it's even better.
> >
> > The memory is being used by the statement journal, which you have in 
> > memory. If the app did not set "journal_mode=memory" and 
> > "temp_store=memory", SQLite would create a really large temp file 
> > instead of using memory. Which would still be sub-optimal, but might 
> > not run into the 32-bit limit.
> >
> > The reason the statement journal is growing so large is that SQLite 
> > only truncates the statement journal when the outermost 
> > sub-transaction is closed. Otherwise it just keeps on appending. i.e.
> >
> > BEGIN;
> > SAVEPOINT xxx;
> > ...
> > SAVEPOINT yyy;
> > ...
> > RELEASE yyy; -- does not truncate statement journal
> > COMMIT xxx; -- truncates statement journal
> > COMMIT;
> >
> > Your example has one explicit sub-transaction (equivalent to xxx) and 
> > each DELETE statement is opening a second, implicit, sub-transaction 
> > (equivalent to yyy).
> >
> > With the child records included, each DELETE statement is modifying 3 
> > db pages - one from table "bar", one from table "foo" and one from the 
> > PRIMARY KEY index on "foo". 100,000 deletes, 4Kib page size, 3 pages 
> > per delete == 1.2GiB. Or without the child records, just 1 page 
> > modified per delete, so closer to 400MiB of memory. Without the 
> > sub-transaction, the implicit sub-transaction created by each DELETE 
> > becomes the outermost and so the statement journal doesn't grow much 
> > at all. So not much memory used in that case.
> 
> Another idea would be to use a deferred foreign key constraint. That way 
> the DELETE operations will not need the statement journal at all.
> 

How can I get around implicit savepoint creation? 
Why doesn't savepoint commit truncate a journal?
Why does journal grow even when there is nothing to delete in bar?

Currently this limitation renders use of sqlite impossible, unless using ugly 
hacks.
>From the user's perspective, this overhead is unimaginable. This is completely
normal use of SQL yet overhead is above the wildest imagination :(
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to