/ Check-in [a55eb4c3]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Disable memory-pressure induced cache spill during savepoint rollback.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a55eb4c3e9fa4ea43b8f8182eb9e39138df03f52
User & Date: drh 2010-06-24 14:52:25
Context
2010-06-24
17:37
Modify ctime.test to work with SQLITE_THREADSAFE=2. check-in: c6db3b30 user: dan tags: trunk
14:52
Disable memory-pressure induced cache spill during savepoint rollback. check-in: a55eb4c3 user: drh tags: trunk
13:24
Add a coverage test to pagerfault.test. check-in: b58db67e user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/pager.c.

301
302
303
304
305
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
....
1686
1687
1688
1689
1690
1691
1692


1693


1694
1695
1696
1697
1698
1699
1700
1701
1702
....
3509
3510
3511
3512
3513
3514
3515












3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
....
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
....
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
**   then attempts to upgrade to an exclusive lock. If this attempt
**   fails, then SQLITE_BUSY may be returned to the user and the user
**   may attempt to commit the transaction again later (calling
**   CommitPhaseOne() again). This flag is used to ensure that the 
**   master journal name is only written to the journal file the first
**   time CommitPhaseOne() is called.
**
** doNotSync
**
**   When enabled, cache spills are prohibited and the journal file cannot
**   be synced.  This variable is set and cleared by sqlite3PagerWrite() 
**   in order to prevent a journal sync from happening in between the
**   journalling of two pages on the same sector.

**
** needSync
**
**   TODO: It might be easier to set this variable in writeJournalHdr()
**   and writeMasterJournal() only. Change its meaning to "unsynced data
**   has been written to the journal".
**
................................................................................
  */
  u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 needSync;                /* True if an fsync() is needed on the journal */
  u8 journalStarted;          /* True if header of journal is synced */
  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
  u8 dbSizeValid;             /* Set when dbSize is correct */
  u8 subjInMemory;            /* True to use in-memory sub-journals */
  Pgno dbSize;                /* Number of pages in the database */
  Pgno dbOrigSize;            /* dbSize before the current transaction */
  Pgno dbFileSize;            /* Number of pages in the database file */
  int errCode;                /* One of several kinds of errors */
  int nRec;                   /* Pages journalled since last j-header written */
................................................................................
    **
    ** The solution is to add an in-memory page to the cache containing
    ** the data just read from the sub-journal. Mark the page as dirty 
    ** and if the pager requires a journal-sync, then mark the page as 
    ** requiring a journal-sync before it is written.
    */
    assert( isSavepnt );


    if( (rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1))!=SQLITE_OK ){


      return rc;
    }
    pPg->flags &= ~PGHDR_NEED_READ;
    sqlite3PcacheMakeDirty(pPg);
  }
  if( pPg ){
    /* No page should ever be explicitly rolled back that is in use, except
    ** for page 1 which is held in use in order to keep the lock on the
    ** database active. However such a page may be rolled back as a result
................................................................................
*/
static int pagerStress(void *p, PgHdr *pPg){
  Pager *pPager = (Pager *)p;
  int rc = SQLITE_OK;

  assert( pPg->pPager==pPager );
  assert( pPg->flags&PGHDR_DIRTY );













  pPg->pDirty = 0;
  if( pagerUseWal(pPager) ){
    /* Write a single frame for this page to the log. */
    if( subjRequiresPage(pPg) ){ 
      rc = subjournalPage(pPg); 
    }
    if( rc==SQLITE_OK ){
      rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
    }
  }else{
    /* The doNotSync flag is set by the sqlite3PagerWrite() function while it
    ** is journalling a set of two or more database pages that are stored
    ** on the same disk sector. Syncing the journal is not allowed while
    ** this is happening as it is important that all members of such a
    ** set of pages are synced to disk together. So, if the page this function
    ** is trying to make clean will require a journal sync and the doNotSync
    ** flag is set, return without doing anything. The pcache layer will
    ** just have to go ahead and allocate a new page buffer instead of
    ** reusing pPg.
    **
    ** Similarly, if the pager has already entered the error state, do not
    ** try to write the contents of pPg to disk.
    */
    if( pPager->errCode || (pPager->doNotSync && pPg->flags&PGHDR_NEED_SYNC) ){
      return SQLITE_OK;
    }
  
    /* Sync the journal file if required. */
    if( pPg->flags&PGHDR_NEED_SYNC ){
      rc = syncJournal(pPager);
      if( rc==SQLITE_OK && pPager->fullSync && 
        !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) &&
        !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
................................................................................
  if( nPagePerSector>1 ){
    Pgno nPageCount;          /* Total number of pages in database file */
    Pgno pg1;                 /* First page of the sector pPg is located on. */
    int nPage;                /* Number of pages starting at pg1 to journal */
    int ii;                   /* Loop counter */
    int needSync = 0;         /* True if any page has PGHDR_NEED_SYNC */

    /* Set the doNotSync flag to 1. This is because we cannot allow a journal
    ** header to be written between the pages journaled by this function.
    */
    assert( !MEMDB );
    assert( pPager->doNotSync==0 );
    pPager->doNotSync = 1;

    /* This trick assumes that both the page-size and sector-size are
    ** an integer power of 2. It sets variable pg1 to the identifier
    ** of the first page of the sector pPg is located on.
    */
    pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;

................................................................................
          pPage->flags |= PGHDR_NEED_SYNC;
          sqlite3PagerUnref(pPage);
        }
      }
      assert(pPager->needSync);
    }

    assert( pPager->doNotSync==1 );
    pPager->doNotSync = 0;
  }else{
    rc = pager_write(pDbPage);
  }
  return rc;
}

