Add REST endpoint to list caches

GET /config/server/caches/ lists the server caches. Caches defined by
plugins are included.

The implementation of the SSH show-caches was adapted so that it uses
the new REST endpoint to get the information about the caches. This
avoids code duplication.

For disk caches a new interface was introduced in gerrit-server. This
is done because the list caches REST endpoint needs extra information
for disk caches which can only be provided by the disk cache, aka
H2CacheImpl. H2CacheImpl is defined in gerrit-cache-h2, but
gerrit-server doesn't have a dependency on it and hence can't use
instanceof to check whether a cache is a disk cache. Adding this
dependency is not wanted and also not possible (because
gerrit-cache-h2 already depends on gerrit-server and we can't have
cycles in the dependencies).

Change-Id: I21bf6e114310f98051a0a8db01012511f58d4add
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin 2014-05-23 08:45:56 +02:00
parent 0fa95e7eb9
commit 42c9cf61c8
10 changed files with 720 additions and 124 deletions

View File

@ -30,6 +30,223 @@ Returns the version of the Gerrit server.
"2.7"
----
[[list-caches]]
=== List Caches
--
'GET /config/server/caches/'
--
Lists the caches of the server. Caches defined by plugins are included.
The caller must be a member of a group that is granted the
link:access-control.html#capability_viewCaches[View Caches] capability
or the link:access-control.html#capability_administrateServer[
Administrate Server] capability.
As result a map of link:#cache-info[CacheInfo] entities is returned.
The entries in the map are sorted by cache name.
.Request
----
GET /config/server/caches/ HTTP/1.0
----
.Response
----
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
)]}'
{
"accounts": {
"entries": {
"mem": 4
},
"average_get": "2.5ms",
"hit_ratio": {
"mem": 94
}
},
"accounts_byemail": {
"entries": {
"mem": 4
},
"average_get": "771.8us",
"hit_ratio": {
"mem": 95
}
},
"accounts_byname": {
"entries": {
"mem": 4
},
"hit_ratio": {
"mem": 100
}
},
"adv_bases": {
"entries": {},
"hit_ratio": {}
},
"change_kind": {
"type": "DISK",
"entries": {
"space": "0.00k"
},
"hit_ratio": {}
},
"changes": {
"entries": {},
"hit_ratio": {}
},
"conflicts": {
"type": "DISK",
"entries": {
"mem": 2,
"disk": 3,
"space": "2.75k"
},
"hit_ratio": {
"mem": 0,
"disk": 100
}
},
"diff": {
"type": "DISK",
"entries": {
"mem": 177,
"disk": 253,
"space": "170.97k"
},
"average_get": "1.1ms",
"hit_ratio": {
"mem": 67,
"disk": 100
}
},
"diff_intraline": {
"type": "DISK",
"entries": {
"mem": 1,
"disk": 1,
"space": "0.37k"
},
"average_get": "6.8ms",
"hit_ratio": {
"mem": 0
}
},
"git_tags": {
"type": "DISK",
"entries": {
"space": "0.00k"
},
"hit_ratio": {}
},
groups": {
"entries": {
"mem": 27
},
"average_get": "183.2us",
"hit_ratio": {
"mem": 12
}
},
"groups_byinclude": {
"entries": {},
"hit_ratio": {}
},
"groups_byname": {
"entries": {},
"hit_ratio": {}
},
"groups_byuuid": {
"entries": {
"mem": 25
},
"average_get": "173.4us",
"hit_ratio": {
"mem": 13
}
},
"groups_external": {
"entries": {},
"hit_ratio": {}
},
groups_members": {
"entries": {
"mem": 4
},
"average_get": "697.8us",
"hit_ratio": {
"mem": 82
}
},
"permission_sort": {
"entries": {
"mem": 16
},
"hit_ratio": {
"mem": 96
}
},
"plugin_resources": {
"entries": {
"mem": 2
},
"hit_ratio": {
"mem": 83
}
},
"project_list": {
"entries": {
"mem": 1
},
"average_get": "18.6ms",
"hit_ratio": {
"mem": 0
}
},
"projects": {
"entries": {
"mem": 35
},
"average_get": "8.6ms",
"hit_ratio": {
"mem": 99
}
},
"quota-repo_size": {
"type": "DISK",
"entries": {
"space": "0.00k"
},
"hit_ratio": {}
},
"sshkeys": {
"entries": {
"mem": 1
},
"average_get": "3.2ms",
"hit_ratio": {
"mem": 50
}
},
"web_sessions": {
"type": "DISK",
"entries": {
"mem": 1,
"disk": 2,
"space": "0.78k"
},
"hit_ratio": {
"mem": 82
}
}
}
----
[[list-capabilities]]
=== List Capabilities
--
@ -179,6 +396,31 @@ link:#top-menu-entry-info[TopMenuEntryInfo] entities is returned.
[[json-entities]]
== JSON Entities
[[cache-info]]
=== CacheInfo
The `CacheInfo` entity contains information about a cache.
[options="header",width="50%",cols="1,^1,5"]
|==================================
|Field Name ||Description
|`name` |
not set if returned in a map where the cache name is used as map key|
The cache name. If the cache is defined by a plugin the cache name
includes the plugin name: "<plugin-name>-<cache-name>".
|`type` |not set for in memory caches|
The type of the cache (`MEM`: in memory cache, `DISK`: disk cache).
|`entries` ||
Information about the entries in the cache as a
link:#entries-info[EntriesInfo] entity.
|`average_get` |optional|
The average duration of getting one entry from the cache. The value is
returned with a standard time unit abbreviation (`ns`: nanoseconds,
`us`: microseconds, `ms`: milliseconds, `s`: seconds).
|`hit_ratio` ||
Information about the hit ratio as a link:#hit-ration-info[
HitRatioInfo] entity.
|==================================
[[capability-info]]
=== CapabilityInfo
The `CapabilityInfo` entity contains information about a capability.
@ -191,6 +433,39 @@ The `CapabilityInfo` entity contains information about a capability.
|`name` |capability name
|=================================
[[entries-info]]
=== EntriesInfo
The `EntriesInfo` entity contains information about the entries in a
cache.
[options="header",width="50%",cols="1,^1,5"]
|==================================
|Field Name ||Description
|`mem` |optional|Number of cache entries that are held in memory.
|`disk` |optional|Number of cache entries on the disk. For non-disk
caches this value is not set; for disk caches it is only set if there
are entries in the cache.
|`space` |optional|
The space that is consumed by the cache on disk. The value is returned
with a unit abbreviation (`k`: kilobytes, `m`: megabytes,
`g`: gigabytes). Only set for disk caches.
|==================================
[[hit-ration-info]]
=== HitRatioInfo
The `HitRatioInfo` entity contains information about the hit ratio of a
cache.
[options="header",width="50%",cols="1,^1,5"]
|==================================
|Field Name ||Description
|`mem` ||
Hit ratio for cache entries that are held in memory (0 \<= value \<= 100).
|`disk` |optional|
Hit ratio for cache entries that are held on disk (0 \<= value \<= 100).
Only set for disk caches.
|==================================
[[top-menu-entry-info]]
=== TopMenuEntryInfo
The `TopMenuEntryInfo` entity contains information about a top menu

