LCOV - code coverage report
Current view: top level - server/change - AbandonUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 43 55 78.2 %
Date: 2022-11-19 15:00:39 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2015 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.change;
      16             : 
      17             : import com.google.common.collect.ImmutableListMultimap;
      18             : import com.google.common.collect.ListMultimap;
      19             : import com.google.common.flogger.FluentLogger;
      20             : import com.google.gerrit.entities.Project;
      21             : import com.google.gerrit.exceptions.StorageException;
      22             : import com.google.gerrit.index.query.QueryParseException;
      23             : import com.google.gerrit.server.InternalUser;
      24             : import com.google.gerrit.server.config.ChangeCleanupConfig;
      25             : import com.google.gerrit.server.query.change.ChangeData;
      26             : import com.google.gerrit.server.query.change.ChangeQueryBuilder;
      27             : import com.google.gerrit.server.query.change.ChangeQueryProcessor;
      28             : import com.google.gerrit.server.update.BatchUpdate;
      29             : import com.google.inject.Inject;
      30             : import com.google.inject.Provider;
      31             : import com.google.inject.Singleton;
      32             : import java.util.ArrayList;
      33             : import java.util.Collection;
      34             : import java.util.List;
      35             : import java.util.concurrent.TimeUnit;
      36             : 
      37             : @Singleton
      38             : public class AbandonUtil {
      39         138 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      40             : 
      41             :   private final ChangeCleanupConfig cfg;
      42             :   private final Provider<ChangeQueryProcessor> queryProvider;
      43             :   private final ChangeQueryBuilder queryBuilder;
      44             :   private final BatchAbandon batchAbandon;
      45             :   private final InternalUser internalUser;
      46             : 
      47             :   @Inject
      48             :   AbandonUtil(
      49             :       ChangeCleanupConfig cfg,
      50             :       InternalUser.Factory internalUserFactory,
      51             :       Provider<ChangeQueryProcessor> queryProvider,
      52             :       ChangeQueryBuilder queryBuilder,
      53         138 :       BatchAbandon batchAbandon) {
      54         138 :     this.cfg = cfg;
      55         138 :     this.queryProvider = queryProvider;
      56         138 :     this.queryBuilder = queryBuilder;
      57         138 :     this.batchAbandon = batchAbandon;
      58         138 :     internalUser = internalUserFactory.create();
      59         138 :   }
      60             : 
      61             :   public void abandonInactiveOpenChanges(BatchUpdate.Factory updateFactory) {
      62           1 :     if (cfg.getAbandonAfter() <= 0) {
      63           0 :       return;
      64             :     }
      65             : 
      66             :     try {
      67           1 :       String query =
      68           1 :           "status:new age:" + TimeUnit.MILLISECONDS.toMinutes(cfg.getAbandonAfter()) + "m";
      69           1 :       if (!cfg.getAbandonIfMergeable()) {
      70           1 :         query += " -is:mergeable";
      71             :       }
      72             : 
      73           1 :       List<ChangeData> changesToAbandon =
      74           1 :           queryProvider.get().enforceVisibility(false).query(queryBuilder.parse(query)).entities();
      75             :       ImmutableListMultimap.Builder<Project.NameKey, ChangeData> builder =
      76           1 :           ImmutableListMultimap.builder();
      77           1 :       for (ChangeData cd : changesToAbandon) {
      78           1 :         builder.put(cd.project(), cd);
      79           1 :       }
      80             : 
      81           1 :       int count = 0;
      82           1 :       ListMultimap<Project.NameKey, ChangeData> abandons = builder.build();
      83           1 :       String message = cfg.getAbandonMessage();
      84           1 :       for (Project.NameKey project : abandons.keySet()) {
      85           1 :         Collection<ChangeData> changes = getValidChanges(abandons.get(project), query);
      86             :         try {
      87           1 :           batchAbandon.batchAbandon(updateFactory, project, internalUser, changes, message);
      88           1 :           count += changes.size();
      89           0 :         } catch (Exception e) {
      90           0 :           StringBuilder msg = new StringBuilder("Failed to auto-abandon inactive change(s):");
      91           0 :           for (ChangeData change : changes) {
      92           0 :             msg.append(" ").append(change.getId().get());
      93           0 :           }
      94           0 :           msg.append(".");
      95           0 :           logger.atSevere().withCause(e).log("%s", msg);
      96           1 :         }
      97           1 :       }
      98           1 :       logger.atInfo().log("Auto-Abandoned %d of %d changes.", count, changesToAbandon.size());
      99           0 :     } catch (QueryParseException | StorageException e) {
     100           0 :       logger.atSevere().withCause(e).log(
     101             :           "Failed to query inactive open changes for auto-abandoning.");
     102           1 :     }
     103           1 :   }
     104             : 
     105             :   private Collection<ChangeData> getValidChanges(Collection<ChangeData> changes, String query)
     106             :       throws QueryParseException {
     107           1 :     Collection<ChangeData> validChanges = new ArrayList<>();
     108           1 :     for (ChangeData cd : changes) {
     109           1 :       String newQuery = query + " change:" + cd.getId();
     110           1 :       List<ChangeData> changesToAbandon =
     111             :           queryProvider
     112           1 :               .get()
     113           1 :               .enforceVisibility(false)
     114           1 :               .query(queryBuilder.parse(newQuery))
     115           1 :               .entities();
     116           1 :       if (!changesToAbandon.isEmpty()) {
     117           1 :         validChanges.add(cd);
     118             :       } else {
     119           0 :         logger.atFine().log(
     120             :             "Change data with id \"%s\" does not satisfy the query \"%s\""
     121             :                 + " any more, hence skipping it in clean up",
     122           0 :             cd.getId(), query);
     123             :       }
     124           1 :     }
     125           1 :     return validChanges;
     126             :   }
     127             : }

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