Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge the fix for [a1fa75cbdd02] from the experimental branch. Also fix the persistent-wal mode feature of truncating the WAL on close so that it always truncates the WAL to zero bytes. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
09ccc4a1be7ba81890f10aac6623dd90 |
User & Date: | drh 2011-12-16 15:38:52.854 |
References
2011-12-16
| ||
17:01 | Add code for a test that was failing before the persistent-wal related changes of [09ccc4a1be]. (check-in: 49d21ce50f user: dan tags: trunk) | |
Context
2011-12-16
| ||
19:34 | Proposed changes that ensure that the WAL header is written prior to the first commit mark. (check-in: 91d0437c07 user: drh tags: wal-header-sync) | |
17:01 | Add code for a test that was failing before the persistent-wal related changes of [09ccc4a1be]. (check-in: 49d21ce50f user: dan tags: trunk) | |
15:38 | Merge the fix for [a1fa75cbdd02] from the experimental branch. Also fix the persistent-wal mode feature of truncating the WAL on close so that it always truncates the WAL to zero bytes. (check-in: 09ccc4a1be user: drh tags: trunk) | |
15:11 | Improved logging of master-journal name conflicts. (check-in: b1005ef46c user: drh tags: trunk) | |
13:24 | Experimental fix for [a1fa75cbdd]. (Closed-Leaf check-in: 6492af76ea user: dan tags: experimental) | |
Changes
Changes to src/wal.c.
︙ | ︙ | |||
417 418 419 420 421 422 423 424 425 426 427 428 429 430 | volatile u32 **apWiData; /* Pointer to wal-index content in memory */ u32 szPage; /* Database page size */ i16 readLock; /* Which read lock is being held. -1 for none */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; | > | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | volatile u32 **apWiData; /* Pointer to wal-index content in memory */ u32 szPage; /* Database page size */ i16 readLock; /* Which read lock is being held. -1 for none */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ u8 truncateOnCommit; /* True to truncate WAL file on commit */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; |
︙ | ︙ | |||
1778 1779 1780 1781 1782 1783 1784 | walcheckpoint_out: walIteratorFree(pIter); return rc; } /* | | | | < | | | | | | | | | | < | 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 | walcheckpoint_out: walIteratorFree(pIter); return rc; } /* ** If the WAL file is currently larger than nMax bytes in size, truncate ** it to exactly nMax bytes. If an error occurs while doing so, ignore it. */ static void walLimitSize(Wal *pWal, i64 nMax){ i64 sz; int rx; sqlite3BeginBenignMalloc(); rx = sqlite3OsFileSize(pWal->pWalFd, &sz); if( rx==SQLITE_OK && (sz > nMax ) ){ rx = sqlite3OsTruncate(pWal->pWalFd, nMax); } sqlite3EndBenignMalloc(); if( rx ){ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); } } /* ** Close a connection to a log file. */ int sqlite3WalClose( |
︙ | ︙ | |||
1820 1821 1822 1823 1824 1825 1826 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ | < > > | | > > > | | > > > > > > | > | 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } rc = sqlite3WalCheckpoint( pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 ); if( rc==SQLITE_OK ){ int bPersist = -1; sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist); if( bPersist!=1 ){ /* Try to delete the WAL file if the checkpoint completed and ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal ** mode (!bPersist) */ isDelete = 1; }else if( pWal->mxWalSize>=0 ){ /* Try to truncate the WAL file to zero bytes if the checkpoint ** completed and fsynced (rc==SQLITE_OK) and we are in persistent ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a ** non-negative value (pWal->mxWalSize>=0). Note that we truncate ** to zero bytes as truncating to the journal_size_limit might ** leave a corrupt WAL file on disk. */ walLimitSize(pWal, 0); } } } walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); if( isDelete ){ sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); |
︙ | ︙ | |||
2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 | ** End a write transaction. The commit has already been done. This ** routine merely releases the lock. */ int sqlite3WalEndWriteTransaction(Wal *pWal){ if( pWal->writeLock ){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); pWal->writeLock = 0; } return SQLITE_OK; } /* ** If any data has been written (but not committed) to the log file, this ** function moves the write-pointer back to the start of the transaction. | > | 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 | ** End a write transaction. The commit has already been done. This ** routine merely releases the lock. */ int sqlite3WalEndWriteTransaction(Wal *pWal){ if( pWal->writeLock ){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); pWal->writeLock = 0; pWal->truncateOnCommit = 0; } return SQLITE_OK; } /* ** If any data has been written (but not committed) to the log file, this ** function moves the write-pointer back to the start of the transaction. |
︙ | ︙ | |||
2574 2575 2576 2577 2578 2579 2580 | ** at this point. But updating the actual wal-index header is also ** safe and means there is no special case for sqlite3WalUndo() ** to handle if this transaction is rolled back. */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ | < | 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 | ** at this point. But updating the actual wal-index header is also ** safe and means there is no special case for sqlite3WalUndo() ** to handle if this transaction is rolled back. */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); aSalt[1] = salt1; walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; |
︙ | ︙ | |||
2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 | sqlite3Put4byte(&aWalHdr[24], aCksum[0]); sqlite3Put4byte(&aWalHdr[28], aCksum[1]); pWal->szPage = szPage; pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; pWal->hdr.aFrameCksum[0] = aCksum[0]; pWal->hdr.aFrameCksum[1] = aCksum[1]; rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } } | > | 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 | sqlite3Put4byte(&aWalHdr[24], aCksum[0]); sqlite3Put4byte(&aWalHdr[28], aCksum[1]); pWal->szPage = szPage; pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN; pWal->hdr.aFrameCksum[0] = aCksum[0]; pWal->hdr.aFrameCksum[1] = aCksum[1]; pWal->truncateOnCommit = 1; rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } } |
︙ | ︙ | |||
2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 | } nLast++; iOffset += szPage; } rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } /* Append data to the wal-index. It is not necessary to lock the ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index ** guarantees that there are no other writers, and no data that may ** be in use by existing readers is being overwritten. */ iFrame = pWal->hdr.mxFrame; | > > > > > > > > > | 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 | } nLast++; iOffset += szPage; } rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){ i64 sz = pWal->mxWalSize; if( walFrameOffset(iFrame+nLast+1, szPage)>pWal->mxWalSize ){ sz = walFrameOffset(iFrame+nLast+1, szPage); } walLimitSize(pWal, sz); pWal->truncateOnCommit = 0; } /* Append data to the wal-index. It is not necessary to lock the ** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index ** guarantees that there are no other writers, and no data that may ** be in use by existing readers is being overwritten. */ iFrame = pWal->hdr.mxFrame; |
︙ | ︙ |
Added test/walcrash3.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | # 2011 December 16 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This test simulates an application crash immediately following a # system call to truncate a file. Specifically, the system call that # truncates the WAL file if "PRAGMA journal_size_limit" is configured. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !wal {finish_test ; return } set testprefix walcrash3 db close testvfs tvfs tvfs filter {xTruncate xWrite} tvfs script tvfs_callback proc tvfs_callback {args} {} sqlite3 db test.db -vfs tvfs do_execsql_test 1.1 { PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 128; PRAGMA journal_size_limit = 16384; CREATE TABLE t1(a BLOB, b BLOB, UNIQUE(a, b)); INSERT INTO t1 VALUES(randomblob(10), randomblob(1000)); } {wal 128 16384} proc tvfs_callback {method file arglist} { if {$::state==1} { foreach f [glob -nocomplain xx_test.*] { forcedelete $f } foreach f [glob -nocomplain test.*] { forcecopy $f "xx_$f" } set ::state 2 } if {$::state==0 && $method=="xTruncate" && [file tail $file]=="test.db-wal"} { set ::state 1 } } for {set i 2} {$i<1000} {incr i} { # If the WAL file is truncated within the following, within the following # xWrite call the [tvfs_callback] makes a copy of the database and WAL # files set sets $::state to 2. So that the copied files are in the same # state as the real database and WAL files would be if an application crash # occurred immediately following the xTruncate(). # set ::state 0 do_execsql_test 1.$i.1 { INSERT INTO t1 VALUES(randomblob(10), randomblob(1000)); } # If a copy was made, open it and run the integrity-check. # if {$::state==2} { sqlite3 db2 xx_test.db do_test 1.$i.2 { execsql { PRAGMA integrity_check } db2 } "ok" do_test 1.$i.3 { execsql { SELECT count(*) FROM t1 } db2 } [expr $i-1] db2 close } } finish_test |
Changes to test/walpersist.test.
︙ | ︙ | |||
64 65 66 67 68 69 70 | } {0 1} do_test walpersist-1.11 { db close list [file exists test.db] [file exists test.db-wal] [file exists test.db-shm] } {1 1 1} # Make sure the journal_size_limit works to limit the size of the | | > > > | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | } {0 1} do_test walpersist-1.11 { db close list [file exists test.db] [file exists test.db-wal] [file exists test.db-shm] } {1 1 1} # Make sure the journal_size_limit works to limit the size of the # persisted wal file. In persistent-wal mode, any non-negative # journal_size_limit causes the WAL file to be truncated to zero bytes # when closing. # forcedelete test.db test.db-shm test.db-wal do_test walpersist-2.1 { sqlite3 db test.db db eval { PRAGMA journal_mode=WAL; PRAGMA wal_autocheckpoint=OFF; PRAGMA journal_size_limit=12000; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(50000)); UPDATE t1 SET x=randomblob(50000); } expr {[file size test.db-wal]>100000} } {1} do_test walpersist-2.2 { file_control_persist_wal db 1 db close concat [file exists test.db-wal] [file size test.db-wal] } {1 0} finish_test |