Allow to listen to garbage collector events

A new event type is introduced that allows to listen to garbage
collector events. This enables to trigger actions that depend on
changes in the file size footprint of a project such as quota
enforcement or usage data monitoring.

Change-Id: I1eb913a618df5fe972f3f516af504c13871e5fa5
This commit is contained in:
Adrian Görler
2014-08-22 17:09:18 +02:00
parent 3772eb551d
commit f4a4c9a628
4 changed files with 78 additions and 1 deletions

View File

@@ -404,6 +404,10 @@ Update of HEAD on a project
+ +
Publication of usage data Publication of usage data
* `com.google.gerrit.extensions.events.GarbageCollectorListener`:
+
Garbage collection ran on a project
[[stream-events]] [[stream-events]]
== Sending Events to the Events Stream == Sending Events to the Events Stream

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2014 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.google.gerrit.extensions.events;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import java.util.Properties;
/**
* Notified whenever the garbage collector has run successfully on a project.
*/
@ExtensionPoint
public interface GarbageCollectorListener {
public interface Event {
/**
* The name of the Gerrit project that has been garbage collected
* @return
*/
String getProjectName();
/**
* Properties describing the result of the garbage collection performed by
* JGit
*
* @see org.eclipse.jgit.api.GarbageCollectCommand#call()
*/
Properties getStatistics();
}
void onGarbageCollected(Event event);
}

View File

@@ -27,6 +27,7 @@ import com.google.gerrit.extensions.events.HeadUpdatedListener;
import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.events.ProjectDeletedListener; import com.google.gerrit.extensions.events.ProjectDeletedListener;
import com.google.gerrit.extensions.events.GarbageCollectorListener;
import com.google.gerrit.extensions.events.UsageDataPublishedListener; import com.google.gerrit.extensions.events.UsageDataPublishedListener;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
@@ -253,6 +254,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), PreUploadHook.class); DynamicSet.setOf(binder(), PreUploadHook.class);
DynamicSet.setOf(binder(), NewProjectCreatedListener.class); DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
DynamicSet.setOf(binder(), ProjectDeletedListener.class); DynamicSet.setOf(binder(), ProjectDeletedListener.class);
DynamicSet.setOf(binder(), GarbageCollectorListener.class);
DynamicSet.setOf(binder(), HeadUpdatedListener.class); DynamicSet.setOf(binder(), HeadUpdatedListener.class);
DynamicSet.setOf(binder(), UsageDataPublishedListener.class); DynamicSet.setOf(binder(), UsageDataPublishedListener.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterUpdate.class); DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ReindexAfterUpdate.class);

View File

@@ -16,6 +16,9 @@ package com.google.gerrit.server.git;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GarbageCollectionResult; import com.google.gerrit.common.data.GarbageCollectionResult;
import com.google.gerrit.extensions.events.GarbageCollectorListener;
import com.google.gerrit.extensions.events.GarbageCollectorListener.Event;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GcConfig; import com.google.gerrit.server.config.GcConfig;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -48,6 +51,7 @@ public class GarbageCollection {
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final GarbageCollectionQueue gcQueue; private final GarbageCollectionQueue gcQueue;
private final GcConfig gcConfig; private final GcConfig gcConfig;
private final DynamicSet<GarbageCollectorListener> listeners;
public interface Factory { public interface Factory {
GarbageCollection create(); GarbageCollection create();
@@ -55,10 +59,12 @@ public class GarbageCollection {
@Inject @Inject
GarbageCollection(GitRepositoryManager repoManager, GarbageCollection(GitRepositoryManager repoManager,
GarbageCollectionQueue gcQueue, GcConfig config) { GarbageCollectionQueue gcQueue, GcConfig config,
DynamicSet<GarbageCollectorListener> listeners) {
this.repoManager = repoManager; this.repoManager = repoManager;
this.gcQueue = gcQueue; this.gcQueue = gcQueue;
this.gcConfig = config; this.gcConfig = config;
this.listeners = listeners;
} }
public GarbageCollectionResult run(List<Project.NameKey> projectNames) { public GarbageCollectionResult run(List<Project.NameKey> projectNames) {
@@ -93,6 +99,7 @@ public class GarbageCollection {
Properties statistics = gc.call(); Properties statistics = gc.call();
logGcInfo(p, "after: ", statistics); logGcInfo(p, "after: ", statistics);
print(writer, "done.\n\n"); print(writer, "done.\n\n");
fire(p, statistics);
} catch (RepositoryNotFoundException e) { } catch (RepositoryNotFoundException e) {
logGcError(writer, p, e); logGcError(writer, p, e);
result.addError(new GarbageCollectionResult.Error( result.addError(new GarbageCollectionResult.Error(
@@ -112,6 +119,27 @@ public class GarbageCollection {
return result; return result;
} }
private void fire(final Project.NameKey p, final Properties statistics) {
Event event = new GarbageCollectorListener.Event() {
@Override
public String getProjectName() {
return p.get();
}
@Override
public Properties getStatistics() {
return statistics;
}
};
for (GarbageCollectorListener l : listeners) {
try {
l.onGarbageCollected(event);
} catch (RuntimeException e) {
log.warn("Failure in GarbageCollectorListener", e);
}
}
}
private static void logGcInfo(Project.NameKey projectName, String msg) { private static void logGcInfo(Project.NameKey projectName, String msg) {
logGcInfo(projectName, msg, null); logGcInfo(projectName, msg, null);
} }