Yes, you seem to understand the issue.  The issue only happens when using
shared caches.

I've reproduced the issue using both SERIALIZED and MULTITHREADED modes.

I'm surprised to hear you say that it might be an inherit limitation or
something not necessarily undesirable.  Of course, when using shared
caches, there must be locks to protect the global list of caches.  But
ideally, I would expect these times to be short lived.  If a situation ever
arises where all unrelated database opening, from all threads, is blocked
for long periods of time, that seems to me to be an obvious bug.  That's
the bug that I'm reporting.

Being an inherit limitation would seem to imply that there is no solution
to this problem, that having shared caches and WAL indexes rebuilding
necessarily should block all unrelated databases opening.  I don't see why
that should be the case and I'll explain some reasons why.  It's a little
bit hard for me to talk about it though, because I'm not entirely sure what
the lock SQLITE_MUTEX_STATIC_OPEN is protecting.  When iterating through
the list of shared caches, it acquires the lock SQLITE_MUTEX_STATIC_MASTER,
so the other OPEN lock must be for something else.  The comments say it's
to prevent a race condition and references "Ticket #3537", but I can't seem
to find that ticket.

The problematic line seems to be this one:
https://github.com/mackyle/sqlite/blob/master/src/btree.c#L2405

It seems that the only purpose of this line is to check to see if this is
an already shared pager-cache.  The problem is that this line ends up
acquiring a lock on the btree.  If there was simply a way to check if this
is an already shared pager-cache, without the possibility of waiting on a
lock, then this bug would be solved.

This is the checkin that originally created the open lock
https://www.sqlite.org/src/info/19fa5a29b97f017a

It doesn't appear that the problematic line existed at this time, which
leads me to believe that there were probably versions of sqlite released
that didn't have this bug.

It seems to me that when this open lock was created, it was expected that
the open code would always execute within a very short period of time.
Later on, someone added the call to sqlite3BtreeSchema, not realizing that
it could possibly get hung up on a lock, which could block all opening for
all databases.

Please let me know if I'm thinking about this problem clearly, or if you
would like me to test some things or write a simple test case.

Thanks,
Brian Vincent


On Sat, Jul 9, 2016 at 5:58 AM Olivier Mascia <[email protected]> wrote:

> > Le 9 juil. 2016 à 12:33, Simon Slavin <[email protected]> a écrit :
> >
> >> I'm really interested in knowing wether you use the engine in
> SERIALIZED or MULTITHREADED mode during this event reproduction?
> >
> > In other words, please read the last part of
> >
> > <https://www.sqlite.org/threadsafe.html>
> >
> > and try to reproduce your problem in "Multi-thread" mode.  If the
> problem still occurs, please try again in "Serialized" mode.
>
> In between I had a quick look at sqlite3.c code.  It looks like the source
> of the issue is expected. The MULTITHREADED mode relaxes usage of mutexes,
> albeit only on connections and statements structures.  The remaining bits
> of the engine still make uses of multiple mutexes to protect its integrity
> from multiple threads using the engine.
>
> See sqlite3BtreeOpen() and this section of code where the
> SQLITE_MUTEX_STATIC_OPEN is acquired.
> The SERIALIZED and MULTITHREADED modes both have SQLITE_THREADSAFE != 0
> (which is right).
>
> #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
>   /*
>   ** If this Btree is a candidate for shared cache, try to find an
>   ** existing BtShared object that we can share with
>   */
>   if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){
>     if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
>       int nFilename = sqlite3Strlen30(zFilename)+1;
>       int nFullPathname = pVfs->mxPathname+1;
>       char *zFullPathname = sqlite3Malloc(MAX(nFullPathname,nFilename));
>       MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
>
>       p->sharable = 1;
>       if( !zFullPathname ){
>         sqlite3_free(p);
>         return SQLITE_NOMEM_BKPT;
>       }
>       if( isMemdb ){
>         memcpy(zFullPathname, zFilename, nFilename);
>       }else{
>         rc = sqlite3OsFullPathname(pVfs, zFilename,
>                                    nFullPathname, zFullPathname);
>         if( rc ){
>           sqlite3_free(zFullPathname);
>           sqlite3_free(p);
>           return rc;
>         }
>       }
> #if SQLITE_THREADSAFE
>       mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
>       sqlite3_mutex_enter(mutexOpen);
>       mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
>       sqlite3_mutex_enter(mutexShared);
> #endif
>
> The whole issue then revolves around the SHARED_CACHE/PRIVATE_CACHE and
> not the SERIALIZED/MULTITHREADED mode. Chances are that if OP Brian tries
> the same scenario with PRIVATE_CACHE connections, the issue will not occur.
>
> I think this would probably qualify for an inherent limitation, which
> might be documented, but not something necessarily undesirable.
>
> --
> Meilleures salutations, Met vriendelijke groeten, Best Regards,
> Olivier Mascia, integral.be/om
>
>
>
> _______________________________________________
> sqlite-users mailing list
> [email protected]
> http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users
>
_______________________________________________
sqlite-users mailing list
[email protected]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to