Author: julianfoad
Date: Fri Aug 24 10:48:37 2018
New Revision: 1838813

URL: http://svn.apache.org/viewvc?rev=1838813&view=rev
Log:
Let 'svnadmin recover' prune the rep-cache even if it is disabled.

Part of issue #4077: "FSFS recover should prune unborn revisions from
rep-cache.db".

Also add tests for both cases (enabled, disabled).

Pruning the rep-cache even if disabled was included as r1213716 in the
original fix for issue #4077, first released in Subversion 1.7.3, but was
reverted in r1367674 (issue #4214, recovery should not create rep-cache.db)
and so omitted from Subversion 1.8 and later series of releases.

* subversion/libsvn_fs_fs/recovery.c
  (recover_body): Prune the rep-cache no matter whether it's in use.

* subversion/tests/cmdline/svnadmin_tests.py
  (read_rep_cache,
   check_recover_prunes_rep_cache): New functions.
  (recover_prunes_rep_cache_when_enabled,
   recover_prunes_rep_cache_when_disabled): New tests.
  (test_list): Run them.

Modified:
    subversion/trunk/subversion/libsvn_fs_fs/recovery.c
    subversion/trunk/subversion/tests/cmdline/svnadmin_tests.py

Modified: subversion/trunk/subversion/libsvn_fs_fs/recovery.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_fs/recovery.c?rev=1838813&r1=1838812&r2=1838813&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_fs/recovery.c (original)
+++ subversion/trunk/subversion/libsvn_fs_fs/recovery.c Fri Aug 24 10:48:37 2018
@@ -471,9 +471,15 @@ recover_body(void *baton, apr_pool_t *po
     }
 
   /* Prune younger-than-(newfound-youngest) revisions from the rep
-     cache if sharing is enabled taking care not to create the cache
-     if it does not exist. */
-  if (ffd->rep_sharing_allowed)
+     cache, taking care not to create the cache if it does not exist.
+
+     We do this whenever rep-cache.db exists, whether it's currently enabled
+     or not, to prevent a data loss that could result from having revisions
+     created after this 'recover' operation referring to rep-cache.db rows
+     that were created before the recover and that point to revisions younger-
+     than-(newfound-youngest).
+   */
+  if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT)
     {
       svn_boolean_t rep_cache_exists;
 

Modified: subversion/trunk/subversion/tests/cmdline/svnadmin_tests.py
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/svnadmin_tests.py?rev=1838813&r1=1838812&r2=1838813&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/svnadmin_tests.py Fri Aug 24 
10:48:37 2018
@@ -53,6 +53,23 @@ Wimp = svntest.testcase.Wimp_deco
 SkipDumpLoadCrossCheck = svntest.testcase.SkipDumpLoadCrossCheck_deco
 Item = svntest.wc.StateItem
 
+def read_rep_cache(repo_dir):
+  """Return the rep-cache contents as a dict {hash: (rev, index, ...)}.
+  """
+  db_path = os.path.join(repo_dir, 'db', 'rep-cache.db')
+  db1 = svntest.sqlite3.connect(db_path)
+  schema1 = db1.execute("pragma user_version").fetchone()[0]
+  # Can't test newer rep-cache schemas with an old built-in SQLite.
+  if schema1 >= 2 and svntest.sqlite3.sqlite_version_info < (3, 8, 2):
+    raise svntest.Failure("Can't read rep-cache schema %d using old "
+                          "Python-SQLite version %s < (3,8,2)" %
+                           (schema1,
+                            svntest.sqlite3.sqlite_version_info))
+
+  content = { row[0]: row[1:] for row in
+              db1.execute("select * from rep_cache") }
+  return content
+
 def check_hotcopy_bdb(src, dst):
   "Verify that the SRC BDB repository has been correctly copied to DST."
   ### TODO: This function should be extended to verify all hotcopied files,
@@ -3843,6 +3860,57 @@ def dump_no_canonicalize_svndate(sbox):
   dump_lines = svntest.actions.run_and_verify_dump(sbox.repo_dir)
   assert propval + '\n' in dump_lines
 
+def check_recover_prunes_rep_cache(sbox, enable_rep_sharing):
+  """Check 'recover' prunes the rep-cache while enable-rep-sharing is
+     true/false.
+  """
+  # Remember the initial rep cache content.
+  rep_cache_r1 = read_rep_cache(sbox.repo_dir)
+  #print '\n'.join([h + ": " + repr(ref) for h, ref in rep_cache_r1.items()])
+
+  # Commit one new rep and check the rep-cache is extended.
+  sbox.simple_append('iota', 'New line.\n')
+  sbox.simple_commit()
+  rep_cache_r2 = read_rep_cache(sbox.repo_dir)
+  assert len(rep_cache_r2) == len(rep_cache_r1) + 1
+
+  # To test 'recover' while rep-sharing is disabled, disable it now.
+  if not enable_rep_sharing:
+    fsfs_conf = svntest.main.get_fsfs_conf_file_path(sbox.repo_dir)
+    svntest.main.file_append(fsfs_conf,
+                             "[rep-sharing]\n"
+                             "enable-rep-sharing = false\n")
+
+  # Break r2 in such a way that 'recover' will discard it
+  head_rev_path = fsfs_file(sbox.repo_dir, 'revs', '2')
+  os.remove(head_rev_path)
+  current_path = os.path.join(sbox.repo_dir, 'db', 'current')
+  svntest.main.file_write(current_path, '1\n')
+
+  # Recover back to r1.
+  svntest.actions.run_and_verify_svnadmin(None, [],
+                                          "recover", sbox.repo_dir)
+
+  # Check the rep-cache is pruned.
+  rep_cache_recovered = read_rep_cache(sbox.repo_dir)
+  assert rep_cache_recovered == rep_cache_r1
+
+@Issue(4077)
+@SkipUnless(svntest.main.is_fs_type_fsfs)
+def recover_prunes_rep_cache_when_enabled(sbox):
+  "recover prunes rep cache when enabled"
+  sbox.build()
+
+  check_recover_prunes_rep_cache(sbox, enable_rep_sharing=True)
+
+@Issue(4077)
+@SkipUnless(svntest.main.is_fs_type_fsfs)
+def recover_prunes_rep_cache_when_disabled(sbox):
+  "recover prunes rep cache when disabled"
+  sbox.build()
+
+  check_recover_prunes_rep_cache(sbox, enable_rep_sharing=False)
+
 ########################################################################
 # Run the tests
 
@@ -3918,6 +3986,8 @@ test_list = [ None,
               dump_invalid_filtering_option,
               load_issue4725,
               dump_no_canonicalize_svndate,
+              recover_prunes_rep_cache_when_enabled,
+              recover_prunes_rep_cache_when_disabled,
              ]
 
 if __name__ == '__main__':


Reply via email to