/*







|




|
>







 







|







 







>
>
|
>
>
|
<







 







>
>
>
>
>
>
>
>
>
>
>
>











<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|



|
|







 







|
|







301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
....
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699

1700
1701
1702
1703
1704
1705
1706
....
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
















3543
3544
3545
3546
3547
3548
3549
....
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
....
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
**   then attempts to upgrade to an exclusive lock. If this attempt
**   fails, then SQLITE_BUSY may be returned to the user and the user
**   may attempt to commit the transaction again later (calling
**   CommitPhaseOne() again). This flag is used to ensure that the 
**   master journal name is only written to the journal file the first
**   time CommitPhaseOne() is called.
**
** doNotSpill
**
**   When enabled, cache spills are prohibited and the journal file cannot
**   be synced.  This variable is set and cleared by sqlite3PagerWrite() 
**   in order to prevent a journal sync from happening in between the
**   journalling of two pages on the same sector.  It is also set to prevent
**   pagerStress() from trying to use the journal during a rollback.
**
** needSync
**
**   TODO: It might be easier to set this variable in writeJournalHdr()
**   and writeMasterJournal() only. Change its meaning to "unsynced data
**   has been written to the journal".
**
................................................................................
  */
  u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 needSync;                /* True if an fsync() is needed on the journal */
  u8 journalStarted;          /* True if header of journal is synced */
  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  u8 doNotSpill;              /* Do not spill the cache when non-zero */
  u8 dbSizeValid;             /* Set when dbSize is correct */
  u8 subjInMemory;            /* True to use in-memory sub-journals */
  Pgno dbSize;                /* Number of pages in the database */
  Pgno dbOrigSize;            /* dbSize before the current transaction */
  Pgno dbFileSize;            /* Number of pages in the database file */
  int errCode;                /* One of several kinds of errors */
  int nRec;                   /* Pages journalled since last j-header written */
................................................................................
    **
    ** The solution is to add an in-memory page to the cache containing
    ** the data just read from the sub-journal. Mark the page as dirty 
    ** and if the pager requires a journal-sync, then mark the page as 
    ** requiring a journal-sync before it is written.
    */
    assert( isSavepnt );
    assert( pPager->doNotSpill==0 );
    pPager->doNotSpill++;
    rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
    assert( pPager->doNotSpill==1 );
    pPager->doNotSpill--;
    if( rc!=SQLITE_OK ) return rc;

    pPg->flags &= ~PGHDR_NEED_READ;
    sqlite3PcacheMakeDirty(pPg);
  }
  if( pPg ){
    /* No page should ever be explicitly rolled back that is in use, except
    ** for page 1 which is held in use in order to keep the lock on the
    ** database active. However such a page may be rolled back as a result
................................................................................
*/
static int pagerStress(void *p, PgHdr *pPg){
  Pager *pPager = (Pager *)p;
  int rc = SQLITE_OK;

  assert( pPg->pPager==pPager );
  assert( pPg->flags&PGHDR_DIRTY );

  /* The doNotSpill flag is set during times when writing to the journal
  ** is disallowed:  (1) during calls to sqlite3PagerWrite() while it
  ** is journalling a set of two or more database pages that are stored
  ** on the same disk sector, and (2) while performing a rollback.
  **
  ** Similarly, if the pager has already entered the error state, do not
  ** try to write the contents of pPg to disk.
  */
  if( pPager->errCode || pPager->doNotSpill ){
    return SQLITE_OK;
  }

  pPg->pDirty = 0;
  if( pagerUseWal(pPager) ){
    /* Write a single frame for this page to the log. */
    if( subjRequiresPage(pPg) ){ 
      rc = subjournalPage(pPg); 
    }
    if( rc==SQLITE_OK ){
      rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
    }
  }else{
















  
    /* Sync the journal file if required. */
    if( pPg->flags&PGHDR_NEED_SYNC ){
      rc = syncJournal(pPager);
      if( rc==SQLITE_OK && pPager->fullSync && 
        !(pPager->journalMode==PAGER_JOURNALMODE_MEMORY) &&
        !(sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
................................................................................
  if( nPagePerSector>1 ){
    Pgno nPageCount;          /* Total number of pages in database file */
    Pgno pg1;                 /* First page of the sector pPg is located on. */
    int nPage;                /* Number of pages starting at pg1 to journal */
    int ii;                   /* Loop counter */
    int needSync = 0;         /* True if any page has PGHDR_NEED_SYNC */

    /* Set the doNotSpill flag to 1. This is because we cannot allow a journal
    ** header to be written between the pages journaled by this function.
    */
    assert( !MEMDB );
    assert( pPager->doNotSpill==0 );
    pPager->doNotSpill++;

    /* This trick assumes that both the page-size and sector-size are
    ** an integer power of 2. It sets variable pg1 to the identifier
    ** of the first page of the sector pPg is located on.
    */
    pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;

................................................................................
          pPage->flags |= PGHDR_NEED_SYNC;
          sqlite3PagerUnref(pPage);
        }
      }
      assert(pPager->needSync);
    }

    assert( pPager->doNotSpill==1 );
    pPager->doNotSpill--;
  }else{
    rc = pager_write(pDbPage);
  }
  return rc;
}

/*