Merge "New option to schedule the Git garbage collection as a background task"
This commit is contained in:
commit
dc2da0d4d5
@ -844,6 +844,34 @@ The response is the streamed output of the garbage collection.
|
||||
done.
|
||||
----
|
||||
|
||||
==== Asynchronous Execution
|
||||
|
||||
The option `async` allows to schedule a background task that asynchronously
|
||||
executes a Git garbage collection.
|
||||
|
||||
The `Location` header of the response refers to the link:rest-api-config.html#get-task[background task]
|
||||
which allows to inspect the progress of its execution. In case of asynchronous
|
||||
execution the `show_progress` option is ignored.
|
||||
|
||||
.Request
|
||||
----
|
||||
POST /projects/plugins%2Freplication/gc HTTP/1.0
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
|
||||
{
|
||||
"async": true
|
||||
}
|
||||
----
|
||||
|
||||
The response is empty.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 202 Accepted
|
||||
Content-Disposition: attachment
|
||||
Location: https:<host>/a/config/server/tasks/383a0602
|
||||
----
|
||||
|
||||
[[ban-commit]]
|
||||
=== Ban Commit
|
||||
--
|
||||
@ -2212,6 +2240,8 @@ collection.
|
||||
Whether progress information should be shown.
|
||||
|`aggressive` |`false` if not set|
|
||||
Whether an aggressive garbage collection should be done.
|
||||
|`async` |`false` if not set|
|
||||
Whether the garbage collection should run asynchronously.
|
||||
|=============================
|
||||
|
||||
[[head-input]]
|
||||
|
@ -36,6 +36,11 @@ public abstract class Response<T> {
|
||||
return new Impl<>(201, value);
|
||||
}
|
||||
|
||||
/** HTTP 202 Accepted: accepted as background task. */
|
||||
public static Accepted accepted(String location) {
|
||||
return new Accepted(location);
|
||||
}
|
||||
|
||||
/** HTTP 204 No Content: typically used when the resource is deleted. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Response<T> none() {
|
||||
@ -168,4 +173,33 @@ public abstract class Response<T> {
|
||||
return String.format("[302 Redirect] %s", location);
|
||||
}
|
||||
}
|
||||
|
||||
/** Accepted as task for asynchronous execution. */
|
||||
public static final class Accepted {
|
||||
private final String location;
|
||||
|
||||
private Accepted(String url) {
|
||||
this.location = url;
|
||||
}
|
||||
|
||||
public String location() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return location.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Accepted
|
||||
&& ((Accepted) o).location.equals(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[202 Accepted] %s", location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.math.RoundingMode.CEILING;
|
||||
import static java.nio.charset.StandardCharsets.ISO_8859_1;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_CREATED;
|
||||
@ -347,6 +348,11 @@ public class RestApiServlet extends HttpServlet {
|
||||
CacheHeaders.setNotCacheable(res);
|
||||
res.sendRedirect(((Response.Redirect) result).location());
|
||||
return;
|
||||
} else if (result instanceof Response.Accepted) {
|
||||
CacheHeaders.setNotCacheable(res);
|
||||
res.setStatus(SC_ACCEPTED);
|
||||
res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted)result).location());
|
||||
return;
|
||||
} else {
|
||||
CacheHeaders.setNotCacheable(res);
|
||||
}
|
||||
|
@ -20,13 +20,19 @@ import com.google.gerrit.common.data.GarbageCollectionResult;
|
||||
import com.google.gerrit.common.data.GlobalCapability;
|
||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.gerrit.server.git.GarbageCollection;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.project.GarbageCollect.Input;
|
||||
import com.google.gerrit.server.util.IdGenerator;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -42,22 +48,61 @@ public class GarbageCollect implements RestModifyView<ProjectResource, Input>,
|
||||
public static class Input {
|
||||
public boolean showProgress;
|
||||
public boolean aggressive;
|
||||
public boolean async;
|
||||
}
|
||||
|
||||
private final boolean canGC;
|
||||
private GarbageCollection.Factory garbageCollectionFactory;
|
||||
private final GarbageCollection.Factory garbageCollectionFactory;
|
||||
private final WorkQueue workQueue;
|
||||
private final Provider<String> canonicalUrl;
|
||||
|
||||
@Inject
|
||||
GarbageCollect(
|
||||
GitRepositoryManager repoManager,
|
||||
GarbageCollection.Factory garbageCollectionFactory) {
|
||||
GarbageCollect(GitRepositoryManager repoManager,
|
||||
GarbageCollection.Factory garbageCollectionFactory, WorkQueue workQueue,
|
||||
@CanonicalWebUrl Provider<String> canonicalUrl) {
|
||||
this.workQueue = workQueue;
|
||||
this.canonicalUrl = canonicalUrl;
|
||||
this.canGC = repoManager instanceof LocalDiskRepositoryManager;
|
||||
this.garbageCollectionFactory = garbageCollectionFactory;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public BinaryResult apply(final ProjectResource rsrc, final Input input) {
|
||||
public Object apply(ProjectResource rsrc, Input input) {
|
||||
Project.NameKey project = rsrc.getNameKey();
|
||||
if (input.async) {
|
||||
return applyAsync(project, input);
|
||||
} else {
|
||||
return applySync(project, input);
|
||||
}
|
||||
}
|
||||
|
||||
private Response.Accepted applyAsync(final Project.NameKey project, final Input input) {
|
||||
Runnable job = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runGC(project, input, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Run " + (input.aggressive ? "aggressive " : "")
|
||||
+ "garbage collection on project " + project.get();
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
WorkQueue.Task<Void> task =
|
||||
(WorkQueue.Task<Void>) workQueue.getDefaultQueue().submit(job);
|
||||
|
||||
String location = canonicalUrl.get() + "a/config/server/tasks/"
|
||||
+ IdGenerator.format(task.getTaskId());
|
||||
|
||||
return Response.accepted(location);
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
private BinaryResult applySync(final Project.NameKey project,
|
||||
final Input input) {
|
||||
return new BinaryResult() {
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
@ -69,10 +114,8 @@ public class GarbageCollect implements RestModifyView<ProjectResource, Input>,
|
||||
}
|
||||
};
|
||||
try {
|
||||
GarbageCollectionResult result =
|
||||
garbageCollectionFactory.create().run(
|
||||
Collections.singletonList(rsrc.getNameKey()), input.aggressive,
|
||||
input.showProgress ? writer : null);
|
||||
PrintWriter progressWriter = input.showProgress ? writer : null;
|
||||
GarbageCollectionResult result = runGC(project, input, progressWriter);
|
||||
String msg = "Garbage collection completed successfully.";
|
||||
if (result.hasErrors()) {
|
||||
for (GarbageCollectionResult.Error e : result.getErrors()) {
|
||||
@ -104,6 +147,13 @@ public class GarbageCollect implements RestModifyView<ProjectResource, Input>,
|
||||
.disableGzip();
|
||||
}
|
||||
|
||||
GarbageCollectionResult runGC(Project.NameKey project,
|
||||
Input input, PrintWriter progressWriter) {
|
||||
return garbageCollectionFactory.create().run(
|
||||
Collections.singletonList(project), input.aggressive,
|
||||
progressWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UiAction.Description getDescription(ProjectResource rsrc) {
|
||||
return new UiAction.Description()
|
||||
|
Loading…
Reference in New Issue
Block a user