test-transaction-rollback-on-revlog-split.t
448 lines
| 11.9 KiB
| text/troff
|
Tads3Lexer
/ tests / test-transaction-rollback-on-revlog-split.t
Joerg Sonnenberger
|
r48064 | Test correctness of revlog inline -> non-inline transition | ||
---------------------------------------------------------- | ||||
r51348 | We test various file length and naming pattern as this created issue in the | |||
past. | ||||
r51237 | Helper extension to intercept renames and kill process | |||
Joerg Sonnenberger
|
r48065 | |||
r51237 | $ cat > $TESTTMP/intercept_before_rename.py << EOF | |||
Joerg Sonnenberger
|
r48065 | > import os | ||
r51237 | > import signal | |||
Joerg Sonnenberger
|
r48065 | > from mercurial import extensions, util | ||
> | ||||
> def extsetup(ui): | ||||
r51242 | > def rename(orig, src, dest, *args, **kwargs): | |||
> path = util.normpath(dest) | ||||
> if path.endswith(b'data/file.i'): | ||||
r51237 | > os.kill(os.getpid(), signal.SIGKILL) | |||
r51242 | > return orig(src, dest, *args, **kwargs) | |||
> extensions.wrapfunction(util, 'rename', rename) | ||||
Joerg Sonnenberger
|
r48065 | > EOF | ||
r51238 | $ cat > $TESTTMP/intercept_after_rename.py << EOF | |||
> import os | ||||
> import signal | ||||
> from mercurial import extensions, util | ||||
> | ||||
> def extsetup(ui): | ||||
> def close(orig, *args, **kwargs): | ||||
> path = util.normpath(args[0]._atomictempfile__name) | ||||
> r = orig(*args, **kwargs) | ||||
> if path.endswith(b'/.hg/store/data/file.i'): | ||||
> os.kill(os.getpid(), signal.SIGKILL) | ||||
> return r | ||||
> extensions.wrapfunction(util.atomictempfile, 'close', close) | ||||
r51242 | > def extsetup(ui): | |||
> def rename(orig, src, dest, *args, **kwargs): | ||||
> path = util.normpath(dest) | ||||
> r = orig(src, dest, *args, **kwargs) | ||||
> if path.endswith(b'data/file.i'): | ||||
> os.kill(os.getpid(), signal.SIGKILL) | ||||
> return r | ||||
> extensions.wrapfunction(util, 'rename', rename) | ||||
r51238 | > EOF | |||
r51237 | $ cat > $TESTTMP/killme.py << EOF | |||
> import os | ||||
> import signal | ||||
> | ||||
> def killme(ui, repo, hooktype, **kwargs): | ||||
> os.kill(os.getpid(), signal.SIGKILL) | ||||
> EOF | ||||
r51239 | $ cat > $TESTTMP/reader_wait_split.py << EOF | |||
> import os | ||||
> import signal | ||||
> from mercurial import extensions, revlog, testing | ||||
> def _wait_post_load(orig, self, *args, **kwargs): | ||||
> wait = b'data/file' in self.radix | ||||
> if wait: | ||||
> testing.wait_file(b"$TESTTMP/writer-revlog-split") | ||||
> r = orig(self, *args, **kwargs) | ||||
> if wait: | ||||
> testing.write_file(b"$TESTTMP/reader-index-read") | ||||
> testing.wait_file(b"$TESTTMP/writer-revlog-unsplit") | ||||
> return r | ||||
> | ||||
> def extsetup(ui): | ||||
> extensions.wrapfunction(revlog.revlog, '_loadindex', _wait_post_load) | ||||
> EOF | ||||
r51237 | setup a repository for tests | |||
---------------------------- | ||||
$ cat >> $HGRCPATH << EOF | ||||
> [format] | ||||
> revlog-compression=none | ||||
> EOF | ||||
$ hg init troffset-computation | ||||
$ cd troffset-computation | ||||
r51346 | $ files=" | |||
> file | ||||
> Directory_With,Special%Char/Complex_File.babar | ||||
> foo/bar/babar_celeste/foo | ||||
r51348 | > 1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/f | |||
r51346 | > " | |||
$ for f in $files; do | ||||
> mkdir -p `dirname $f` | ||||
> done | ||||
$ for f in $files; do | ||||
> printf '%20d' '1' > $f | ||||
> done | ||||
r51237 | $ hg commit -Aqma | |||
r51346 | $ for f in $files; do | |||
> printf '%1024d' '1' > $f | ||||
> done | ||||
r51237 | $ hg commit -Aqmb | |||
r51346 | $ for f in $files; do | |||
> printf '%20d' '1' > $f | ||||
> done | ||||
r51237 | $ hg commit -Aqmc | |||
r51346 | $ for f in $files; do | |||
> dd if=/dev/zero of=$f bs=1k count=128 > /dev/null 2>&1 | ||||
> done | ||||
r51242 | $ hg commit -AqmD --traceback | |||
r51237 | ||||
Reference size: | ||||
$ f -s file | ||||
file: size=131072 | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.d: size=132139 | ||||
.hg/store/data/file.i: size=256 | ||||
$ cd .. | ||||
Test a hard crash after the file was split but before the transaction was committed | ||||
=================================================================================== | ||||
Arseniy Alekseyev
|
r49422 | Test offset computation to correctly factor in the index entries themselves. | ||
Joerg Sonnenberger
|
r48065 | Also test that the new data size has the correct size if the transaction is aborted | ||
after the index has been replaced. | ||||
Arseniy Alekseyev
|
r49422 | Test repo has commits a, b, c, D, where D is large (grows the revlog enough that it | ||
transitions to non-inline storage). The clone initially has changes a, b | ||||
and will transition to non-inline storage when adding c, D. | ||||
If the transaction adding c, D is rolled back, then we don't undo the revlog split, | ||||
but truncate the index and the data to remove both c and D. | ||||
Joerg Sonnenberger
|
r48064 | |||
Arseniy Alekseyev
|
r49422 | |||
r51237 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-copy | |||
Joerg Sonnenberger
|
r48064 | $ cd troffset-computation-copy | ||
Joerg Sonnenberger
|
r48065 | |||
Reference size: | ||||
r51237 | $ f -s file | |||
file: size=1024 | ||||
Joerg Sonnenberger
|
r48065 | $ f -s .hg/store/data/file* | ||
.hg/store/data/file.i: size=1174 | ||||
Joerg Sonnenberger
|
r48064 | $ cat > .hg/hgrc <<EOF | ||
> [hooks] | ||||
r51237 | > pretxnchangegroup = python:$TESTTMP/killme.py:killme | |||
Joerg Sonnenberger
|
r48064 | > EOF | ||
#if chg | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
[255] | ||||
#else | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
pacien
|
r51277 | *Killed* (glob) | ||
r51237 | [137] | |||
Joerg Sonnenberger
|
r48064 | #endif | ||
r51237 | ||||
r51242 | The inline revlog still exist, but a split version exist next to it | |||
r51237 | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.d: size=132139 | ||||
r51242 | .hg/store/data/file.i: size=132395 | |||
.hg/store/data/file.i.s: size=256 | ||||
r51237 | ||||
Joerg Sonnenberger
|
r48065 | |||
Arseniy Alekseyev
|
r49422 | The first file.i entry should match the "Reference size" above. | ||
Joerg Sonnenberger
|
r48065 | The first file.d entry is the temporary record during the split, | ||
r51237 | ||||
r51242 | A "temporary file" entry exist for the split index. | |||
Joerg Sonnenberger
|
r48065 | |||
$ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | ||||
data/file.i 1174 | ||||
data/file.d 0 | ||||
r51242 | $ cat .hg/store/journal.backupfiles | tr -s '\000' ' ' | tr -s '\00' ' '| grep data/file | |||
data/file.i data/journal.backup.file.i 0 | ||||
data/file.i.s 0 | ||||
recover is rolling the split back, the fncache is still valid | ||||
Joerg Sonnenberger
|
r48066 | $ hg recover | ||
rolling back interrupted transaction | ||||
(verify step skipped, run `hg verify` to check your repository content) | ||||
$ f -s .hg/store/data/file* | ||||
r51242 | .hg/store/data/file.i: size=1174 | |||
Joerg Sonnenberger
|
r48066 | $ hg tip | ||
r51348 | changeset: 1:cc8dfb126534 | |||
Joerg Sonnenberger
|
r48066 | tag: tip | ||
user: test | ||||
date: Thu Jan 01 00:00:00 1970 +0000 | ||||
Arseniy Alekseyev
|
r49422 | summary: b | ||
Joerg Sonnenberger
|
r48066 | |||
Valentin Gatien-Baron
|
r48688 | $ hg verify -q | ||
Valentin Gatien-Baron
|
r48689 | $ hg debugrebuildfncache --only-data | ||
r51242 | fncache already up to date | |||
Valentin Gatien-Baron
|
r48689 | $ hg verify -q | ||
Joerg Sonnenberger
|
r48065 | $ cd .. | ||
r51237 | Test a hard crash right before the index is move into place | |||
=========================================================== | ||||
Joerg Sonnenberger
|
r48066 | |||
Now retry the procedure but intercept the rename of the index and check that | ||||
Joerg Sonnenberger
|
r48065 | the journal does not contain the new index size. This demonstrates the edge case | ||
where the data file is left as garbage. | ||||
r51237 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-copy2 | |||
Joerg Sonnenberger
|
r48065 | $ cd troffset-computation-copy2 | ||
r51237 | ||||
Reference size: | ||||
$ f -s file | ||||
file: size=1024 | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.i: size=1174 | ||||
Joerg Sonnenberger
|
r48065 | $ cat > .hg/hgrc <<EOF | ||
> [extensions] | ||||
r51237 | > intercept_rename = $TESTTMP/intercept_before_rename.py | |||
Joerg Sonnenberger
|
r48065 | > EOF | ||
#if chg | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
r51242 | searching for changes | |||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
Joerg Sonnenberger
|
r48065 | [255] | ||
#else | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
r51242 | searching for changes | |||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
pacien
|
r51277 | *Killed* (glob) | ||
r51237 | [137] | |||
Joerg Sonnenberger
|
r48065 | #endif | ||
r51237 | ||||
r51242 | The inline revlog still exist, but a split version exist next to it | |||
r51237 | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.d: size=132139 | ||||
.hg/store/data/file.i: size=132395 | ||||
r51242 | .hg/store/data/file.i.s: size=256 | |||
r51237 | ||||
Joerg Sonnenberger
|
r48065 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | ||
data/file.i 1174 | ||||
data/file.d 0 | ||||
r51242 | ||||
recover is rolling the split back, the fncache is still valid | ||||
Joerg Sonnenberger
|
r48066 | |||
$ hg recover | ||||
rolling back interrupted transaction | ||||
(verify step skipped, run `hg verify` to check your repository content) | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.i: size=1174 | ||||
$ hg tip | ||||
r51348 | changeset: 1:cc8dfb126534 | |||
Joerg Sonnenberger
|
r48066 | tag: tip | ||
user: test | ||||
date: Thu Jan 01 00:00:00 1970 +0000 | ||||
Arseniy Alekseyev
|
r49422 | summary: b | ||
Joerg Sonnenberger
|
r48066 | |||
Valentin Gatien-Baron
|
r48688 | $ hg verify -q | ||
Joerg Sonnenberger
|
r48065 | $ cd .. | ||
Joerg Sonnenberger
|
r48066 | |||
r51238 | Test a hard crash right after the index is move into place | |||
=========================================================== | ||||
Now retry the procedure but intercept the rename of the index. | ||||
$ hg clone --quiet --rev 1 troffset-computation troffset-computation-crash-after-rename | ||||
$ cd troffset-computation-crash-after-rename | ||||
Reference size: | ||||
$ f -s file | ||||
file: size=1024 | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.i: size=1174 | ||||
$ cat > .hg/hgrc <<EOF | ||||
> [extensions] | ||||
> intercept_rename = $TESTTMP/intercept_after_rename.py | ||||
> EOF | ||||
#if chg | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
r51242 | searching for changes | |||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
r51238 | [255] | |||
#else | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
r51242 | searching for changes | |||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
pacien
|
r51277 | *Killed* (glob) | ||
r51238 | [137] | |||
#endif | ||||
r51242 | The inline revlog was over written on disk | |||
r51238 | ||||
$ f -s .hg/store/data/file* | ||||
.hg/store/data/file.d: size=132139 | ||||
.hg/store/data/file.i: size=256 | ||||
$ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | ||||
data/file.i 1174 | ||||
data/file.d 0 | ||||
r51242 | ||||
recover is rolling the split back, the fncache is still valid | ||||
r51238 | ||||
$ hg recover | ||||
rolling back interrupted transaction | ||||
r51242 | (verify step skipped, run `hg verify` to check your repository content) | |||
r51238 | $ f -s .hg/store/data/file* | |||
r51242 | .hg/store/data/file.i: size=1174 | |||
r51238 | $ hg tip | |||
r51348 | changeset: 1:cc8dfb126534 | |||
r51238 | tag: tip | |||
user: test | ||||
date: Thu Jan 01 00:00:00 1970 +0000 | ||||
summary: b | ||||
$ hg verify -q | ||||
$ cd .. | ||||
r51237 | Have the transaction rollback itself without any hard crash | |||
=========================================================== | ||||
Joerg Sonnenberger
|
r48066 | |||
Repeat the original test but let hg rollback the transaction. | ||||
r51237 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-copy-rb | |||
Joerg Sonnenberger
|
r48066 | $ cd troffset-computation-copy-rb | ||
$ cat > .hg/hgrc <<EOF | ||||
> [hooks] | ||||
> pretxnchangegroup = false | ||||
> EOF | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
searching for changes | ||||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
transaction abort! | ||||
rollback completed | ||||
abort: pretxnchangegroup hook exited with status 1 | ||||
[40] | ||||
r51237 | ||||
r51242 | The split was rollback | |||
r51237 | ||||
Joerg Sonnenberger
|
r48066 | $ f -s .hg/store/data/file* | ||
r51242 | .hg/store/data/file.d: size=0 | |||
.hg/store/data/file.i: size=1174 | ||||
r51237 | ||||
Joerg Sonnenberger
|
r48066 | $ hg tip | ||
r51348 | changeset: 1:cc8dfb126534 | |||
Joerg Sonnenberger
|
r48066 | tag: tip | ||
user: test | ||||
date: Thu Jan 01 00:00:00 1970 +0000 | ||||
Arseniy Alekseyev
|
r49422 | summary: b | ||
Joerg Sonnenberger
|
r48066 | |||
Valentin Gatien-Baron
|
r48688 | $ hg verify -q | ||
Joerg Sonnenberger
|
r48066 | $ cd .. | ||
r51239 | Read race | |||
========= | ||||
We check that a client that started reading a revlog (its index) after the | ||||
split and end reading (the data) after the rollback should be fine | ||||
$ hg clone --quiet --rev 1 troffset-computation troffset-computation-race | ||||
$ cd troffset-computation-race | ||||
$ cat > .hg/hgrc <<EOF | ||||
> [hooks] | ||||
> pretxnchangegroup=$RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/reader-index-read $TESTTMP/writer-revlog-split | ||||
> pretxnclose = false | ||||
> EOF | ||||
start a reader | ||||
$ hg cat --rev 0 file \ | ||||
> --config "extensions.wait_read=$TESTTMP/reader_wait_split.py" \ | ||||
> 2> $TESTTMP/reader.stderr \ | ||||
> > $TESTTMP/reader.stdout & | ||||
Do a failed pull in // | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
searching for changes | ||||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
transaction abort! | ||||
rollback completed | ||||
abort: pretxnclose hook exited with status 1 | ||||
[40] | ||||
$ touch $TESTTMP/writer-revlog-unsplit | ||||
$ wait | ||||
The reader should be fine | ||||
$ cat $TESTTMP/reader.stderr | ||||
$ cat $TESTTMP/reader.stdout | ||||
1 (no-eol) | ||||
$ cd .. | ||||
r51240 | ||||
pending hooks | ||||
============= | ||||
We checks that hooks properly see the inside of the transaction, while other process don't. | ||||
$ hg clone --quiet --rev 1 troffset-computation troffset-computation-hooks | ||||
$ cd troffset-computation-hooks | ||||
$ cat > .hg/hgrc <<EOF | ||||
> [hooks] | ||||
> pretxnclose.01-echo = hg cat -r 'max(all())' file | f --size | ||||
> pretxnclose.02-echo = $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/hook-done $TESTTMP/hook-tr-ready | ||||
> pretxnclose.03-abort = false | ||||
> EOF | ||||
$ ( | ||||
> $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/hook-tr-ready;\ | ||||
> hg cat -r 'max(all())' file | f --size;\ | ||||
> touch $TESTTMP/hook-done | ||||
> ) >stdout 2>stderr & | ||||
$ hg pull ../troffset-computation | ||||
pulling from ../troffset-computation | ||||
searching for changes | ||||
adding changesets | ||||
adding manifests | ||||
adding file changes | ||||
size=131072 | ||||
transaction abort! | ||||
rollback completed | ||||
abort: pretxnclose.03-abort hook exited with status 1 | ||||
[40] | ||||
$ cat stdout | ||||
size=1024 | ||||
$ cat stderr | ||||
$ cd .. | ||||