View File

@ -0,0 +1,6 @@
include_defs('//gerrit-acceptance-tests/tests.defs')
acceptance_tests(
srcs = glob(['*IT.java']),
labels = ['rest']
)

View File

@ -0,0 +1,68 @@
// 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
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.gson.reflect.TypeToken;
import org.apache.http.HttpStatus;
import org.junit.Test;
import java.io.IOException;
import java.util.Map;
public class ListCachesIT extends AbstractDaemonTest {
@Test
public void listCaches() throws IOException {
RestResponse r = adminSession.get("/config/server/caches/");
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
Map<String, CacheInfo> result =
newGson().fromJson(r.getReader(),
new TypeToken<Map<String, CacheInfo>>() {}.getType());
assertTrue(result.containsKey("accounts"));
CacheInfo accountsCacheInfo = result.get("accounts");
assertNull(accountsCacheInfo.type);
assertEquals(1, accountsCacheInfo.entries.mem.longValue());
assertNotNull(accountsCacheInfo.averageGet);
assertTrue(accountsCacheInfo.averageGet.endsWith("s"));
assertNull(accountsCacheInfo.entries.disk);
assertNull(accountsCacheInfo.entries.space);
assertTrue(accountsCacheInfo.hitRatio.mem >= 0);
assertTrue(accountsCacheInfo.hitRatio.mem <= 100);
assertNull(accountsCacheInfo.hitRatio.disk);
userSession.get("/config/server/version").consume();
r = adminSession.get("/config/server/caches/");
assertEquals(HttpStatus.SC_OK, r.getStatusCode());
result = newGson().fromJson(r.getReader(),
new TypeToken<Map<String, CacheInfo>>() {}.getType());
assertEquals(2, result.get("accounts").entries.mem.longValue());
}
@Test
public void listCaches_Forbidden() throws IOException {
RestResponse r = userSession.get("/config/server/caches/");
assertEquals(HttpStatus.SC_FORBIDDEN, r.getStatusCode());
}
}

