LCOV - code coverage report
Current view: top level - server/notedb - AllUsersAsyncUpdate.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 39 41 95.1 %
Date: 2022-11-19 15:00:39 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2019 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.notedb;
      16             : 
      17             : import static com.google.common.base.MoreObjects.firstNonNull;
      18             : import static com.google.common.base.Preconditions.checkState;
      19             : 
      20             : import com.google.common.collect.Iterables;
      21             : import com.google.common.collect.ListMultimap;
      22             : import com.google.common.collect.MultimapBuilder;
      23             : import com.google.common.flogger.FluentLogger;
      24             : import com.google.gerrit.git.RefUpdateUtil;
      25             : import com.google.gerrit.server.FanOutExecutor;
      26             : import com.google.gerrit.server.config.AllUsersName;
      27             : import com.google.gerrit.server.git.GitRepositoryManager;
      28             : import com.google.inject.Inject;
      29             : import java.io.IOException;
      30             : import java.util.Map;
      31             : import java.util.concurrent.ExecutorService;
      32             : import java.util.concurrent.Future;
      33             : import org.eclipse.jgit.lib.BatchRefUpdate;
      34             : import org.eclipse.jgit.lib.PersonIdent;
      35             : import org.eclipse.jgit.transport.PushCertificate;
      36             : 
      37             : /**
      38             :  * Performs an update on {@code All-Users} asynchronously if required. No-op in case no updates were
      39             :  * scheduled for asynchronous execution.
      40             :  */
      41             : public class AllUsersAsyncUpdate {
      42         110 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      43             : 
      44             :   private final ExecutorService executor;
      45             :   private final AllUsersName allUsersName;
      46             :   private final GitRepositoryManager repoManager;
      47             :   private final ListMultimap<String, ChangeDraftUpdate> draftUpdates;
      48             : 
      49             :   private PersonIdent serverIdent;
      50             : 
      51             :   @Inject
      52             :   AllUsersAsyncUpdate(
      53             :       @FanOutExecutor ExecutorService executor,
      54             :       AllUsersName allUsersName,
      55         110 :       GitRepositoryManager repoManager) {
      56         110 :     this.executor = executor;
      57         110 :     this.allUsersName = allUsersName;
      58         110 :     this.repoManager = repoManager;
      59         110 :     this.draftUpdates = MultimapBuilder.hashKeys().arrayListValues().build();
      60         110 :   }
      61             : 
      62             :   void setDraftUpdates(ListMultimap<String, ChangeDraftUpdate> draftUpdates) {
      63          26 :     checkState(isEmpty(), "attempted to set draft comment updates for async execution twice");
      64          26 :     boolean allPublishOnly =
      65          26 :         draftUpdates.values().stream().allMatch(ChangeDraftUpdate::canRunAsync);
      66          26 :     checkState(allPublishOnly, "not all updates can be run asynchronously");
      67             :     // Add deep copies to avoid any threading issues.
      68          26 :     for (Map.Entry<String, ChangeDraftUpdate> entry : draftUpdates.entries()) {
      69          26 :       this.draftUpdates.put(entry.getKey(), entry.getValue().copy());
      70          26 :     }
      71          26 :     if (draftUpdates.size() > 0) {
      72             :       // Save the PersonIdent for later so that we get consistent time stamps in the commit and ref
      73             :       // log.
      74          26 :       serverIdent = Iterables.get(draftUpdates.entries(), 0).getValue().serverIdent;
      75             :     }
      76          26 :   }
      77             : 
      78             :   /** Returns true if no operations should be performed on the repo. */
      79             :   boolean isEmpty() {
      80         110 :     return draftUpdates.isEmpty();
      81             :   }
      82             : 
      83             :   /** Executes repository update asynchronously. No-op in case no updates were scheduled. */
      84             :   void execute(PersonIdent refLogIdent, String refLogMessage, PushCertificate pushCert) {
      85         109 :     if (isEmpty()) {
      86         109 :       return;
      87             :     }
      88             : 
      89             :     @SuppressWarnings("unused")
      90          26 :     Future<?> possiblyIgnoredError =
      91          26 :         executor.submit(
      92             :             () -> {
      93          26 :               try (OpenRepo allUsersRepo = OpenRepo.open(repoManager, allUsersName)) {
      94          26 :                 allUsersRepo.addUpdatesNoLimits(draftUpdates);
      95          26 :                 allUsersRepo.flush();
      96          26 :                 BatchRefUpdate bru = allUsersRepo.repo.getRefDatabase().newBatchUpdate();
      97          26 :                 bru.setPushCertificate(pushCert);
      98          26 :                 if (refLogMessage != null) {
      99           4 :                   bru.setRefLogMessage(refLogMessage, false);
     100             :                 } else {
     101          23 :                   bru.setRefLogMessage(
     102          23 :                       firstNonNull(NoteDbUtil.guessRestApiHandler(), "Update NoteDb refs async"),
     103             :                       false);
     104             :                 }
     105          26 :                 bru.setRefLogIdent(refLogIdent != null ? refLogIdent : serverIdent);
     106          26 :                 bru.setAtomic(true);
     107          26 :                 allUsersRepo.cmds.addTo(bru);
     108          26 :                 bru.setAllowNonFastForwards(true);
     109          26 :                 RefUpdateUtil.executeChecked(bru, allUsersRepo.rw);
     110           0 :               } catch (IOException e) {
     111           0 :                 logger.atSevere().withCause(e).log(
     112             :                     "Failed to delete draft comments asynchronously after publishing them");
     113          26 :               }
     114          26 :             });
     115          26 :   }
     116             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750