Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a way to remove old commits #681

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ void historyAndDiff() {
final List<Commit> commits = centralDogmaRepo.history().get(new Revision(2), Revision.HEAD).join();
assertThat(commits.stream()
.map(Commit::summary)
.collect(toImmutableList())).containsExactly("commit3", "commit2");
.collect(toImmutableList())).containsExactly("commit2", "commit3");
assertThat(centralDogmaRepo.diff("/foo.json")
.get(Revision.INIT, Revision.HEAD)
.join())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ public int major() {
return major;
}

/**
* Tells whether the {@link #major()} of this {@link Revision} is lower than the {@code other}
* {@link Revision}.
*/
public boolean isLowerThan(Revision other) {
requireNonNull(other, "other");
return major < other.major();
}

/**
* Returns {@code 0}.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2018 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* https://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.linecorp.centraldogma.common;

/**
* A {@link RevisionNotFoundException} that is raised when attempted to access a removed revision
* by rolling repository.
*/
public class RolledRevisionAccessException extends RevisionNotFoundException {

private static final long serialVersionUID = -8291795114909224145L;

/**
* Creates a new instance.
*/
public RolledRevisionAccessException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ public class GitRepositoryBenchmark {
private int previousCommits;

private File repoDir;
private GitRepository repo;
private GitRepositoryV2 repo;
private int currentRevision;

@Setup
public void init() throws Exception {
repoDir = Files.createTempDirectory("jmh-gitrepository.").toFile();
repo = new GitRepository(mock(Project.class), repoDir, ForkJoinPool.commonPool(),
System.currentTimeMillis(), AUTHOR, null);
repo = new GitRepositoryV2(mock(Project.class), repoDir, ForkJoinPool.commonPool(),
System.currentTimeMillis(), AUTHOR, null);
currentRevision = 1;

for (int i = 0; i < previousCommits; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ public final class CentralDogmaBuilder {
@Nullable
private CorsConfig corsConfig;

@Nullable
private CommitRetentionConfig commitRetentionConfig;

/**
* Creates a new builder with the specified data directory.
*/
Expand Down Expand Up @@ -527,6 +530,18 @@ public CentralDogmaBuilder writeQuotaPerRepository(int writeQuota, int timeWindo
return this;
}

/**
* Sets the configuration for retaining commits in a {@link Repository}.
* Commits are retained for at least {@code minRetentionDays}. If the number of commits is less than
* {@code minRetentionCommits}, commits are not removed even after {@code minRetentionDays} have passed.
* Specify {@code minRetentionCommits} to {@code 0} to retain all commits.
*/
public CentralDogmaBuilder commitRetention(int minRetentionCommits, int minRetentionDays,
@Nullable String schedule) {
commitRetentionConfig = new CommitRetentionConfig(minRetentionCommits, minRetentionDays, schedule);
return this;
}

/**
* Sets the {@link MeterRegistry} used to collect metrics.
*/
Expand Down Expand Up @@ -577,6 +592,6 @@ private CentralDogmaConfig buildConfig() {
webAppEnabled, webAppTitle, mirroringEnabled, numMirroringThreads,
maxNumFilesPerMirror, maxNumBytesPerMirror, replicationConfig,
null, accessLogFormat, authCfg, quotaConfig,
corsConfig);
commitRetentionConfig, corsConfig);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ public final class CentralDogmaConfig {
@Nullable
private final QuotaConfig writeQuotaPerRepository;

@Nullable
private final CommitRetentionConfig commitRetentionConfig;

@Nullable
private final CorsConfig corsConfig;

Expand Down Expand Up @@ -169,11 +172,10 @@ public final class CentralDogmaConfig {
@JsonProperty("accessLogFormat") @Nullable String accessLogFormat,
@JsonProperty("authentication") @Nullable AuthConfig authConfig,
@JsonProperty("writeQuotaPerRepository") @Nullable QuotaConfig writeQuotaPerRepository,
@JsonProperty("commitRetention") @Nullable CommitRetentionConfig commitRetentionConfig,
@JsonProperty("cors") @Nullable CorsConfig corsConfig) {

this.dataDir = requireNonNull(dataDir, "dataDir");
this.ports = ImmutableList.copyOf(requireNonNull(ports, "ports"));
this.corsConfig = corsConfig;
checkArgument(!ports.isEmpty(), "ports must have at least one port.");
this.tls = tls;
this.trustedProxyAddresses = trustedProxyAddresses;
Expand Down Expand Up @@ -224,6 +226,8 @@ public final class CentralDogmaConfig {
ports.stream().anyMatch(ServerPort::hasProxyProtocol));

this.writeQuotaPerRepository = writeQuotaPerRepository;
this.commitRetentionConfig = commitRetentionConfig;
this.corsConfig = corsConfig;
}

/**
Expand Down Expand Up @@ -456,11 +460,20 @@ public AuthConfig authConfig() {
* Returns the maximum allowed write quota per {@link Repository}.
*/
@Nullable
@JsonProperty("writeQuotaPerRepository")
@JsonProperty
public QuotaConfig writeQuotaPerRepository() {
return writeQuotaPerRepository;
}

/**
* Returns the {@link CommitRetentionConfig}.
*/
@Nullable
@JsonProperty("commitRetention")
public CommitRetentionConfig commitRetentionConfig() {
return commitRetentionConfig;
}

/**
* Returns the {@link CorsConfig}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright 2021 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* https://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.linecorp.centraldogma.server;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;

import javax.annotation.Nullable;

import com.cronutils.model.Cron;
import com.cronutils.model.CronType;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.parser.CronParser;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import com.linecorp.centraldogma.server.storage.repository.Repository;

/**
* A configuration for retaining commits in a {@link Repository}.
*/
public final class CommitRetentionConfig {

// TODO(minwoox): Make this configurable.
// The minimum of minRetentionCommits
private static final int MINIMUM_MINIMUM_RETENTION_COMMITS = 5000;

private static final String DEFAULT_SCHEDULE = "0 0 * * * ?"; // Every day
private static final CronParser cronParser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(
CronType.QUARTZ));

private final int minRetentionCommits;
private final int minRetentionDays;
private final Cron schedule;

/**
* Creates a new instance.
*/
@JsonCreator
public CommitRetentionConfig(@JsonProperty("minRetentionCommits") int minRetentionCommits,
@JsonProperty("minRetentionDays") int minRetentionDays,
@JsonProperty("schedule") @Nullable String schedule) {
checkArgument(minRetentionCommits == 0 || minRetentionCommits >= MINIMUM_MINIMUM_RETENTION_COMMITS,
"minRetentionCommits: %s (expected: 0 || >= %s)",
minRetentionCommits, MINIMUM_MINIMUM_RETENTION_COMMITS);
checkArgument(minRetentionDays >= 0,
"minRetentionDays: %s (expected: >= 0)", minRetentionDays);
this.minRetentionCommits = minRetentionCommits;
this.minRetentionDays = minRetentionDays;
this.schedule = cronParser.parse(firstNonNull(schedule, DEFAULT_SCHEDULE));
}

/**
* Returns the minimum number of commits that a {@link Repository} should retain. {@code 0} means that
* commits are not removed.
*/
public int minRetentionCommits() {
return minRetentionCommits;
}

/**
* Returns the minimum number of days of a commit that a {@link Repository} should retain.
*/
public int minRetentionDays() {
return minRetentionDays;
}

/**
* Returns the schedule of the job that removes old commits.
*/
public Cron schedule() {
return schedule;
}

@Override
public String toString() {
return toStringHelper(this).add("minRetentionCommits", minRetentionCommits)
.add("minRetentionDays", minRetentionDays)
.add("schedule", schedule)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public final String toString() {
return toStringHelper().toString();
}

// TODO(minwoox): Do not expose ToStringHelper to public.
MoreObjects.ToStringHelper toStringHelper() {
return MoreObjects.toStringHelper(this)
.add("type", type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.linecorp.centraldogma.server.command;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

import javax.annotation.Nullable;
Expand Down Expand Up @@ -48,6 +49,7 @@
@Type(value = RemoveRepositoryCommand.class, name = "REMOVE_REPOSITORY"),
@Type(value = PurgeRepositoryCommand.class, name = "PURGE_REPOSITORY"),
@Type(value = UnremoveRepositoryCommand.class, name = "UNREMOVE_REPOSITORY"),
@Type(value = CreateRollingRepositoryCommand.class, name = "CREATE_ROLLING_REPOSITORY"),
@Type(value = NormalizingPushCommand.class, name = "NORMALIZING_PUSH"),
@Type(value = PushAsIsCommand.class, name = "PUSH"),
@Type(value = CreateSessionCommand.class, name = "CREATE_SESSIONS"),
Expand Down Expand Up @@ -246,6 +248,28 @@ static Command<Void> purgeRepository(@Nullable Long timestamp, Author author,
return new PurgeRepositoryCommand(timestamp, author, projectName, repositoryName);
}

/**
* Returns a new {@link Command} which is used to create a rolling repository.
*
* @param projectName the name of the project
* @param repositoryName the name of the repository which is supposed to be purged
* @param initialRevision the initial {@link Revision} of the rolling repository
* @param minRetentionCommits the configured number of recent commits that a repository should retain
* @param minRetentionDays the configured number of days for recent commits that a repository should retain
*/
static Command<Void> createRollingRepository(
String projectName, String repositoryName, Revision initialRevision,
int minRetentionCommits, int minRetentionDays) {
requireNonNull(projectName, "projectName");
requireNonNull(repositoryName, "repositoryName");
requireNonNull(initialRevision, "initialRevision");
checkArgument(minRetentionCommits > 0, "minRetentionCommits: %s (expected: > 0)",
minRetentionCommits);
checkArgument(minRetentionDays > 0, "minRetentionDays: %s (expected: > 0)", minRetentionDays);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question) isn't it possible for minRetentionDays to be 0?

Suggested change
checkArgument(minRetentionDays > 0, "minRetentionDays: %s (expected: > 0)", minRetentionDays);
checkArgument(minRetentionDays >= 0, "minRetentionDays: %s (expected: >= 0)", minRetentionDays);

return new CreateRollingRepositoryCommand(projectName, repositoryName, initialRevision,
minRetentionCommits, minRetentionDays);
}

/**
* Returns a new {@link Command} which is used to push the changes. The changes are normalized via
* {@link Repository#previewDiff(Revision, Iterable)} before they are applied.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum CommandType {
CREATE_REPOSITORY(Void.class),
REMOVE_REPOSITORY(Void.class),
UNREMOVE_REPOSITORY(Void.class),
CREATE_ROLLING_REPOSITORY(Void.class),
NORMALIZING_PUSH(CommitResult.class),
PUSH(Revision.class),
SAVE_NAMED_QUERY(Void.class),
Expand Down
Loading