View File

@ -11,6 +11,7 @@ import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.Funnels;
import com.google.common.hash.PrimitiveSink;
import com.google.gerrit.server.cache.PersistentCache;
import com.google.gerrit.server.util.TimeUtil;
import com.google.inject.TypeLiteral;
@ -63,7 +64,8 @@ import java.util.concurrent.atomic.AtomicLong;
*
* @see H2CacheFactory
*/
public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements
PersistentCache {
private static final Logger log = LoggerFactory.getLogger(H2CacheImpl.class);
private final Executor executor;
@ -156,6 +158,7 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
return mem.stats();
}
@Override
public DiskStats diskStats() {
return store.diskStats();
}
@ -193,29 +196,6 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
}, delay, TimeUnit.MILLISECONDS);
}
public static class DiskStats {
long size;
long space;
long hitCount;
long missCount;
public long size() {
return size;
}
public long space() {
return space;
}
public long hitCount() {
return hitCount;
}
public long requestCount() {
return hitCount + missCount;
}
}
static class ValueHolder<V> {
final V value;
long created;
@ -599,9 +579,8 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
}
DiskStats diskStats() {
DiskStats d = new DiskStats();
d.hitCount = hitCount.get();
d.missCount = missCount.get();
long size = 0;
long space = 0;
SqlHandle c = null;
try {
c = acquire();
@ -613,8 +592,8 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
+ " FROM data");
try {
if (r.next()) {
d.size = r.getLong(1);
d.space = r.getLong(2);
size = r.getLong(1);
space = r.getLong(2);
}
} finally {
r.close();
@ -628,7 +607,7 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
} finally {
release(c);
}
return d;
return new DiskStats(size, space, hitCount.get(), missCount.get());
}
private SqlHandle acquire() throws SQLException {

View File

@ -0,0 +1,50 @@
// 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.cache;
public interface PersistentCache {
DiskStats diskStats();
public static class DiskStats {
private final long size;
private final long space;
private final long hitCount;
private final long missCount;
public DiskStats(long size, long space, long hitCount, long missCount) {
this.size = size;
this.space = space;
this.hitCount = hitCount;
this.missCount = missCount;
}
public long size() {
return size;
}
public long space() {
return space;
}
public long hitCount() {
return hitCount;
}
public long requestCount() {
return hitCount + missCount;
}
}
}

View File

@ -0,0 +1,23 @@
// 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.gerrit.extensions.restapi.RestView;
import com.google.inject.TypeLiteral;
public class CacheResource extends ConfigResource {
public static final TypeLiteral<RestView<CacheResource>> CACHE_KIND =
new TypeLiteral<RestView<CacheResource>>() {};
}

View File

@ -0,0 +1,58 @@
// 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.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@Singleton
public class CachesCollection implements
ChildCollection<ConfigResource, CacheResource> {
private final DynamicMap<RestView<CacheResource>> views;
private final Provider<ListCaches> list;
@Inject
CachesCollection(DynamicMap<RestView<CacheResource>> views,
Provider<ListCaches> list) {
this.views = views;
this.list = list;
}
@Override
public RestView<ConfigResource> list() {
return list.get();
}
@Override
public CacheResource parse(ConfigResource parent, IdString id)
throws ResourceNotFoundException {
throw new ResourceNotFoundException(id);
}
@Override
public DynamicMap<RestView<CacheResource>> views() {
return views;
}
}

View File

@ -0,0 +1,163 @@
// 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.common.cache.CacheStats;
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.RestReadView;
import com.google.gerrit.server.cache.PersistentCache;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Map;
import java.util.TreeMap;
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@Singleton
public class ListCaches implements RestReadView<ConfigResource> {
private final DynamicMap<Cache<?, ?>> cacheMap;
@Inject
public ListCaches(DynamicMap<Cache<?, ?>> cacheMap) {
this.cacheMap = cacheMap;
}
@Override
public Map<String, CacheInfo> apply(ConfigResource rsrc) {
Map<String, CacheInfo> cacheInfos = new TreeMap<>();
for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
cacheInfos.put(cacheNameOf(e.getPluginName(), e.getExportName()),
new CacheInfo(e.getProvider().get()));
}
return cacheInfos;
}
private static String cacheNameOf(String plugin, String name) {
if ("gerrit".equals(plugin)) {
return name;
} else {
return plugin + "-" + name;
}
}
public enum CacheType {
MEM, DISK;
}
public static class CacheInfo {
public String name;
public CacheType type;
public EntriesInfo entries;
public String averageGet;
public HitRatioInfo hitRatio;
public CacheInfo(Cache<?,?> cache) {
CacheStats stat = cache.stats();
entries = new EntriesInfo();
entries.setMem(cache.size());
averageGet = duration(stat.averageLoadPenalty());
hitRatio = new HitRatioInfo();
hitRatio.setMem(stat.hitCount(), stat.requestCount());
if (cache instanceof PersistentCache) {
type = CacheType.DISK;
PersistentCache.DiskStats diskStats =
((PersistentCache) cache).diskStats();
entries.setDisk(diskStats.size());
entries.setSpace(diskStats.space());
hitRatio.setDisk(diskStats.hitCount(), diskStats.requestCount());
}
}
private static String duration(double ns) {
if (ns < 0.5) {
return null;
}
String suffix = "ns";
if (ns >= 1000.0) {
ns /= 1000.0;
suffix = "us";
}
if (ns >= 1000.0) {
ns /= 1000.0;
suffix = "ms";
}
if (ns >= 1000.0) {
ns /= 1000.0;
suffix = "s";
}
return String.format("%4.1f%s", ns, suffix).trim();
}
}
public static class EntriesInfo {
public Long mem;
public Long disk;
public String space;
public void setMem(long mem) {
this.mem = mem != 0 ? mem : null;
}
public void setDisk(long disk) {
this.disk = disk != 0 ? disk : null;
}
public void setSpace(double value) {
space = bytes(value);
}
private static String bytes(double value) {
value /= 1024;
String suffix = "k";
if (value > 1024) {
value /= 1024;
suffix = "m";
}
if (value > 1024) {
value /= 1024;
suffix = "g";
}
return String.format("%1$6.2f%2$s", value, suffix).trim();
}
}
public static class HitRatioInfo {
public Integer mem;
public Integer disk;
public void setMem(long value, long total) {
mem = percent(value, total);
}
public void setDisk(long value, long total) {
disk = percent(value, total);
}
private static Integer percent(long value, long total) {
if (total <= 0) {
return null;
}
return (int) ((100 * value) / total);
}
}
}

