Julian Foad wrote on Thu, 22 Mar 2018 22:24 +0000:
> This question keeps coming up and I feel we should provide an accurate
> answer, even if the procedure is not "supported".
> Any corrections to my current best effort, below?
As a first step:
Check $REPOS/format, $REPOS/db/fs-type, $REPOS/db/format.
> R=(the highest wanted revision number)
> 1. Any WCs (or parts of WCs) at a revision > R should first be updated
> back to a revision <= R, otherwise those WCs will be broken, possibly in
> subtle ways.
Perhaps emphasize that the subtle corruptions wouldn't necessarily be
detected/reported by clients.
> 2. on the server:
> 2a. freeze the repo
> Use any external means to prevent write access to the repo.
> (You cannot use 'svnadmin freeze' with the commands below as the script,
> because the 'svnadmin recover' below would fail. I tried.)
Obviously, we should implement a 'svnadmin recover --my-grandparent-process-has-a-write-lock' flag. :-)
More seriously, 'svnadmin freeze $SHELL' is an effective method of
implementing an ad-hoc lock. The advantage would be that the lock is
guaranteed to be effective, regardless of write access configuration.
The disadvantage would be that the steps 'recover' does would need to be
effected manually. (More on this at the end.)
> Let's assume you're in the repo dir and running Bash or a similar shell.
For users this will need to be clarified, that we mean $REPOS_DIR, as
opposed to $REPOS_DIR/db and to a directory in a working copy.
> Collect some info:
> $ R=998902 # the desired new head revision
Having established a write lock, use 'svnadmin info' to check that $((R+1))
hasn't been packed.
> $ OLD_R=$(cat db/current)
OLD_R=$(svnlook youngest ./)
No reason to use a back door here.
I note that despite the names, $OLD_R is younger than $R. (This is
probably not very important.)
> $ OLD_TXNS=$(svnadmin lstxns .)
> * set the 'current' file to R, or (arguably slightly easier/safer)
> delete it; running 'svnadmin recover' in a later step will recreate it:
The problem I see with telling people to manually create the file is
that running "echo 12 > current" (with two spaces) on Windows would or
"echo 12>current" on Unix would corrupt the file. (The former would
create a leading space, IIRC; the latter might be interpreted as a
"redirect file descriptor 12" by some shells.)
On Unix we could advise people to run «printf '%d\n' >db/current $R».
Unsure about Windows.
> $ rm db/current
> * delete the discarded revision file(s):
> $ for F in $(seq $((R+1)) $OLD_R); do rm -f db/revs/*/$F
> db/revprops/*/$F; done
seq(1) is Linux specific. I'm not aware of a good portable way to do a
"for number in range(42, 80)" loop in sh. The cleanest option I'm aware
of is to use a while loop with «x=$(( $x + 1 ))» at the end.
> * clear out any references to discarded revs in 'rep-cache.db'
> You'll need the 'sqlite3' command-line utility program, which you may
> need to install from an operating system package. On Ubuntu the package
> is named 'sqlite3'.
> $ sqlite3 db/rep-cache.db "delete from rep_cache where revision > $R"
State that the expected output is no output.
If it's not possible to install sqlite3(1), deleting rep-cache.db is a workaround.
> * delete any pending transactions
> $ svnadmin rmtxns . $OLD_TXNS
> "Clean up":
> $ svnadmin recover .
> (Q: Does 'recover' do any useful magic beyond recreating the 'current'
Yes. At least it already deletes too-new rep-cache entries. See recover_body().
> > 2c. un-freeze the repo
Before doing this, restart reader processes to flush in-processes caches
that reference the expunged revisions.
I wonder if it would be possible to say: Run 'svnadmin freeze $SHELL';
therein delete 'db/current', the revision files, and txns; restart all
reader processes; exit that subshell and run 'svnadmin recover'. This
would save the admin from having to manually run 'recover'. Between
'freeze' exiting and 'recover' grabbing the write lock, no writes will
happen (because there's no db/current file and no extant svn_fs_t handle
had been opened while a db/current file was existing) and any reads (if
they don't trip on failure to initialise ffd->youngest_rev_cache) would
> 'svnadmin verify' doesn't detect if the rep-cache still contains too-new
> revs, which would cause silent corruption during later commits if
> allowed to remain in place. We should fix that. (I will file an issue.)
> 'svnadmin recover' could do the rep-cache recovery step. Any reason it
> should not?
Doesn't it already do that step? See the call the
svn_fs_fs__del_rep_cache() in recover_body().
Received on 2018-03-23 07:04:22 CET