/ Check-in [66fb9e1c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Do not search for locks to clear when connecting to a db in multi-process mode unless it looks like the previous user of the client-id crashed.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | server-process-edition
Files: files | file ages | folders
SHA3-256: 66fb9e1cb479f1e764f1606f041bd97fd3bd428093832c000ee36b643377e9e2
User & Date: dan 2017-08-18 16:04:40
Context
2017-08-18
18:55
Add tests to this branch. check-in: abb6e076 user: dan tags: server-process-edition
16:04
Do not search for locks to clear when connecting to a db in multi-process mode unless it looks like the previous user of the client-id crashed. check-in: 66fb9e1c user: dan tags: server-process-edition
2017-08-17
19:32
Add support for crash recovery in multi-process mode. And add test cases for the same. check-in: a8115f95 user: dan tags: server-process-edition
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/server.c.

    61     61   ** The argument to this macro is the value of a locking slot. This macro
    62     62   ** returns the current number of slow reader clients reading the page.
    63     63   */
    64     64   #define slotGetSlowReaders(v) (((v) & HMA_SLOT_TR_MASK) >> HMA_SLOT_RLWL_BITS)
    65     65   
    66     66   #define slotReaderMask(v) ((v) & HMA_SLOT_RL_MASK)
    67     67   
           68  +#define fdOpen(pFd) ((pFd)->pMethods!=0)
    68     69   
    69     70   /* 
    70     71   ** Atomic CAS primitive used in multi-process mode. Equivalent to:
    71     72   **
    72     73   **   int serverCompareAndSwap(u32 *ptr, u32 oldval, u32 newval){
    73     74   **     if( *ptr==oldval ){
    74     75   **       *ptr = newval;
................................................................................
    80     81   #define serverCompareAndSwap(ptr,oldval,newval) \
    81     82     __sync_bool_compare_and_swap(ptr,oldval,newval)
    82     83   
    83     84   
    84     85   typedef struct ServerDb ServerDb;
    85     86   typedef struct ServerJournal ServerJournal;
    86     87   
    87         -struct ServerGlobal {
    88         -  ServerDb *pDb;                  /* Linked list of all ServerDb objects */
    89         -};
    90         -static struct ServerGlobal g_server;
    91         -
    92     88   struct ServerJournal {
    93     89     char *zJournal;
    94     90     sqlite3_file *jfd;
    95     91   };
    96     92   
    97     93   /*
    98     94   ** There is one instance of the following structure for each distinct 
    99     95   ** database file opened in server mode by this process.
   100     96   */
   101     97   struct ServerDb {
   102         -  sqlite3_mutex *mutex;           /* Non-recursive mutex */
           98  +  i64 aFileId[2];                 /* Opaque VFS file-id */
           99  +  ServerDb *pNext;                /* Next db in this process */
   103    100     int nClient;                    /* Current number of clients */
          101  +  sqlite3_mutex *mutex;           /* Non-recursive mutex */
          102  +
          103  +  /* Variables above this point are protected by the global mutex -
          104  +  ** serverEnterMutex()/LeaveMutex(). Those below this point are 
          105  +  ** protected by the ServerDb.mutex mutex.  */
          106  +
   104    107     int bInit;                      /* True once initialized */
   105    108     u32 transmask;                  /* Bitmask of taken transaction ids */
   106    109     u32 *aSlot;                     /* Array of page locking slots */
   107         -  i64 aFileId[2];                 /* Opaque VFS file-id */
   108         -  ServerDb *pNext;                /* Next db in this process */
   109    110   
   110    111     sqlite3_vfs *pVfs;
   111    112     ServerJournal aJrnl[HMA_MAX_TRANSACTIONID];
   112    113     u8 *aJrnlFdSpace;
   113    114   
   114         -  void *pServerShm;
          115  +  void *pServerShm;               /* SHMOPEN handle (multi-process only) */
          116  +  u32 *aClient;                   /* Client "transaction active" flags */
   115    117   
   116    118     int iNextCommit;                /* Commit id for next pre-commit call */ 
   117    119     Server *pCommit;                /* List of connections currently commiting */
   118    120     Server *pReader;                /* Connections in slower-reader transaction */
   119    121     ServerPage *pPgFirst;           /* First (oldest) in list of pages */
   120    122     ServerPage *pPgLast;            /* Last (newest) in list of pages */
   121    123     ServerPage *apPg[HMA_HASH_SIZE];/* Hash table of "old" page data */
................................................................................
   133    135     int iTransId;                   /* Current transaction id (or -1) */
   134    136     int iCommitId;                  /* Current commit id (or 0) */
   135    137     int nAlloc;                     /* Allocated size of aLock[] array */
   136    138     int nLock;                      /* Number of entries in aLock[] */
   137    139     u32 *aLock;                     /* Array of held locks */
   138    140     Server *pNext;                  /* Next in pCommit or pReader list */
   139    141   };
          142  +
          143  +struct ServerGlobal {
          144  +  ServerDb *pDb;                  /* Linked list of all ServerDb objects */
          145  +};
          146  +static struct ServerGlobal g_server;
          147  +
   140    148   
   141    149   struct ServerFcntlArg {
   142    150     void *h;                        /* Handle from SHMOPEN */
   143    151     void *p;                        /* Mapping */
   144    152     int i1;                         /* Integer value 1 */
   145    153     int i2;                         /* Integer value 2 */
   146    154   };