View File

@ -14,6 +14,7 @@
package com.google.gerrit.server.config;
import static com.google.gerrit.server.config.CacheResource.CACHE_KIND;
import static com.google.gerrit.server.config.CapabilityResource.CAPABILITY_KIND;
import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND;
import static com.google.gerrit.server.config.TopMenuResource.TOP_MENU_KIND;
@ -24,9 +25,11 @@ import com.google.gerrit.extensions.restapi.RestApiModule;
public class Module extends RestApiModule {
@Override
protected void configure() {
DynamicMap.mapOf(binder(), CACHE_KIND);
DynamicMap.mapOf(binder(), CAPABILITY_KIND);
DynamicMap.mapOf(binder(), CONFIG_KIND);
DynamicMap.mapOf(binder(), TOP_MENU_KIND);
DynamicMap.mapOf(binder(), CAPABILITY_KIND);
child(CONFIG_KIND, "caches").to(CachesCollection.class);
child(CONFIG_KIND, "capabilities").to(CapabilitiesCollection.class);
child(CONFIG_KIND, "top-menus").to(TopMenuCollection.class);
get(CONFIG_KIND, "version").to(GetVersion.class);

View File

@ -16,20 +16,21 @@ package com.google.gerrit.sshd.commands;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheStats;
import com.google.common.collect.Maps;
import com.google.common.base.Strings;
import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.server.cache.h2.H2CacheImpl;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.ListCaches;
import com.google.gerrit.server.config.ListCaches.CacheInfo;
import com.google.gerrit.server.config.ListCaches.CacheType;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.Task;
import com.google.gerrit.server.util.TimeUtil;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.gerrit.sshd.SshDaemon;
import com.google.inject.Inject;
import com.google.inject.Provider;
@ -52,13 +53,12 @@ import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
/** Show the current cache states. */
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@CommandMetaData(name = "show-caches", description = "Display current cache statistics",
runsAt = MASTER_OR_SLAVE)
final class ShowCaches extends CacheCommand {
final class ShowCaches extends SshCommand {
private static volatile long serverStarted;
static class StartupListener implements LifecycleListener {
@ -88,6 +88,9 @@ final class ShowCaches extends CacheCommand {
@SitePath
private File sitePath;
@Inject
private Provider<ListCaches> listCaches;
@Option(name = "--width", aliases = {"-w"}, metaVar = "COLS", usage = "width of output table")
private int columns = 80;
private int nw;
@ -145,23 +148,10 @@ final class ShowCaches extends CacheCommand {
}
stdout.print("+---------------------+---------+---------+\n");
Map<String, H2CacheImpl<?, ?>> disks = Maps.newTreeMap();
printMemoryCaches(disks, sortedCoreCaches());
printMemoryCaches(disks, sortedPluginCaches());
for (Map.Entry<String, H2CacheImpl<?, ?>> entry : disks.entrySet()) {
H2CacheImpl<?, ?> cache = entry.getValue();
CacheStats stat = cache.stats();
H2CacheImpl.DiskStats disk = cache.diskStats();
stdout.print(String.format(
"D %-"+nw+"s|%6s %6s %7s| %7s |%4s %4s|\n",
entry.getKey(),
count(cache.size()),
count(disk.size()),
bytes(disk.space()),
duration(stat.averageLoadPenalty()),
percent(stat.hitCount(), stat.requestCount()),
percent(disk.hitCount(), disk.requestCount())));
}
Collection<CacheInfo> caches = getCaches();
printMemoryCoreCaches(caches);
printMemoryPluginCaches(caches);
printDiskCaches(caches);
stdout.print('\n');
if (gc) {
@ -181,46 +171,62 @@ final class ShowCaches extends CacheCommand {
stdout.flush();
}
private void printMemoryCaches(
Map<String, H2CacheImpl<?, ?>> disks,
Map<String, Cache<?,?>> caches) {
for (Map.Entry<String, Cache<?,?>> entry : caches.entrySet()) {
Cache<?,?> cache = entry.getValue();
if (cache instanceof H2CacheImpl) {
disks.put(entry.getKey(), (H2CacheImpl<?,?>)cache);
continue;
private Collection<CacheInfo> getCaches() {
Map<String, CacheInfo> caches = listCaches.get().apply(new ConfigResource());
for (Map.Entry<String, CacheInfo> entry : caches.entrySet()) {
CacheInfo cache = entry.getValue();
if (cache.type == null) {
cache.type = CacheType.MEM;
}
cache.name = entry.getKey();
}
return caches.values();
}
private void printMemoryCoreCaches(Collection<CacheInfo> caches) {
for (CacheInfo cache : caches) {
if (!cache.name.contains("-") && CacheType.MEM.equals(cache.type)) {
printCache(cache);
}
CacheStats stat = cache.stats();
stdout.print(String.format(
" %-"+nw+"s|%6s %6s %7s| %7s |%4s %4s|\n",
entry.getKey(),
count(cache.size()),
"",
"",
duration(stat.averageLoadPenalty()),
percent(stat.hitCount(), stat.requestCount()),
""));
}
}
private Map<String, Cache<?, ?>> sortedCoreCaches() {
SortedMap<String, Cache<?, ?>> m = Maps.newTreeMap();
for (Map.Entry<String, Provider<Cache<?, ?>>> entry :
cacheMap.byPlugin("gerrit").entrySet()) {
m.put(cacheNameOf("gerrit", entry.getKey()), entry.getValue().get());
}
return m;
}
private Map<String, Cache<?, ?>> sortedPluginCaches() {
SortedMap<String, Cache<?, ?>> m = Maps.newTreeMap();
for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
if (!"gerrit".equals(e.getPluginName())) {
m.put(cacheNameOf(e.getPluginName(), e.getExportName()),
e.getProvider().get());
private void printMemoryPluginCaches(Collection<CacheInfo> caches) {
for (CacheInfo cache : caches) {
if (cache.name.contains("-") && CacheType.MEM.equals(cache.type)) {
printCache(cache);
}
}
return m;
}
private void printDiskCaches(Collection<CacheInfo> caches) {
for (CacheInfo cache : caches) {
if (CacheType.DISK.equals(cache.type)) {
printCache(cache);
}
}
}
private void printCache(CacheInfo cache) {
stdout.print(String.format(
"%1s %-"+nw+"s|%6s %6s %7s| %7s |%4s %4s|\n",
CacheType.DISK.equals(cache.type) ? "D" : "",
cache.name,
nullToEmpty(cache.entries.mem),
nullToEmpty(cache.entries.disk),
Strings.nullToEmpty(cache.entries.space),
Strings.nullToEmpty(cache.averageGet),
formatAsPercent(cache.hitRatio.mem),
formatAsPercent(cache.hitRatio.disk)
));
}
private static String nullToEmpty(Long l) {
return l != null ? String.valueOf(l) : "";
}
private static String formatAsPercent(Integer i) {
return i != null ? String.valueOf(i) + "%" : "";
}
private void memSummary() {
@ -358,39 +364,4 @@ final class ShowCaches extends CacheCommand {
}
return String.format("%1$6.2f%2$s", value, suffix);
}
private String count(long cnt) {
if (cnt == 0) {
return "";
}
return String.format("%6d", cnt);
}
private String duration(double ns) {
if (ns < 0.5) {
return "";
}
String suffix = "ns";
if (ns >= 1000.0) {
ns /= 1000.0;
suffix = "us";
}
if (ns >= 1000.0) {
ns /= 1000.0;
suffix = "ms";
}
if (ns >= 1000.0) {
ns /= 1000.0;
suffix = "s ";
}
return String.format("%4.1f%s", ns, suffix);
}
private String percent(final long value, final long total) {
if (total <= 0) {
return "";
}
final long pcent = (100 * value) / total;
return String.format("%3d%%", (int) pcent);
}
}