Allow locking refs whilst filtering for fetch requests Allow the lock/unlock pattern when filtering the refs to fetch. This mechanism is used for making sure that the refs selected for the fetch replication operation do not move throughout the replication process. This is needed for preventing a ref-update operation happening concurrently with the replication process invalidating the filtering validity. Change-Id: I6a30aff43a81f00d671554186eb6e796446b148d
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java index 939a05b..a5c66c0 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
@@ -109,6 +109,7 @@ private final Optional<PullReplicationApiRequestMetrics> apiRequestMetrics; private DynamicItem<ReplicationFetchFilter> replicationFetchFilter; private boolean succeeded; + private Map<String, AutoCloseable> fetchLocks; @Inject FetchOne( @@ -507,6 +508,8 @@ } catch (IOException e) { notifyRefReplicatedIOException(); throw e; + } finally { + unlockRefSpecs(fetchLocks); } return fetchRefSpecs; } @@ -556,6 +559,25 @@ .collect(Collectors.toList()); } + public void unlockRefSpecs(Map<String, AutoCloseable> locks) { + if (locks == null) { + return; + } + + locks.forEach( + (key, value) -> { + try { + value.close(); + } catch (Exception e) { + repLog.error( + String.format( + "[%s] Error when unlocking ref %s:%s", taskIdHex, projectName.get(), key), + e); + } + }); + locks.clear(); + } + public List<FetchRefSpec> safeGetFetchRefSpecs() { try { return getFetchRefSpecs(); @@ -610,7 +632,11 @@ refs.stream().map(FetchRefSpec::refName).collect(Collectors.toUnmodifiableSet()); Set<String> filteredRefNames = replicationFetchFilter() - .map(f -> f.filter(this.projectName.get(), refsNames)) + .map( + f -> { + fetchLocks = f.filterAndLock(this.projectName.get(), refsNames); + return fetchLocks.keySet(); + }) .orElse(refsNames); return refs.stream() .filter(refSpec -> filteredRefNames.contains(refSpec.refName()))
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationFetchFilter.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationFetchFilter.java index 77f4185..17afe94 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationFetchFilter.java +++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ReplicationFetchFilter.java
@@ -15,7 +15,9 @@ package com.googlesource.gerrit.plugins.replication.pull; import com.google.gerrit.extensions.annotations.ExtensionPoint; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * Filter that is invoked before a set of remote refs are fetched from a remote instance. @@ -26,4 +28,9 @@ public interface ReplicationFetchFilter { Set<String> filter(String projectName, Set<String> fetchRefs); + + default Map<String, AutoCloseable> filterAndLock(String projectName, Set<String> fetchRefs) { + return filter(projectName, fetchRefs).stream() + .collect(Collectors.toMap(ref -> ref, ref -> () -> {})); + } }