Merge 'Implement sstable_info command' from Calle
"Fixes #76 Implements JMX level call for "sstable_info" REST api command. Requires seastar patch: json: Make date formatter use RFC8601/RFC3339 format Requires scylla patch set "Implement sstable_info API command (info on sstables)" Forwards call to REST sstable_info and packs the data into CompositeData for JMX consumption." * 'sstabledesc' of git://github.com/elcallio/scylla-jmx: storage_service: Add "getSSTableInfo" command/attribute service: Add objects for deserializing sstable_info json scylla-apiclient: Add Date json serializer helper APIClient: Add jackson JSON serializer support to client object apiclient/pom.xml: Add jackson JSON support libs for REST client
This commit is contained in:
commit
04ea3ab7e0
@ -60,6 +60,21 @@
|
|||||||
<artifactId>activation</artifactId>
|
<artifactId>activation</artifactId>
|
||||||
<version>1.1</version>
|
<version>1.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
<version>2.9.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.9.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||||
|
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||||
|
<version>2.9.9</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -38,6 +38,7 @@ import javax.ws.rs.core.Response;
|
|||||||
|
|
||||||
import org.glassfish.jersey.client.ClientConfig;
|
import org.glassfish.jersey.client.ClientConfig;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
|
||||||
import com.scylladb.jmx.utils.SnapshotDetailsTabularData;
|
import com.scylladb.jmx.utils.SnapshotDetailsTabularData;
|
||||||
|
|
||||||
public class APIClient {
|
public class APIClient {
|
||||||
@ -78,9 +79,12 @@ public class APIClient {
|
|||||||
private static final Logger logger = Logger.getLogger(APIClient.class.getName());
|
private static final Logger logger = Logger.getLogger(APIClient.class.getName());
|
||||||
|
|
||||||
private final APIConfig config;
|
private final APIConfig config;
|
||||||
|
private final ClientConfig clientConfig;
|
||||||
|
|
||||||
public APIClient(APIConfig config) {
|
public APIClient(APIConfig config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.clientConfig = new ClientConfig();
|
||||||
|
clientConfig.register(new JacksonJaxbJsonProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getBaseUrl() {
|
private String getBaseUrl() {
|
||||||
@ -88,7 +92,7 @@ public class APIClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Invocation.Builder get(String path, MultivaluedMap<String, String> queryParams) {
|
public Invocation.Builder get(String path, MultivaluedMap<String, String> queryParams) {
|
||||||
Client client = ClientBuilder.newClient(new ClientConfig());
|
Client client = ClientBuilder.newClient(clientConfig);
|
||||||
WebTarget webTarget = client.target(getBaseUrl()).path(path);
|
WebTarget webTarget = client.target(getBaseUrl()).path(path);
|
||||||
if (queryParams != null) {
|
if (queryParams != null) {
|
||||||
for (Entry<String, List<String>> qp : queryParams.entrySet()) {
|
for (Entry<String, List<String>> qp : queryParams.entrySet()) {
|
||||||
|
19
src/main/java/com/scylladb/jmx/utils/DateXmlAdapter.java
Normal file
19
src/main/java/com/scylladb/jmx/utils/DateXmlAdapter.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.scylladb.jmx.utils;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||||
|
|
||||||
|
public class DateXmlAdapter extends XmlAdapter<String, Date> {
|
||||||
|
@Override
|
||||||
|
public String marshal(Date v) throws Exception {
|
||||||
|
return Instant.ofEpochMilli(v.getTime()).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date unmarshal(String v) throws Exception {
|
||||||
|
return new Date(Instant.parse(v).toEpochMilli());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package org.apache.cassandra.service;
|
||||||
|
|
||||||
|
import static com.sun.jmx.mbeanserver.MXBeanMappingFactory.DEFAULT;
|
||||||
|
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.management.openmbean.CompositeData;
|
||||||
|
import javax.management.openmbean.OpenDataException;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
import com.sun.jmx.mbeanserver.MXBeanMapping;
|
||||||
|
|
||||||
|
@SuppressWarnings("restriction")
|
||||||
|
@XmlRootElement
|
||||||
|
public class PerTableSSTableInfo {
|
||||||
|
private static final MXBeanMapping mxBeanMapping;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
mxBeanMapping = DEFAULT.mappingForType(PerTableSSTableInfo.class, DEFAULT);
|
||||||
|
} catch (OpenDataException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String keyspace;
|
||||||
|
private List<SSTableInfo> sstables;
|
||||||
|
private String table;
|
||||||
|
|
||||||
|
public String getTable() {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTable(String table) {
|
||||||
|
this.table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyspace() {
|
||||||
|
return keyspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyspace(String keyspace) {
|
||||||
|
this.keyspace = keyspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SSTableInfo> getSSTables() {
|
||||||
|
return sstables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSSTableInfos(List<SSTableInfo> sstableInfos) {
|
||||||
|
this.sstables = sstableInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompositeData toCompositeData() {
|
||||||
|
try {
|
||||||
|
return (CompositeData) mxBeanMapping.toOpenValue(this);
|
||||||
|
} catch (OpenDataException e) {
|
||||||
|
throw new Error(e); // should not reach.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PerTableSSTableInfo from(CompositeData data) throws InvalidObjectException {
|
||||||
|
return (PerTableSSTableInfo) mxBeanMapping.fromOpenValue(data);
|
||||||
|
}
|
||||||
|
}
|
143
src/main/java/org/apache/cassandra/service/SSTableInfo.java
Normal file
143
src/main/java/org/apache/cassandra/service/SSTableInfo.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package org.apache.cassandra.service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.scylladb.jmx.utils.DateXmlAdapter;
|
||||||
|
|
||||||
|
public class SSTableInfo {
|
||||||
|
private long size;
|
||||||
|
|
||||||
|
@JsonProperty("data_size")
|
||||||
|
private long dataSize;
|
||||||
|
|
||||||
|
@JsonProperty("index_size")
|
||||||
|
private long indexSize;
|
||||||
|
|
||||||
|
@JsonProperty("filter_size")
|
||||||
|
private long filterSize;
|
||||||
|
|
||||||
|
@XmlJavaTypeAdapter(type = Date.class, value = DateXmlAdapter.class)
|
||||||
|
private Date timestamp;
|
||||||
|
|
||||||
|
private long generation;
|
||||||
|
|
||||||
|
private long level;
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
private Map<String, String> properties;
|
||||||
|
|
||||||
|
public void setProperties(Map<String, String> properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private Map<String, Map<String, String>> extendedProperties;
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(long size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDataSize() {
|
||||||
|
return dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataSize(long dataSize) {
|
||||||
|
this.dataSize = dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getIndexSize() {
|
||||||
|
return indexSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndexSize(long indexSize) {
|
||||||
|
this.indexSize = indexSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFilterSize() {
|
||||||
|
return filterSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilterSize(long filterSize) {
|
||||||
|
this.filterSize = filterSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(Date timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getGeneration() {
|
||||||
|
return generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeneration(long generation) {
|
||||||
|
this.generation = generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(long level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Map<String, String>> getExtendedProperties() {
|
||||||
|
return extendedProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtendedProperties(Map<String, Map<String, String>> extendedProperties) {
|
||||||
|
this.extendedProperties = extendedProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("extended_properties")
|
||||||
|
private void unpackNested(List<Map<String, Object>> properties) {
|
||||||
|
Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
|
||||||
|
|
||||||
|
for (Map<String, Object> map : properties) {
|
||||||
|
Object name = map.get("group");
|
||||||
|
if (name != null) {
|
||||||
|
Map<String, String> dst = new HashMap<>();
|
||||||
|
List<?> value = (List<?>) map.get("attributes");
|
||||||
|
for (Object v : value) {
|
||||||
|
Map<?, ?> subMap = (Map<?, ?>) v;
|
||||||
|
dst.put(String.valueOf(subMap.get("key")), String.valueOf(subMap.get("value")));
|
||||||
|
}
|
||||||
|
result.put(String.valueOf(name), dst);
|
||||||
|
} else {
|
||||||
|
for (Map.Entry<String, Object> e : map.entrySet()) {
|
||||||
|
result.put(e.getKey(), Collections.singletonMap(String.valueOf(e.getValue()), ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extendedProperties = result;
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,7 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
@ -52,14 +53,14 @@ import javax.management.NotificationBroadcaster;
|
|||||||
import javax.management.NotificationBroadcasterSupport;
|
import javax.management.NotificationBroadcasterSupport;
|
||||||
import javax.management.NotificationFilter;
|
import javax.management.NotificationFilter;
|
||||||
import javax.management.NotificationListener;
|
import javax.management.NotificationListener;
|
||||||
|
import javax.management.openmbean.CompositeData;
|
||||||
import javax.management.openmbean.TabularData;
|
import javax.management.openmbean.TabularData;
|
||||||
|
import javax.ws.rs.core.GenericType;
|
||||||
import javax.ws.rs.core.MultivaluedHashMap;
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
|
||||||
import org.apache.cassandra.metrics.StorageMetrics;
|
import org.apache.cassandra.metrics.StorageMetrics;
|
||||||
import org.apache.cassandra.repair.RepairParallelism;
|
import org.apache.cassandra.repair.RepairParallelism;
|
||||||
import org.glassfish.jersey.client.ClientConfig;
|
|
||||||
import org.glassfish.jersey.client.ClientProperties;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.scylladb.jmx.api.APIClient;
|
import com.scylladb.jmx.api.APIClient;
|
||||||
@ -492,12 +493,12 @@ public class StorageService extends MetricsMBean implements StorageServiceMBean,
|
|||||||
@Override
|
@Override
|
||||||
public void takeSnapshot(String tag, Map<String, String> options, String... keyspaceNames) throws IOException {
|
public void takeSnapshot(String tag, Map<String, String> options, String... keyspaceNames) throws IOException {
|
||||||
log(" takeSnapshot(String tag, String... keyspaceNames) throws IOException");
|
log(" takeSnapshot(String tag, String... keyspaceNames) throws IOException");
|
||||||
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
|
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<String, String>();
|
||||||
APIClient.set_query_param(queryParams, "tag", tag);
|
APIClient.set_query_param(queryParams, "tag", tag);
|
||||||
|
|
||||||
if (keyspaceNames.length == 1 && keyspaceNames[0].indexOf('.') != -1) {
|
if (keyspaceNames.length == 1 && keyspaceNames[0].indexOf('.') != -1) {
|
||||||
String[] parts = keyspaceNames[0].split("\\.");
|
String[] parts = keyspaceNames[0].split("\\.");
|
||||||
keyspaceNames = new String[] { parts[0] };
|
keyspaceNames = new String[] { parts[0] };
|
||||||
APIClient.set_query_param(queryParams, "cf", parts[1]);
|
APIClient.set_query_param(queryParams, "cf", parts[1]);
|
||||||
}
|
}
|
||||||
APIClient.set_query_param(queryParams, "kn", APIClient.join(keyspaceNames));
|
APIClient.set_query_param(queryParams, "kn", APIClient.join(keyspaceNames));
|
||||||
@ -603,8 +604,9 @@ public class StorageService extends MetricsMBean implements StorageServiceMBean,
|
|||||||
client.post("/storage_service/keyspace_compaction/" + keyspaceName, queryParams);
|
client.post("/storage_service/keyspace_compaction/" + keyspaceName, queryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forceKeyspaceCompactionForTokenRange(String keyspaceName, String startToken, String endToken, String... tableNames) throws IOException, ExecutionException, InterruptedException {
|
public void forceKeyspaceCompactionForTokenRange(String keyspaceName, String startToken, String endToken,
|
||||||
|
String... tableNames) throws IOException, ExecutionException, InterruptedException {
|
||||||
// TODO: actually handle token ranges.
|
// TODO: actually handle token ranges.
|
||||||
forceKeyspaceCompaction(keyspaceName, tableNames);
|
forceKeyspaceCompaction(keyspaceName, tableNames);
|
||||||
}
|
}
|
||||||
@ -869,7 +871,7 @@ public class StorageService extends MetricsMBean implements StorageServiceMBean,
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public int forceRepairAsync(String keyspace, boolean isSequential, Collection<String> dataCenters,
|
public int forceRepairAsync(String keyspace, boolean isSequential, Collection<String> dataCenters,
|
||||||
Collection<String> hosts, boolean primaryRange, boolean repairedAt, String... columnFamilies)
|
Collection<String> hosts, boolean primaryRange, boolean repairedAt, String... columnFamilies)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
log(" forceRepairAsync(String keyspace, boolean isSequential, Collection<String> dataCenters, Collection<String> hosts, boolean primaryRange, boolean repairedAt, String... columnFamilies) throws IOException");
|
log(" forceRepairAsync(String keyspace, boolean isSequential, Collection<String> dataCenters, Collection<String> hosts, boolean primaryRange, boolean repairedAt, String... columnFamilies) throws IOException");
|
||||||
return repairRangeAsync(null, null, keyspace, isSequential, dataCenters, hosts, primaryRange, repairedAt,
|
return repairRangeAsync(null, null, keyspace, isSequential, dataCenters, hosts, primaryRange, repairedAt,
|
||||||
columnFamilies);
|
columnFamilies);
|
||||||
@ -1298,12 +1300,19 @@ public class StorageService extends MetricsMBean implements StorageServiceMBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #rebuild(String)}, but only for specified keyspace and ranges.
|
* Same as {@link #rebuild(String)}, but only for specified keyspace and
|
||||||
|
* ranges.
|
||||||
*
|
*
|
||||||
* @param sourceDc Name of DC from which to select sources for streaming or null to pick any node
|
* @param sourceDc
|
||||||
* @param keyspace Name of the keyspace which to rebuild or null to rebuild all keyspaces.
|
* Name of DC from which to select sources for streaming or null
|
||||||
* @param tokens Range of tokens to rebuild or null to rebuild all token ranges. In the format of:
|
* to pick any node
|
||||||
* "(start_token_1,end_token_1],(start_token_2,end_token_2],...(start_token_n,end_token_n]"
|
* @param keyspace
|
||||||
|
* Name of the keyspace which to rebuild or null to rebuild all
|
||||||
|
* keyspaces.
|
||||||
|
* @param tokens
|
||||||
|
* Range of tokens to rebuild or null to rebuild all token
|
||||||
|
* ranges. In the format of:
|
||||||
|
* "(start_token_1,end_token_1],(start_token_2,end_token_2],...(start_token_n,end_token_n]"
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void rebuild(String sourceDc, String keyspace, String tokens, String specificSources) {
|
public void rebuild(String sourceDc, String keyspace, String tokens, String specificSources) {
|
||||||
@ -1704,4 +1713,29 @@ public class StorageService extends MetricsMBean implements StorageServiceMBean,
|
|||||||
log(" resumeBootstrap");
|
log(" resumeBootstrap");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CompositeData> getSSTableInfo(String keyspace, String table) {
|
||||||
|
if (keyspace == null && table != null) {
|
||||||
|
throw new IllegalArgumentException("Missing keyspace name");
|
||||||
|
}
|
||||||
|
MultivaluedMap<String, String> queryParams = null;
|
||||||
|
|
||||||
|
if (keyspace != null) {
|
||||||
|
queryParams = new MultivaluedHashMap<String, String>();
|
||||||
|
queryParams.add("keyspace", keyspace);
|
||||||
|
}
|
||||||
|
if (table != null) {
|
||||||
|
queryParams.add("cf", table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.get("/storage_service/sstable_info", queryParams)
|
||||||
|
.get(new GenericType<List<PerTableSSTableInfo>>() {
|
||||||
|
}).stream().map((i) -> i.toCompositeData()).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CompositeData> getSSTableInfo() {
|
||||||
|
return getSSTableInfo(null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javax.management.NotificationEmitter;
|
import javax.management.NotificationEmitter;
|
||||||
|
import javax.management.openmbean.CompositeData;
|
||||||
import javax.management.openmbean.TabularData;
|
import javax.management.openmbean.TabularData;
|
||||||
|
|
||||||
public interface StorageServiceMBean extends NotificationEmitter {
|
public interface StorageServiceMBean extends NotificationEmitter {
|
||||||
@ -872,4 +873,8 @@ public interface StorageServiceMBean extends NotificationEmitter {
|
|||||||
* Sets the hinted handoff throttle in kb per second, per delivery thread.
|
* Sets the hinted handoff throttle in kb per second, per delivery thread.
|
||||||
*/
|
*/
|
||||||
public boolean resumeBootstrap();
|
public boolean resumeBootstrap();
|
||||||
|
|
||||||
|
public List<CompositeData> getSSTableInfo(String keyspace, String table);
|
||||||
|
|
||||||
|
public List<CompositeData> getSSTableInfo();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user