/ Check-in [cbf44ed9]
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:Require exclusive access to the db to wrap the wal file. Have "PRAGMA wal_checkpoint = restart" block for this.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | server-edition
Files: files | file ages | folders
SHA3-256: cbf44ed9758d577e1450b53e645b73c9ca1ee29d2354ce6375c234a41a063400
User & Date: dan 2017-05-12 18:52:27
Context
2017-05-13
19:07
Avoid running recovery while there is another read/write client. check-in: a38858a2 user: dan tags: server-edition
2017-05-12
18:52
Require exclusive access to the db to wrap the wal file. Have "PRAGMA wal_checkpoint = restart" block for this. check-in: cbf44ed9 user: dan tags: server-edition
2017-05-10
16:18
Fix a problem causing a lock to be held past the end of a transaction. Use a blocking lock to take the read-lock on page 1 taken by all transactions. check-in: 2584df3d user: dan tags: server-edition
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/wal.c.

1905
1906
1907
1908
1909
1910
1911



1912
1913
1914
1915
1916
1917
1918
....
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
....
3375
3376
3377
3378
3379
3380
3381





3382

3383
3384
3385
3386
3387
3388
3389
....
3427
3428
3429
3430
3431
3432
3433

3434
3435
3436
3437
3438
3439
3440
          ** checkpointed and behave accordingly. This seems unsafe though,
          ** as it would leave the system in a state where the contents of
          ** the wal-index header do not match the contents of the 
          ** file-system. To avoid this, update the wal-index header to
          ** indicate that the log file contains zero valid frames.  */
          walRestartHdr(pWal, salt1);
          rc = sqlite3OsTruncate(pWal->pWalFd, 0);



        }
        walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
      }
    }
  }

 walcheckpoint_out:
................................................................................
  if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){
    iFirst = pLive->mxFrame+1;
  }

  /* See if it is possible to write these frames into the start of the
  ** log file, instead of appending to it at pWal->hdr.mxFrame.
  */
  if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){
    return rc;
  }

  /* If this is the first frame written into the log, write the WAL
  ** header to the start of the WAL file. See comments at the top of
  ** this source file for a description of the WAL header format.
  */
................................................................................
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
    if( walIsServer(pWal) ){





      rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1);

    }else{
      rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
    }
    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
    }else if( rc==SQLITE_BUSY ){
      eMode2 = SQLITE_CHECKPOINT_PASSIVE;
................................................................................
  }

  /* Release the locks. */
  sqlite3WalEndWriteTransaction(pWal);
  walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
  pWal->ckptLock = 0;
  WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));

  return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
}

/* Return the value to pass to a sqlite3_wal_hook callback, the
** number of frames in the WAL at the point of the last commit since
** sqlite3WalCallback() was called.  If no commits have occurred since
** the last call, then return 0.







>
>
>







 







|







 







>
>
>
>
>
|
>







 







>







1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
....
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
....
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
....
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
          ** checkpointed and behave accordingly. This seems unsafe though,
          ** as it would leave the system in a state where the contents of
          ** the wal-index header do not match the contents of the 
          ** file-system. To avoid this, update the wal-index header to
          ** indicate that the log file contains zero valid frames.  */
          walRestartHdr(pWal, salt1);
          rc = sqlite3OsTruncate(pWal->pWalFd, 0);
        }else if( walIsServer(pWal) ){
          assert( eMode==SQLITE_CHECKPOINT_RESTART );
          walRestartHdr(pWal, salt1);
        }
        walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
      }
    }
  }

 walcheckpoint_out:
................................................................................
  if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){
    iFirst = pLive->mxFrame+1;
  }

  /* See if it is possible to write these frames into the start of the
  ** log file, instead of appending to it at pWal->hdr.mxFrame.
  */
  if( walIsServer(pWal)==0 && SQLITE_OK!=(rc = walRestartLog(pWal)) ){
    return rc;
  }

  /* If this is the first frame written into the log, write the WAL
  ** header to the start of the WAL file. See comments at the top of
  ** this source file for a description of the WAL header format.
  */
................................................................................
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
    if( walIsServer(pWal) ){
      if( eMode>=SQLITE_CHECKPOINT_RESTART ){
        /* Exclusive lock on page 1. This is exclusive access to the db. */
        rc = sqlite3ServerLock(pWal->pServer, 1, 1, 1);
      }else{
        /* Take the server write-lock ("page" 0) */
        rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1);
      }
    }else{
      rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
    }
    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
    }else if( rc==SQLITE_BUSY ){
      eMode2 = SQLITE_CHECKPOINT_PASSIVE;
................................................................................
  }

  /* Release the locks. */
  sqlite3WalEndWriteTransaction(pWal);
  walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
  pWal->ckptLock = 0;
  WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
  if( walIsServer(pWal) ) sqlite3ServerEnd(pWal->pServer);
  return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
}

/* Return the value to pass to a sqlite3_wal_hook callback, the
** number of frames in the WAL at the point of the last commit since
** sqlite3WalCallback() was called.  If no commits have occurred since
** the last call, then return 0.

Changes to test/serverwal.test.

57
58
59
60
61
62
63

64
65
66
67
68
69
70
..
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
...
105
106
107
108
109
110
111
























112
113
  } db2
} {}
do_test 2.2 {
  execsql COMMIT db
  execsql COMMIT db2
} {}
db close


#-------------------------------------------------------------------------
# That the wal file can be wrapped around.
#
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
................................................................................
  INSERT INTO ttt VALUES(7, 8);
  INSERT INTO ttt VALUES(9, 10);
} {wal}

do_test 3.1 {
  set N [file size test.db-wal]
  execsql {
    PRAGMA wal_checkpoint;
    INSERT INTO ttt VALUES(11, 12);
    INSERT INTO ttt VALUES(13, 14);
  }
  expr {$N == [file size test.db-wal]}
} {1}

#-------------------------------------------------------------------------
................................................................................
  PRAGMA integrity_check;
  BEGIN;
    UPDATE ttt SET b=a;
  ROLLBACK;
  PRAGMA integrity_check;
} {ok ok}

























finish_test








>







 







|







 







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


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  } db2
} {}
do_test 2.2 {
  execsql COMMIT db
  execsql COMMIT db2
} {}
db close
db2 close

#-------------------------------------------------------------------------
# That the wal file can be wrapped around.
#
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
................................................................................
  INSERT INTO ttt VALUES(7, 8);
  INSERT INTO ttt VALUES(9, 10);
} {wal}

do_test 3.1 {
  set N [file size test.db-wal]
  execsql {
    PRAGMA wal_checkpoint = restart;
    INSERT INTO ttt VALUES(11, 12);
    INSERT INTO ttt VALUES(13, 14);
  }
  expr {$N == [file size test.db-wal]}
} {1}

#-------------------------------------------------------------------------
................................................................................
  PRAGMA integrity_check;
  BEGIN;
    UPDATE ttt SET b=a;
  ROLLBACK;
  PRAGMA integrity_check;
} {ok ok}

reset_db
do_execsql_test 5.1 {
  CREATE TABLE xyz(a);
  PRAGMA journal_mode = wal;
  INSERT INTO xyz VALUES(1);
  INSERT INTO xyz VALUES(2);
  INSERT INTO xyz VALUES(3);
} {wal}

breakpoint

do_test 5.2 {
  sqlite3 db2 test.db
  execsql { SELECT * FROM xyz } db2
} {1 2 3}

do_execsql_test 5.3 {
  PRAGMA wal_checkpoint = restart 
} {0 0 0}

do_test 5.4 {
  execsql { SELECT * FROM xyz } db2
} {1 2 3}

finish_test