1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. 22 */ 23 package org.opengrok.indexer.history; 24 25 import org.jetbrains.annotations.TestOnly; 26 import org.opengrok.indexer.configuration.RuntimeEnvironment; 27 import org.opengrok.indexer.logger.LoggerFactory; 28 import org.opengrok.indexer.util.Statistics; 29 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 import java.util.logging.Level; 34 import java.util.logging.Logger; 35 36 /** 37 * Helper class to split sequence of VCS changesets into number of intervals. 38 * This is then used in {@link Repository#createCache(HistoryCache, String)} 39 * to store history in chunks, for VCS repositories that support this. 40 */ 41 public class BoundaryChangesets { 42 private int cnt = 0; 43 private final List<String> result = new ArrayList<>(); 44 45 private final int maxCount; 46 private final RepositoryWithPerPartesHistory repository; 47 48 private static final Logger LOGGER = LoggerFactory.getLogger(BoundaryChangesets.class); 49 BoundaryChangesets(RepositoryWithPerPartesHistory repository)50 public BoundaryChangesets(RepositoryWithPerPartesHistory repository) { 51 this.repository = repository; 52 53 int globalPerPartesCount = RuntimeEnvironment.getInstance().getHistoryChunkCount(); 54 if (globalPerPartesCount > 0) { 55 this.maxCount = globalPerPartesCount; 56 } else { 57 this.maxCount = repository.getPerPartesCount(); 58 } 59 if (maxCount <= 1) { 60 throw new RuntimeException(String.format("per partes count for repository ''%s'' " + 61 "must be strictly greater than 1", repository.getDirectoryName())); 62 } 63 LOGGER.log(Level.FINER, "using history cache chunks with {0} entries for repository {1}", 64 new Object[]{this.maxCount, repository}); 65 } 66 reset()67 private void reset() { 68 cnt = 0; 69 result.clear(); 70 } 71 72 @TestOnly getMaxCount()73 int getMaxCount() { 74 return maxCount; 75 } 76 77 /** 78 * @param sinceRevision start revision ID 79 * @return immutable list of revision IDs denoting the intervals 80 * @throws HistoryException if there is problem traversing the changesets in the repository 81 */ getBoundaryChangesetIDs(String sinceRevision)82 public synchronized List<String> getBoundaryChangesetIDs(String sinceRevision) throws HistoryException { 83 reset(); 84 85 LOGGER.log(Level.FINE, "getting boundary changesets for ''{0}''", repository.getDirectoryName()); 86 Statistics stat = new Statistics(); 87 88 repository.accept(sinceRevision, this::visit); 89 90 // The changesets need to go from oldest to newest. 91 Collections.reverse(result); 92 93 stat.report(LOGGER, Level.FINE, 94 String.format("Done getting boundary changesets for ''%s'' (%d entries)", 95 repository.getDirectoryName(), result.size())); 96 97 return List.copyOf(result); 98 } 99 visit(String id)100 private void visit(String id) { 101 if (cnt != 0 && cnt % maxCount == 0) { 102 result.add(id); 103 } 104 cnt++; 105 } 106 } 107