Follow-up to r29036: Now that the "mergeinfo" transaction file is no
[svn.git] / contrib / server-side / backup-recipe.sh
blobd4000bba0bf027e28f1b59545793eb357eb44288
1 #!/bin/sh
3 ###########################################################################
4 # #
5 # This shell script demonstrates a backup/restore recipe for live #
6 # Subversion repositories, using a standard full+incrementals process. #
7 # #
8 # This script is intended only as an example; the idea is that you #
9 # can read over it, understand how it works (it's extensively commented) #
10 # and then implement real backup and restore scripts based on this #
11 # recipe. #
12 # #
13 # To reiterate: this is *not* a backup and restore solution. It's #
14 # really just documentation, in the form of code with comments. #
15 # #
16 # If you do implement your own scripts based on the recipe here, and #
17 # your implementations are generic enough to be generally useful, #
18 # please post them to dev@subversion.tigris.org. It would be great if #
19 # we could offer a real solution, and not just a description of one. #
20 # #
21 # This recipe is distilled from the Berkeley DB documentation, see #
22 # http://www.sleepycat.com/docs/ref/transapp/archival.html. #
23 # #
24 # See also http://www.sleepycat.com/docs/ref/transapp/reclimit.html for #
25 # for possible problems using standard 'cp' in this recipe. #
26 # #
27 ###########################################################################
29 # High-level overview of the full backup recipe:
31 # 1. Ask BDB's db_archive for a list of unused log files.
33 # 2. Copy the entire db/ dir to the backup area.
35 # 3. Recopy all the logfiles to the backup area. There may be more
36 # logfiles now than there were when step (1) ran.
38 # 4. Remove the logfiles listed as inactive in step (1) from the
39 # repository, though not from the backup.
41 # High-level overview of the incremental backup recipe:
43 # 1. Just copy the Berkeley logfiles to a backup area.
45 # High-level overview of the restoration recipe:
47 # 1. Copy all the datafiles and logfiles back to the repository, in
48 # the same order they were backed up.
50 # 2. Run Berkeley's "catastrophic recovery" command on the repository.
52 # That's it. Here we go...
54 # You might need to customize some of these paths.
55 SVN=svn
56 SVNADMIN=svnadmin
57 SVNLOOK=svnlook
58 # See http://www.sleepycat.com/docs/utility/db_archive.html:
59 DB_ARCHIVE=/usr/local/BerkeleyDB.4.2/bin/db_archive
60 # See http://www.sleepycat.com/docs/utility/db_recover.html:
61 DB_RECOVER=/usr/local/BerkeleyDB.4.2/bin/db_recover
63 # This is just source data to generate repository activity.
64 # Any binary file of about 64k will do, it doesn't have to be /bin/ls.
65 DATA_BLOB=/bin/ls
67 # You shouldn't need to customize below here.
68 SANDBOX=`pwd`/backups-test-tmp
69 FULL_BACKUPS=${SANDBOX}/full
70 INCREMENTAL_PREFIX=${SANDBOX}/incremental-logs
71 RECORDS=${SANDBOX}/records
72 PROJ=myproj
73 REPOS=${PROJ}-repos
75 rm -rf ${SANDBOX}
76 mkdir ${SANDBOX}
77 mkdir ${RECORDS}
79 cd ${SANDBOX}
81 ${SVNADMIN} create --bdb-log-keep ${REPOS}
82 ${SVN} co file://${SANDBOX}/${REPOS} wc
84 cd wc
86 # Put in enough data for us to exercise the logfiles.
87 cp ${DATA_BLOB} ./a1
88 cp ${DATA_BLOB} ./b1
89 cp ${DATA_BLOB} ./c1
90 ${SVN} -q add a1 b1 c1
91 ${SVN} -q ci -m "Initial add."
93 echo "Created test data."
95 cd ..
97 # Exercise the logfiles by moving data around a lot. Note that we
98 # avoid adds-with-history, since those cause much less Berkeley
99 # activity than plain adds.
101 # Call this from the parent of wc, that is, with $SANDBOX as CWD.
102 # Pass one argument, a number, indicating how many cycles of exercise
103 # you want. The more cycles, the more logfiles will be generated.
104 # The ratio is about two cycles per logfile.
105 function exercise
107 limit=${1}
109 saved_cwd=`pwd`
110 cd ${SANDBOX}/wc
112 echo ""
114 while [ ${i} -le ${limit} ]; do
115 mv a1 a2
116 mv b1 b2
117 mv c1 c2
118 ${SVN} -q rm a1 b1 c1
119 ${SVN} -q add a2 b2 c2
120 ${SVN} -q ci -m "Move 1s to 2s, but not as cheap copies."
122 mv a2 a1
123 mv b2 b1
124 mv c2 c1
125 ${SVN} -q rm a2 b2 c2
126 ${SVN} -q add a1 b1 c1
127 ${SVN} -q ci -m "Move 2s back to 1s, same way."
129 echo "Exercising repository, pass ${i} of ${limit}."
130 i=`dc -e "${i} 1 + p"`
131 done
132 echo ""
134 cd ${saved_cwd}
137 # Generate some logfile activity.
138 exercise 10
140 # Do a full backup.
141 head=`${SVNLOOK} youngest ${REPOS}`
142 echo "Starting full backup (at r${head})..."
143 mkdir ${FULL_BACKUPS}
144 mkdir ${FULL_BACKUPS}/${PROJ}
145 mkdir ${FULL_BACKUPS}/${PROJ}/repos
146 mkdir ${FULL_BACKUPS}/${PROJ}/logs
147 cd ${REPOS}/db
148 ${DB_ARCHIVE} > ${RECORDS}/${PROJ}-full-backup-inactive-logfiles
149 cd ../..
150 cp -a ${REPOS} ${FULL_BACKUPS}/${PROJ}/repos/
151 cd ${REPOS}/db
152 for logfile in `${DB_ARCHIVE} -l`; do
153 # For maximum paranoia, we want repository activity *while* we're
154 # making the full backup.
155 exercise 5
156 cp ${logfile} ${FULL_BACKUPS}/${PROJ}/logs
157 done
158 cat ${RECORDS}/${PROJ}-full-backup-inactive-logfiles | xargs rm -f
159 cd ../..
160 echo "Full backup completed (r${head} was head when started)."
162 # Do the incremental backups for a nominal week.
163 for day in 1 2 3 4 5 6; do
164 exercise 5
165 head=`${SVNLOOK} youngest ${REPOS}`
166 echo "Starting incremental backup ${day} (at r${head})..."
167 mkdir ${INCREMENTAL_PREFIX}-${day}
168 mkdir ${INCREMENTAL_PREFIX}-${day}/${PROJ}
169 cd ${REPOS}/db
170 ${DB_ARCHIVE} > ${RECORDS}/${PROJ}-incr-backup-${day}-inactive-logfiles
171 for logfile in `${DB_ARCHIVE} -l`; do
172 # For maximum paranoia, we want repository activity *while* we're
173 # making the incremental backup. But if we did commits with each
174 # logfile copy, this script would be quite slow (Fibonacci effect).
175 # So we only exercise on the last two "days" of incrementals.
176 if [ ${day} -ge 5 ]; then
177 exercise 3
179 cp ${logfile} ${INCREMENTAL_PREFIX}-${day}/${PROJ}
180 done
181 cat ${RECORDS}/${PROJ}-incr-backup-${day}-inactive-logfiles | xargs rm -f
182 cd ../..
183 echo "Incremental backup ${day} done (r${head} was head when started)."
184 done
186 # The last revision a restoration is guaranteed to contain is whatever
187 # was head at the start of the last incremental backup.
188 last_guaranteed_rev=${head}
190 # Make the repository vanish, so we can restore it.
191 mv ${REPOS} was_${REPOS}
193 echo ""
194 echo "Oliver Cromwell has destroyed the repository! Restoration coming
195 up..."
196 echo ""
198 # Restore.
200 # After copying the full repository backup over, we remove the shared
201 # memory segments and the dav/* stuff. Recovery recreates the shmem
202 # segments, and anything in dav/* is certainly obsolete if we're doing
203 # a restore.
205 # Note that we use db_recover instead of 'svnadmin recover'. This is
206 # because we want to pass the -c ('catastrophic') flag to db_recover.
207 # As of Subversion 1.0.x, there is no '--catastrophic' flag to
208 # 'svnadmin recover', unfortunately.
209 cp -a ${FULL_BACKUPS}/${PROJ}/repos/${REPOS} .
210 cp -a ${FULL_BACKUPS}/${PROJ}/logs/* ${REPOS}/db
211 rm -rf ${REPOS}/db/__db*
212 rm -rf ${REPOS}/dav/*
213 cd ${REPOS}/db
214 ${DB_RECOVER} -ce
215 cd ../..
216 head=`${SVNLOOK} youngest ${REPOS}`
217 echo ""
218 echo "(Restored from full backup to r${head}...)"
219 for day in 1 2 3 4 5 6; do
220 cd ${REPOS}/db
221 cp ${INCREMENTAL_PREFIX}-${day}/${PROJ}/* .
222 ${DB_RECOVER} -ce
223 cd ../..
224 head=`${SVNLOOK} youngest ${REPOS}`
225 echo "(Restored from incremental-${day} to r${head}...)"
226 done
227 echo ""
228 echo "Restoration complete. All hail the King."
230 # Verify the restoration.
231 was_head=`${SVNLOOK} youngest was_${REPOS}`
232 restored_head=`${SVNLOOK} youngest ${REPOS}`
233 echo ""
234 echo "Highest revision in original repository: ${was_head}"
235 echo "Highest revision restored: ${restored_head}"
236 echo ""
237 echo "(It's okay if restored is less than original, even much less.)"
239 if [ ${restored_head} -lt ${last_guaranteed_rev} ]; then
240 echo ""
241 echo "Restoration failed because r${restored_head} is too low --"
242 echo "should have restored to at least r${last_guaranteed_rev}."
243 exit 1
246 # Looks like we restored at least to the minimum required revision.
247 # Let's do some spot checks, though.
249 echo ""
250 echo "Comparing logs up to r${restored_head} for both repositories..."
251 ${SVN} log -v -r1:${restored_head} file://`pwd`/was_${REPOS} > a
252 ${SVN} log -v -r1:${restored_head} file://`pwd`/${REPOS} > b
253 if cmp a b; then
254 echo "Done comparing logs."
255 else
256 echo "Log comparison failed -- restored repository is not right."
257 exit 1
260 echo ""
261 echo "Comparing r${restored_head} exported trees from both repositories..."
262 ${SVN} -q export -r${restored_head} file://`pwd`/was_${REPOS} orig-export
263 ${SVN} -q export -r${restored_head} file://`pwd`/${REPOS} restored-export
264 if diff -q -r orig-export restored-export; then
265 echo "Done comparing r${restored_head} exported trees."
266 else
267 echo "Recursive diff failed -- restored repository is not right."
270 echo ""
271 echo "Done."