Merge "Support flushing of all caches via REST"
This commit is contained in:
@@ -339,6 +339,33 @@ E.g. this could be used to flush all caches:
|
|||||||
done
|
done
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[cache-operations]]
|
||||||
|
=== Cache Operations
|
||||||
|
--
|
||||||
|
'POST /config/server/caches/'
|
||||||
|
--
|
||||||
|
|
||||||
|
Executes a cache operation that is specified in the request body in a
|
||||||
|
link:#cache-operation-input[CacheOperationInput] entity.
|
||||||
|
|
||||||
|
[[flush-all-caches]]
|
||||||
|
==== Flush All Caches
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
POST /config/server/caches/ HTTP/1.0
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
{
|
||||||
|
"operation": "FLUSH_ALL"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
----
|
||||||
|
|
||||||
[[get-cache]]
|
[[get-cache]]
|
||||||
=== Get Cache
|
=== Get Cache
|
||||||
--
|
--
|
||||||
@@ -589,6 +616,20 @@ The `CapabilityInfo` entity contains information about a capability.
|
|||||||
|`name` |capability name
|
|`name` |capability name
|
||||||
|=================================
|
|=================================
|
||||||
|
|
||||||
|
[[cache-operation-input]]
|
||||||
|
=== CacheOperationInput
|
||||||
|
The `CacheOperationInput` entity contains information about an
|
||||||
|
operation that should be executed on caches.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,6"]
|
||||||
|
|=================================
|
||||||
|
|Field Name |Description
|
||||||
|
|`operation` |
|
||||||
|
The cache operation that should be executed:
|
||||||
|
|
||||||
|
`FLUSH_ALL`: Flushes all caches, except the `web_sessions` cache.
|
||||||
|
|=================================
|
||||||
|
|
||||||
[[entries-info]]
|
[[entries-info]]
|
||||||
=== EntriesInfo
|
=== EntriesInfo
|
||||||
The `EntriesInfo` entity contains information about the entries in a
|
The `EntriesInfo` entity contains information about the entries in a
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// 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.acceptance.rest.config;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.config.PostCaches.Operation.FLUSH_ALL;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||||
|
import com.google.gerrit.acceptance.RestResponse;
|
||||||
|
import com.google.gerrit.server.config.ListCaches.CacheInfo;
|
||||||
|
import com.google.gerrit.server.config.PostCaches;
|
||||||
|
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CacheOperationsIT extends AbstractDaemonTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void flushAll() throws IOException {
|
||||||
|
RestResponse r = adminSession.get("/config/server/caches/project_list");
|
||||||
|
CacheInfo cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class);
|
||||||
|
assertTrue(cacheInfo.entries.mem.longValue() > 0);
|
||||||
|
|
||||||
|
r = adminSession.post("/config/server/caches/", new PostCaches.Input(FLUSH_ALL));
|
||||||
|
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
|
||||||
|
r.consume();
|
||||||
|
|
||||||
|
r = adminSession.get("/config/server/caches/project_list");
|
||||||
|
cacheInfo = newGson().fromJson(r.getReader(), CacheInfo.class);
|
||||||
|
assertNull(cacheInfo.entries.mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void flushAll_Forbidden() throws IOException {
|
||||||
|
RestResponse r = userSession.post("/config/server/caches/",
|
||||||
|
new PostCaches.Input(FLUSH_ALL));
|
||||||
|
assertEquals(HttpStatus.SC_FORBIDDEN, r.getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,12 @@ import com.google.common.cache.Cache;
|
|||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.AcceptsPost;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||||
import com.google.gerrit.extensions.restapi.IdString;
|
import com.google.gerrit.extensions.restapi.IdString;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||||
import com.google.gerrit.extensions.restapi.RestView;
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
import com.google.gerrit.server.AnonymousUser;
|
import com.google.gerrit.server.AnonymousUser;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
@@ -32,26 +34,29 @@ import com.google.inject.Singleton;
|
|||||||
@RequiresCapability(GlobalCapability.VIEW_CACHES)
|
@RequiresCapability(GlobalCapability.VIEW_CACHES)
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CachesCollection implements
|
public class CachesCollection implements
|
||||||
ChildCollection<ConfigResource, CacheResource> {
|
ChildCollection<ConfigResource, CacheResource>, AcceptsPost<ConfigResource> {
|
||||||
|
|
||||||
private final DynamicMap<RestView<CacheResource>> views;
|
private final DynamicMap<RestView<CacheResource>> views;
|
||||||
private final ListCaches list;
|
private final Provider<ListCaches> list;
|
||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
private final DynamicMap<Cache<?, ?>> cacheMap;
|
private final DynamicMap<Cache<?, ?>> cacheMap;
|
||||||
|
private final PostCaches postCaches;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CachesCollection(DynamicMap<RestView<CacheResource>> views,
|
CachesCollection(DynamicMap<RestView<CacheResource>> views,
|
||||||
ListCaches list, Provider<CurrentUser> self,
|
Provider<ListCaches> list, Provider<CurrentUser> self,
|
||||||
DynamicMap<Cache<?, ?>> cacheMap) {
|
DynamicMap<Cache<?, ?>> cacheMap,
|
||||||
|
PostCaches postCaches) {
|
||||||
this.views = views;
|
this.views = views;
|
||||||
this.list = list;
|
this.list = list;
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.cacheMap = cacheMap;
|
this.cacheMap = cacheMap;
|
||||||
|
this.postCaches = postCaches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RestView<ConfigResource> list() {
|
public RestView<ConfigResource> list() {
|
||||||
return list;
|
return list.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,4 +90,10 @@ public class CachesCollection implements
|
|||||||
public DynamicMap<RestView<CacheResource>> views() {
|
public DynamicMap<RestView<CacheResource>> views() {
|
||||||
return views;
|
return views;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public PostCaches post(ConfigResource parent) throws RestApiException {
|
||||||
|
return postCaches;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class FlushCache implements RestModifyView<CacheResource, Input> {
|
|||||||
public static class Input {
|
public static class Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String WEB_SESSIONS = "web_sessions";
|
public static final String WEB_SESSIONS = "web_sessions";
|
||||||
|
|
||||||
private final Provider<CurrentUser> self;
|
private final Provider<CurrentUser> self;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// 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.server.config;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
|
import com.google.gerrit.extensions.restapi.Response;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||||
|
import com.google.gerrit.server.config.PostCaches.Input;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
@RequiresCapability(GlobalCapability.FLUSH_CACHES)
|
||||||
|
@Singleton
|
||||||
|
public class PostCaches implements RestModifyView<ConfigResource, Input> {
|
||||||
|
public static class Input {
|
||||||
|
public Operation operation;
|
||||||
|
|
||||||
|
public Input() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Input(Operation op) {
|
||||||
|
operation = op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum Operation {
|
||||||
|
FLUSH_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final DynamicMap<Cache<?, ?>> cacheMap;
|
||||||
|
private final FlushCache flushCache;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public PostCaches(DynamicMap<Cache<?, ?>> cacheMap,
|
||||||
|
FlushCache flushCache) {
|
||||||
|
this.cacheMap = cacheMap;
|
||||||
|
this.flushCache = flushCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(ConfigResource rsrc, Input input) throws AuthException,
|
||||||
|
ResourceNotFoundException, BadRequestException {
|
||||||
|
if (input == null || input.operation == null) {
|
||||||
|
throw new BadRequestException("operation must be specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (input.operation) {
|
||||||
|
case FLUSH_ALL:
|
||||||
|
for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
|
||||||
|
CacheResource cacheResource =
|
||||||
|
new CacheResource(e.getPluginName(), e.getExportName(),
|
||||||
|
e.getProvider());
|
||||||
|
if (FlushCache.WEB_SESSIONS.equals(cacheResource.getName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
flushCache.apply(cacheResource, null);
|
||||||
|
}
|
||||||
|
return Response.ok("");
|
||||||
|
default:
|
||||||
|
throw new BadRequestException("unsupported operation: " + input.operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.sshd.commands;
|
package com.google.gerrit.sshd.commands;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.config.PostCaches.Operation.FLUSH_ALL;
|
||||||
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
|
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
@@ -26,6 +27,7 @@ import com.google.gerrit.server.config.ConfigResource;
|
|||||||
import com.google.gerrit.server.config.FlushCache;
|
import com.google.gerrit.server.config.FlushCache;
|
||||||
import com.google.gerrit.server.config.ListCaches;
|
import com.google.gerrit.server.config.ListCaches;
|
||||||
import com.google.gerrit.server.config.ListCaches.OutputFormat;
|
import com.google.gerrit.server.config.ListCaches.OutputFormat;
|
||||||
|
import com.google.gerrit.server.config.PostCaches;
|
||||||
import com.google.gerrit.sshd.CommandMetaData;
|
import com.google.gerrit.sshd.CommandMetaData;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
@@ -40,8 +42,6 @@ import java.util.List;
|
|||||||
@CommandMetaData(name = "flush-caches", description = "Flush some/all server caches from memory",
|
@CommandMetaData(name = "flush-caches", description = "Flush some/all server caches from memory",
|
||||||
runsAt = MASTER_OR_SLAVE)
|
runsAt = MASTER_OR_SLAVE)
|
||||||
final class FlushCaches extends CacheCommand {
|
final class FlushCaches extends CacheCommand {
|
||||||
private static final String WEB_SESSIONS = "web_sessions";
|
|
||||||
|
|
||||||
@Option(name = "--cache", usage = "flush named cache", metaVar = "NAME")
|
@Option(name = "--cache", usage = "flush named cache", metaVar = "NAME")
|
||||||
private List<String> caches = new ArrayList<>();
|
private List<String> caches = new ArrayList<>();
|
||||||
|
|
||||||
@@ -57,32 +57,44 @@ final class FlushCaches extends CacheCommand {
|
|||||||
@Inject
|
@Inject
|
||||||
private Provider<ListCaches> listCaches;
|
private Provider<ListCaches> listCaches;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private PostCaches postCaches;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Failure {
|
protected void run() throws Failure {
|
||||||
if (list) {
|
try {
|
||||||
if (all || caches.size() > 0) {
|
if (list) {
|
||||||
throw error("error: cannot use --list with --all or --cache");
|
if (all || caches.size() > 0) {
|
||||||
|
throw error("error: cannot use --list with --all or --cache");
|
||||||
|
}
|
||||||
|
doList();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
doList();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (all && caches.size() > 0) {
|
if (all && caches.size() > 0) {
|
||||||
throw error("error: cannot combine --all and --cache");
|
throw error("error: cannot combine --all and --cache");
|
||||||
} else if (!all && caches.size() == 1 && caches.contains("all")) {
|
} else if (!all && caches.size() == 1 && caches.contains("all")) {
|
||||||
caches.clear();
|
caches.clear();
|
||||||
all = true;
|
all = true;
|
||||||
} else if (!all && caches.isEmpty()) {
|
} else if (!all && caches.isEmpty()) {
|
||||||
all = true;
|
all = true;
|
||||||
}
|
|
||||||
|
|
||||||
List<String> names = cacheNames();
|
|
||||||
for (String n : caches) {
|
|
||||||
if (!names.contains(n)) {
|
|
||||||
throw error("error: cache \"" + n + "\" not recognized");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (all) {
|
||||||
|
postCaches.apply(new ConfigResource(),
|
||||||
|
new PostCaches.Input(FLUSH_ALL));
|
||||||
|
} else {
|
||||||
|
List<String> names = cacheNames();
|
||||||
|
for (String n : caches) {
|
||||||
|
if (!names.contains(n)) {
|
||||||
|
throw error("error: cache \"" + n + "\" not recognized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doBulkFlush();
|
||||||
|
}
|
||||||
|
} catch (RestApiException e) {
|
||||||
|
throw die(e.getMessage());
|
||||||
}
|
}
|
||||||
doBulkFlush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UnloggedFailure error(final String msg) {
|
private static UnloggedFailure error(final String msg) {
|
||||||
@@ -95,7 +107,7 @@ final class FlushCaches extends CacheCommand {
|
|||||||
.apply(new ConfigResource());
|
.apply(new ConfigResource());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doList() throws UnloggedFailure {
|
private void doList() throws RestApiException {
|
||||||
for (String name : cacheNames()) {
|
for (String name : cacheNames()) {
|
||||||
stderr.print(name);
|
stderr.print(name);
|
||||||
stderr.print('\n');
|
stderr.print('\n');
|
||||||
@@ -107,7 +119,7 @@ final class FlushCaches extends CacheCommand {
|
|||||||
try {
|
try {
|
||||||
for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
|
for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
|
||||||
String n = cacheNameOf(e.getPluginName(), e.getExportName());
|
String n = cacheNameOf(e.getPluginName(), e.getExportName());
|
||||||
if (flush(n)) {
|
if (caches.contains(n)) {
|
||||||
try {
|
try {
|
||||||
flushCache.apply(
|
flushCache.apply(
|
||||||
new CacheResource(e.getPluginName(), e.getExportName(),
|
new CacheResource(e.getPluginName(), e.getExportName(),
|
||||||
@@ -122,19 +134,4 @@ final class FlushCaches extends CacheCommand {
|
|||||||
stderr.flush();
|
stderr.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean flush(final String cacheName) {
|
|
||||||
if (caches.contains(cacheName)) {
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else if (all) {
|
|
||||||
if (WEB_SESSIONS.equals(cacheName)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user