Merge branch 'stable-2.16' into stable-3.0
* stable-2.16:
Auto-reindex: Restore indexIfNeeded for Groups
Change-Id: Ibca40c8d1a7af7bfa6764221a65d318bed73c13a
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
index e37234c..35445cc 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnable.java
@@ -14,22 +14,52 @@
package com.ericsson.gerrit.plugins.highavailability.autoreindex;
+import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexGroupHandler;
+import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.AbstractIndexRestApiServlet;
+import com.google.common.collect.Streams;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
+import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.inject.Inject;
+import java.io.IOException;
import java.sql.Timestamp;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
import java.util.Optional;
+import java.util.stream.Stream;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Repository;
public class GroupReindexRunnable extends ReindexRunnable<GroupReference> {
+ private static final FluentLogger log = FluentLogger.forEnclosingClass();
+
private final Groups groups;
+ private final GitRepositoryManager repoManager;
+ private final AllUsersName allUsers;
+ private final ForwardedIndexGroupHandler indexer;
@Inject
- public GroupReindexRunnable(IndexTs indexTs, OneOffRequestContext ctx, Groups groups) {
+ public GroupReindexRunnable(
+ ForwardedIndexGroupHandler indexer,
+ IndexTs indexTs,
+ OneOffRequestContext ctx,
+ Groups groups,
+ GitRepositoryManager repoManager,
+ AllUsersName allUsers) {
super(AbstractIndexRestApiServlet.IndexName.GROUP, indexTs, ctx);
this.groups = groups;
+ this.repoManager = repoManager;
+ this.allUsers = allUsers;
+ this.indexer = indexer;
}
@Override
@@ -39,6 +69,54 @@
@Override
protected Optional<Timestamp> indexIfNeeded(GroupReference g, Timestamp sinceTs) {
+ try {
+ Optional<InternalGroup> internalGroup = groups.getGroup(g.getUUID());
+ if (internalGroup.isPresent()) {
+ InternalGroup group = internalGroup.get();
+ Timestamp groupCreationTs = group.getCreatedOn();
+
+ Repository allUsersRepo = repoManager.openRepository(allUsers);
+
+ List<AccountGroupByIdAud> subGroupMembersAud =
+ groups.getSubgroupsAudit(allUsersRepo, g.getUUID());
+ Stream<Timestamp> groupIdAudAddedTs =
+ subGroupMembersAud.stream()
+ .map(AccountGroupByIdAud::getAddedOn)
+ .filter(Objects::nonNull);
+ Stream<Timestamp> groupIdAudRemovedTs =
+ subGroupMembersAud.stream()
+ .map(AccountGroupByIdAud::getRemovedOn)
+ .filter(Objects::nonNull);
+
+ List<AccountGroupMemberAudit> groupMembersAud =
+ groups.getMembersAudit(allUsersRepo, g.getUUID());
+ Stream<Timestamp> groupMemberAudAddedTs =
+ groupMembersAud.stream()
+ .map(AccountGroupMemberAudit::getAddedOn)
+ .filter(Objects::nonNull);
+ Stream<Timestamp> groupMemberAudRemovedTs =
+ groupMembersAud.stream()
+ .map(AccountGroupMemberAudit::getRemovedOn)
+ .filter(Objects::nonNull);
+
+ Optional<Timestamp> groupLastTs =
+ Streams.concat(
+ groupIdAudAddedTs,
+ groupIdAudRemovedTs,
+ groupMemberAudAddedTs,
+ groupMemberAudRemovedTs,
+ Stream.of(groupCreationTs))
+ .max(Comparator.naturalOrder());
+
+ if (groupLastTs.isPresent() && groupLastTs.get().after(sinceTs)) {
+ log.atInfo().log("Index {}/{}/{}", g.getUUID(), g.getName(), groupLastTs.get());
+ indexer.index(g.getUUID(), Operation.INDEX, Optional.empty());
+ return groupLastTs;
+ }
+ }
+ } catch (IOException | ConfigInvalidException e) {
+ log.atSevere().withCause(e).log("Reindex failed");
+ }
return Optional.empty();
}
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java
new file mode 100644
index 0000000..9517f29
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/autoreindex/GroupReindexRunnableTest.java
@@ -0,0 +1,200 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.ericsson.gerrit.plugins.highavailability.autoreindex;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexGroupHandler;
+import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwardedIndexingHandler.Operation;
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.Account.Id;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.AccountGroup.UUID;
+import com.google.gerrit.reviewdb.client.AccountGroupById;
+import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
+import com.google.gerrit.reviewdb.client.AccountGroupMember;
+import com.google.gerrit.reviewdb.client.AccountGroupMember.Key;
+import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.db.Groups;
+import com.google.gerrit.server.util.OneOffRequestContext;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GroupReindexRunnableTest {
+
+ @Mock private ForwardedIndexGroupHandler indexer;
+ @Mock private IndexTs indexTs;
+ @Mock private OneOffRequestContext ctx;
+ @Mock private Groups groups;
+ @Mock private GitRepositoryManager repoManager;
+ @Mock private AllUsersName allUsers;
+ @Mock private GroupReference groupReference;
+
+ private GroupReindexRunnable groupReindexRunnable;
+ private static UUID uuid;
+
+ @Before
+ public void setUp() throws Exception {
+ groupReindexRunnable =
+ new GroupReindexRunnable(indexer, indexTs, ctx, groups, repoManager, allUsers);
+ uuid = new UUID("123");
+ when(groupReference.getUUID()).thenReturn(uuid);
+ }
+
+ @Test
+ public void groupIsIndexedWhenItIsCreatedAfterLastGroupReindex() throws Exception {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Timestamp afterCurrentTime = new Timestamp(currentTime.getTime() + 1000L);
+ when(groups.getGroup(uuid)).thenReturn(getInternalGroup(afterCurrentTime));
+
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isTrue();
+ assertThat(groupLastTs.get()).isEqualTo(afterCurrentTime);
+ verify(indexer).index(uuid, Operation.INDEX, Optional.empty());
+ }
+
+ @Test
+ public void groupIsNotIndexedWhenItIsCreatedBeforeLastGroupReindex() throws Exception {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Timestamp beforeCurrentTime = new Timestamp(currentTime.getTime() - 1000L);
+ when(groups.getGroup(uuid)).thenReturn(getInternalGroup(beforeCurrentTime));
+
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isFalse();
+ verify(indexer, never()).index(uuid, Operation.INDEX, Optional.empty());
+ }
+
+ @Test
+ public void groupIsNotIndexedGroupReferenceNotPresent() {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isFalse();
+ }
+
+ @Test
+ public void groupIsIndexedWhenNewUserAddedAfterLastGroupReindex() throws Exception {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Timestamp afterCurrentTime = new Timestamp(currentTime.getTime() + 1000L);
+ when(groups.getGroup(uuid)).thenReturn(getInternalGroup(currentTime));
+ when(groups.getMembersAudit(any(), any()))
+ .thenReturn(
+ Collections.singletonList(
+ new AccountGroupMemberAudit(
+ new AccountGroupMember(new Key(new Id(1), new AccountGroup.Id(2))),
+ null,
+ afterCurrentTime)));
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isTrue();
+ assertThat(groupLastTs.get()).isEqualTo(afterCurrentTime);
+ verify(indexer).index(uuid, Operation.INDEX, Optional.empty());
+ }
+
+ @Test
+ public void groupIsIndexedWhenUserRemovedAfterLastGroupReindex() throws Exception {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Timestamp afterCurrentTime = new Timestamp(currentTime.getTime() + 1000L);
+ Timestamp beforeCurrentTime = new Timestamp(currentTime.getTime() - 1000L);
+
+ AccountGroupMemberAudit accountGroupMemberAudit =
+ new AccountGroupMemberAudit(
+ new AccountGroupMember(new Key(new Id(1), new AccountGroup.Id(2))),
+ null,
+ beforeCurrentTime);
+ accountGroupMemberAudit.removed(new Id(1), afterCurrentTime);
+ when(groups.getGroup(uuid)).thenReturn(getInternalGroup(currentTime));
+ when(groups.getMembersAudit(any(), any()))
+ .thenReturn(Collections.singletonList(accountGroupMemberAudit));
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isTrue();
+ assertThat(groupLastTs.get()).isEqualTo(afterCurrentTime);
+ verify(indexer).index(uuid, Operation.INDEX, Optional.empty());
+ }
+
+ @Test
+ public void groupIsIndexedWhenItIsSubGroupAddedAfterLastGroupReindex() throws Exception {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Timestamp afterCurrentTime = new Timestamp(currentTime.getTime() + 1000L);
+ Timestamp beforeCurrentTime = new Timestamp(currentTime.getTime() - 1000L);
+ when(groups.getGroup(uuid)).thenReturn(getInternalGroup(beforeCurrentTime));
+ when(groups.getSubgroupsAudit(any(), any()))
+ .thenReturn(
+ Collections.singletonList(
+ new AccountGroupByIdAud(
+ new AccountGroupById(
+ new AccountGroupById.Key(new AccountGroup.Id(1), new UUID("123"))),
+ null,
+ afterCurrentTime)));
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isTrue();
+ assertThat(groupLastTs.get()).isEqualTo(afterCurrentTime);
+ verify(indexer).index(uuid, Operation.INDEX, Optional.empty());
+ }
+
+ @Test
+ public void groupIsIndexedWhenItIsSubGroupRemovedAfterLastGroupReindex() throws Exception {
+ Timestamp currentTime = Timestamp.valueOf(LocalDateTime.now());
+ Timestamp afterCurrentTime = new Timestamp(currentTime.getTime() + 1000L);
+ Timestamp beforeCurrentTime = new Timestamp(currentTime.getTime() - 1000L);
+
+ AccountGroupByIdAud accountGroupByIdAud =
+ new AccountGroupByIdAud(
+ new AccountGroupById(new AccountGroupById.Key(new AccountGroup.Id(1), new UUID("123"))),
+ null,
+ beforeCurrentTime);
+ accountGroupByIdAud.removed(new Id(1), afterCurrentTime);
+ when(groups.getGroup(uuid)).thenReturn(getInternalGroup(beforeCurrentTime));
+ when(groups.getSubgroupsAudit(any(), any()))
+ .thenReturn(Collections.singletonList(accountGroupByIdAud));
+
+ Optional<Timestamp> groupLastTs =
+ groupReindexRunnable.indexIfNeeded(groupReference, currentTime);
+ assertThat(groupLastTs.isPresent()).isTrue();
+ assertThat(groupLastTs.get()).isEqualTo(afterCurrentTime);
+ verify(indexer).index(uuid, Operation.INDEX, Optional.empty());
+ }
+
+ private Optional<InternalGroup> getInternalGroup(Timestamp timestamp) {
+ AccountGroup accountGroup =
+ new AccountGroup(new AccountGroup.NameKey("Test"), new AccountGroup.Id(1), uuid, timestamp);
+ return Optional.ofNullable(
+ InternalGroup.create(
+ accountGroup,
+ ImmutableSet.<Id>builder().build(),
+ ImmutableSet.<UUID>builder().build(),
+ null));
+ }
+}