Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add tests and fix bugs in WAL locking mechanism. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
c18077f2465fc34830f11c9832e76be5 |
User & Date: | dan 2010-04-14 18:50:08.000 |
Context
2010-04-15
| ||
02:37 | Bring over the recent query planner enhancements from the trunk. (check-in: 82969f27e5 user: drh tags: wal) | |
2010-04-14
| ||
18:50 | Add tests and fix bugs in WAL locking mechanism. (check-in: c18077f246 user: dan tags: wal) | |
18:06 | Add tests to check inter-process WAL locking. (check-in: 9435f31358 user: dan tags: wal) | |
Changes
Changes to src/log.c.
︙ | ︙ | |||
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 | /* Normal reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_A|LOG_REGION_B)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_B)) /* Region D reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_D)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_D)) /* Checkpointer lock operations */ || (op==LOG_WRLOCK && mRegion==(LOG_REGION_B|LOG_REGION_C)) || (op==LOG_WRLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_B|LOG_REGION_C)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_A|LOG_REGION_B|LOG_REGION_C)) | > | 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | /* Normal reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_A|LOG_REGION_B)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_B)) /* Region D reader lock operations */ || (op==LOG_RDLOCK && mRegion==(LOG_REGION_D)) || (op==LOG_RDLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_D)) /* Checkpointer lock operations */ || (op==LOG_WRLOCK && mRegion==(LOG_REGION_B|LOG_REGION_C)) || (op==LOG_WRLOCK && mRegion==(LOG_REGION_A)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_B|LOG_REGION_C)) || (op==LOG_UNLOCK && mRegion==(LOG_REGION_A|LOG_REGION_B|LOG_REGION_C)) |
︙ | ︙ | |||
1330 1331 1332 1333 1334 1335 1336 | */ void sqlite3LogMaxpgno(Log *pLog, Pgno *pPgno){ assert( pLog->isLocked ); *pPgno = pLog->hdr.nPage; } /* | < < < | | > > > > > > | > | > | | > > | > > | 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | */ void sqlite3LogMaxpgno(Log *pLog, Pgno *pPgno){ assert( pLog->isLocked ); *pPgno = pLog->hdr.nPage; } /* ** This function returns SQLITE_OK if the caller may write to the database. ** Otherwise, if the caller is operating on a snapshot that has already ** been overwritten by another writer, SQLITE_BUSY is returned. */ int sqlite3LogWriteLock(Log *pLog, int op){ assert( pLog->isLocked ); if( op ){ /* Obtain the writer lock */ int rc = logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_WRLOCK); if( rc!=SQLITE_OK ){ return rc; } /* If this is connection is a region D, then the SHARED lock on region ** D has just been upgraded to EXCLUSIVE. But no lock at all is held on ** region A. This means that if the write-transaction is committed ** and this connection downgrades to a reader, it will be left with no ** lock at all. And its snapshot could get clobbered by a checkpoint ** operation. ** ** To stop this from happening, grab a SHARED lock on region A now. ** This should always be successful, as the only time a client holds ** an EXCLUSIVE lock on region A, it must also be holding an EXCLUSIVE ** lock on region C (a checkpointer does this). This is not possible, ** as this connection currently has the EXCLUSIVE lock on region C. */ if( pLog->isLocked==LOG_REGION_D ){ logLockRegion(pLog, LOG_REGION_A, LOG_RDLOCK); pLog->isLocked = LOG_REGION_A; } if( memcmp(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)) ){ logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_UNLOCK); return SQLITE_BUSY; } pLog->isWriteLocked = 1; }else if( pLog->isWriteLocked ){ logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_UNLOCK); memcpy(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)); |
︙ | ︙ |
Changes to test/wal.test.
︙ | ︙ | |||
443 444 445 446 447 448 449 | proc busyhandler x { if {$x==3} { sql3 { BEGIN; SELECT * FROM t1 } } if {$x==4} { sql2 COMMIT } if {$x<5} { return 0 } return 1 } db busy busyhandler | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | proc busyhandler x { if {$x==3} { sql3 { BEGIN; SELECT * FROM t1 } } if {$x==4} { sql2 COMMIT } if {$x<5} { return 0 } return 1 } db busy busyhandler do_test wal-10.$tn.17 { execsql { PRAGMA checkpoint } } {} do_test wal-10.$tn.18 { sql3 { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.19 { catchsql { INSERT INTO t1 VALUES(13, 14) } } {1 {database is locked}} do_test wal-10.$tn.20 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.21 { sql3 COMMIT } {} do_test wal-10.$tn.22 { execsql { INSERT INTO t1 VALUES(13, 14) } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} # Set [db3] up as a "region D" reader again. Then upgrade it to a writer # and back down to a reader. Then, check that a checkpoint is not possible # (as [db3] still has a snapshot locked). # do_test wal-10.$tn.23 { execsql { PRAGMA checkpoint } } {} do_test wal-10.$tn.24 { sql2 { BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-10.$tn.25 { execsql { PRAGMA checkpoint } } {} do_test wal-10.$tn.26 { catchsql { INSERT INTO t1 VALUES(15, 16) } } {1 {database is locked}} do_test wal-10.$tn.27 { sql3 { INSERT INTO t1 VALUES(15, 16) } } {} do_test wal-10.$tn.28 { code3 { set ::STMT [sqlite3_prepare db3 "SELECT * FROM t1" -1 TAIL] sqlite3_step $::STMT } sql3 COMMIT execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} db busy {} do_test wal-10.$tn.29 { execsql { INSERT INTO t1 VALUES(17, 18) } catchsql { PRAGMA checkpoint } } {1 {database is locked}} do_test wal-10.$tn.30 { code3 { sqlite3_finalize $::STMT } execsql { PRAGMA checkpoint } } {} # At one point, if a reader failed to upgrade to a writer because it # was reading an old snapshot, the write-locks were not being released. # Test that this bug has been fixed. # do_test wal-10.$tn.31 { execsql { BEGIN ; SELECT * FROM t1 } sql2 { INSERT INTO t1 VALUES(19, 20) } catchsql { INSERT INTO t1 VALUES(21, 22) } } {1 {database is locked}} do_test wal-10.$tn.32 { # This statement would fail when the bug was present. sql2 { INSERT INTO t1 VALUES(21, 22) } } {} do_test wal-10.$tn.33 { execsql { SELECT * FROM t1 ; COMMIT } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18} do_test wal-10.$tn.34 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22} catch { db close } catch { code2 { db2 close } } catch { code3 { db3 close } } catch { close $::code2_chan } catch { close $::code3_chan } } |
︙ | ︙ |