................................................................................
   162    170   */
   163    171   static void serverEnterMutex(void){
   164    172     sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1));
   165    173   }
   166    174   static void serverLeaveMutex(void){
   167    175     sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1));
   168    176   }
          177  +#if 0
   169    178   static void serverAssertMutexHeld(void){
   170    179     assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1)) );
   171    180   }
          181  +#endif
   172    182   
   173    183   /*
   174    184   ** Locate the ServerDb object shared by all connections to the db identified
   175    185   ** by aFileId[2], increment its ref count and set pNew->pDb to point to it. 
   176    186   ** In this context "locate" may mean to find an existing object or to
   177    187   ** allocate a new one.
   178    188   */
................................................................................
   216    226   
   217    227   static int serverClientRollback(Server *p, int iClient){
   218    228     ServerDb *pDb = p->pDb;
   219    229     ServerJournal *pJ = &pDb->aJrnl[iClient];
   220    230     int bExist = 1;
   221    231     int rc = SQLITE_OK;
   222    232   
   223         -  if( pJ->jfd->pMethods==0 ){
          233  +  if( fdOpen(pJ->jfd)==0 ){
   224    234       bExist = 0;
   225    235       rc = sqlite3OsAccess(pDb->pVfs, pJ->zJournal, SQLITE_ACCESS_EXISTS,&bExist);
   226    236       if( bExist && rc==SQLITE_OK ){
   227    237         int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
   228    238         rc = sqlite3OsOpen(pDb->pVfs, pJ->zJournal, pJ->jfd, flags, &flags);
   229    239       }
   230    240     }
................................................................................
   244    254     Server *p, 
   245    255     sqlite3_file *dbfd, 
   246    256     int bDelete
   247    257   ){
   248    258     ServerDb *pDb = p->pDb;
   249    259     int i;
   250    260   
          261  +  assert( pDb->pServerShm || bDelete );
   251    262     for(i=0; i<HMA_MAX_TRANSACTIONID; i++){
   252    263       ServerJournal *pJ = &pDb->aJrnl[i];
   253    264   
   254         -    if( pDb->pServerShm && bDelete ){
          265  +    if( bDelete && (pDb->pServerShm || fdOpen(pJ->jfd)) ){
   255    266         int rc = serverClientRollback(p, i);
   256    267         if( rc!=SQLITE_OK ) bDelete = 0;
   257    268       }
   258    269   
   259         -    if( pJ->jfd ){
          270  +    if( fdOpen(pJ->jfd) ){
   260    271         sqlite3OsClose(pJ->jfd);
   261    272         if( bDelete ) sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0);
   262    273       }
          274  +
   263    275       sqlite3_free(pJ->zJournal);
   264    276     }
   265    277     memset(pDb->aJrnl, 0, sizeof(ServerJournal)*HMA_MAX_TRANSACTIONID);
   266    278   
   267    279     if( pDb->aJrnlFdSpace ){
   268    280       sqlite3_free(pDb->aJrnlFdSpace);
   269    281       pDb->aJrnlFdSpace = 0;
................................................................................
   277    289     }else{
   278    290       sqlite3_free(pDb->aSlot);
   279    291     }
   280    292     pDb->aSlot = 0;
   281    293     pDb->bInit = 0;
   282    294   }
   283    295   
          296  +/*
          297  +** Clear all page locks held by client iClient. The handle passed as the
          298  +** first argument may or may not correspond to client iClient.
          299  +**
          300  +** This function is called in multi-process mode as part of restoring the
          301  +** system state after it has been detected that client iClient may have
          302  +** failed mid transaction. It is never called for a single process system.
          303  +*/
   284    304   static void serverClientUnlock(Server *p, int iClient){
   285    305     ServerDb *pDb = p->pDb;
   286    306     int i;
   287    307   
   288    308     assert( pDb->pServerShm );
   289    309     for(i=0; i<HMA_PAGELOCK_SLOTS; i++){
   290    310       u32 *pSlot = &pDb->aSlot[i];
................................................................................
   322    342     if( pDb->aJrnlFdSpace==0 ){
   323    343       rc = SQLITE_NOMEM_BKPT;
   324    344     }else{
   325    345       if( eServer==2 ){
   326    346         ServerFcntlArg arg;
   327    347         arg.h = 0;
   328    348         arg.p = 0;
   329         -      arg.i1 = sizeof(u32)*HMA_PAGELOCK_SLOTS;
          349  +      arg.i1 = sizeof(u32)*(HMA_PAGELOCK_SLOTS + HMA_MAX_TRANSACTIONID);
   330    350         arg.i2 = 0;
   331    351   
   332    352         rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMOPEN, (void*)&arg);
   333    353         if( rc==SQLITE_OK ){
   334    354           pDb->aSlot = (u32*)arg.p;
          355  +        pDb->aClient = &pDb->aSlot[HMA_PAGELOCK_SLOTS];
   335    356           pDb->pServerShm = arg.h;
   336    357           bRollback = arg.i2;
   337    358         }
   338    359       }else{
   339    360         pDb->aSlot = (u32*)sqlite3MallocZero(sizeof(u32)*HMA_PAGELOCK_SLOTS);
   340    361         if( pDb->aSlot==0 ) rc = SQLITE_NOMEM_BKPT;
   341    362         bRollback = 1;
................................................................................
   495    516           sqlite3_mutex_leave(pNew->pDb->mutex);
   496    517   
   497    518           /* If this is a multi-process database, it may be that the previous
   498    519           ** user of client-id pNew->iTransId crashed mid transaction. Roll
   499    520           ** back any hot journal file in the file-system and release 
   500    521           ** page locks held by any crashed process. TODO: The call to
   501    522           ** serverClientUnlock() is expensive.  */
   502         -        if( rc==SQLITE_OK && pDb->pServerShm ){
          523  +        if( rc==SQLITE_OK && pDb->pServerShm && pDb->aClient[pNew->iTransId] ){
   503    524             serverClientUnlock(pNew, pNew->iTransId);
   504    525             rc = serverClientRollback(pNew, pNew->iTransId);
   505    526           }
   506    527         }
   507    528       }else{
   508    529         rc = SQLITE_NOMEM_BKPT;
   509    530       }
................................................................................
   527    548     if( p->eTrans==SERVER_TRANS_NONE ){
   528    549       ServerDb *pDb = p->pDb;
   529    550       u32 t;
   530    551   
   531    552       assert( p->pNext==0 );
   532    553       if( pDb->pServerShm ){
   533    554         p->eTrans = SERVER_TRANS_READWRITE;
          555  +      pDb->aClient[p->iTransId] = 1;
   534    556       }else{
   535    557         assert( p->iTransId<0 );
   536    558         sqlite3_mutex_enter(pDb->mutex);
   537    559         if( bReadonly ){
   538    560           Server *pIter;
   539    561           p->iCommitId = pDb->iNextCommit;
   540    562           for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
................................................................................
   674    696         pDb->pPgFirst = pPg;
   675    697       }
   676    698     }
   677    699   
   678    700     sqlite3_mutex_leave(pDb->mutex);
   679    701   
   680    702     p->pNext = 0;
   681         -  p->eTrans = SERVER_TRANS_NONE;
   682    703     p->iTransId = -1;
   683    704     p->iCommitId = 0;
   684    705   }
   685    706   
   686    707   /*
   687    708   ** End a transaction (and release all locks).
   688    709   */
   689    710   int sqlite3ServerEnd(Server *p){
   690    711     if( p->eTrans!=SERVER_TRANS_NONE ){
   691    712       if( p->pDb->pServerShm ){
   692    713         serverReleaseLocks(p);
          714  +      p->pDb->aClient[p->iTransId] = 0;
   693    715       }else{
   694    716         serverEndSingle(p);
   695    717       }
          718  +    p->eTrans = SERVER_TRANS_NONE;
   696    719     }
   697    720     return SQLITE_OK;
   698    721   }
   699    722   
   700    723   int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){
   701    724     ServerDb *pDb = p->pDb;
   702    725     int rc = SQLITE_OK;