From a568e52cb570b1260790ce6fe07f2bbc16b958be Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Thu, 8 Oct 2015 15:01:46 +0300 Subject: [PATCH 1/7] Adding the RecentEstimatedHistogram Some of the jmx methods uses the notion of recent estimated histogram. In origin the implementation uses an estimated histogram and clean the histogram values on each call. The RecentEstimatedHistogram mimic this behaviour, it store the latest values of the last call. In each call new values are stored in the histogram and the results is the delta between the last two calls. Signed-off-by: Amnon Heiman --- .../utils/RecentEstimatedHistogram.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/com/cloudius/urchin/utils/RecentEstimatedHistogram.java diff --git a/src/main/java/com/cloudius/urchin/utils/RecentEstimatedHistogram.java b/src/main/java/com/cloudius/urchin/utils/RecentEstimatedHistogram.java new file mode 100644 index 0000000..692f41e --- /dev/null +++ b/src/main/java/com/cloudius/urchin/utils/RecentEstimatedHistogram.java @@ -0,0 +1,62 @@ +package com.cloudius.urchin.utils; +/* + * Copyright (C) 2015 ScyllaDB + */ + +/* + * This file is part of Scylla. + * + * Scylla is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Scylla is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Scylla. If not, see . + */ + +/** + * + * RecentEstimatedHistogram In the (deprecated) 'recent' functionality, each + * call to get the values cleans the value. + * + * The RecentEstimatedHistogram support recent call to EstimatedHistogram. + * It holds the latest total values and a call to getBuckets return the delta. + * + */ +public class RecentEstimatedHistogram extends EstimatedHistogram { + public RecentEstimatedHistogram() { + } + + public RecentEstimatedHistogram(int bucketCount) { + super(bucketCount); + } + + public RecentEstimatedHistogram(long[] offsets, long[] bucketData) { + super(offsets, bucketData); + } + + /** + * Set the current buckets to new value and return the delta from the last + * getBuckets call + * + * @param bucketData + * - new bucket value + * @return a long[] containing the current histogram difference buckets + */ + public long[] getBuckets(long[] bucketData) { + final int len = buckets.length(); + long[] rv = new long[len]; + + for (int i = 0; i < len; i++) { + rv[i] = bucketData[i]; + rv[i] -= buckets.getAndSet(i, bucketData[i]); + } + return rv; + } +} From 931571c3edeff8ff0f8d7c91d4b3f1bde0539f34 Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Wed, 21 Oct 2015 11:34:15 +0300 Subject: [PATCH 2/7] EstimatedHistogram: Add constructor from data This patch allows to create an EstimatedHistogram from an array of data value. It will be used by the APIClient to return EstimatedHistogram Signed-off-by: Amnon Heiman --- .../java/com/cloudius/urchin/utils/EstimatedHistogram.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/cloudius/urchin/utils/EstimatedHistogram.java b/src/main/java/com/cloudius/urchin/utils/EstimatedHistogram.java index 9042a7b..29bf21b 100644 --- a/src/main/java/com/cloudius/urchin/utils/EstimatedHistogram.java +++ b/src/main/java/com/cloudius/urchin/utils/EstimatedHistogram.java @@ -67,6 +67,12 @@ public class EstimatedHistogram { buckets = new AtomicLongArray(bucketData); } + + public EstimatedHistogram(long[] bucketData) { + bucketOffsets = newOffsets(bucketData.length - 1); + buckets = new AtomicLongArray(bucketData); + } + private static long[] newOffsets(int size) { long[] result = new long[size]; long last = 1; From a7c30493cd6445661d880f5f0b90dd46bbdd4b4f Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Wed, 21 Oct 2015 11:33:00 +0300 Subject: [PATCH 3/7] APIClient to support EstimatedHistogram This patch modify the CacheEntry to support both String and EstimatedHistogram. It is possible to add more supported types in the future when needed. In the APIClient, the cache will now support both String and EstimatedHistogram in a similiar way. Signed-off-by: Amnon Heiman apiclient need to merge to cache --- .../com/cloudius/urchin/api/APIClient.java | 31 +++++++++++++++-- .../com/cloudius/urchin/api/CacheEntry.java | 34 ++++++++++++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/cloudius/urchin/api/APIClient.java b/src/main/java/com/cloudius/urchin/api/APIClient.java index e2d6ecd..012c76a 100644 --- a/src/main/java/com/cloudius/urchin/api/APIClient.java +++ b/src/main/java/com/cloudius/urchin/api/APIClient.java @@ -26,6 +26,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriBuilder; +import com.cloudius.urchin.utils.EstimatedHistogram; import com.cloudius.urchin.utils.SnapshotDetailsTabularData; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; @@ -50,13 +51,23 @@ public class APIClient { } return key; } - String getFromCache(String key, long duration) { + + String getStringFromCache(String key, long duration) { if (key == null) { return null; } CacheEntry value = cache.get(key); - return (value!= null && value.valid(duration))? value.value : null; + return (value!= null && value.valid(duration))? value.stringValue() : null; } + + EstimatedHistogram getEstimatedHistogramFromCache(String key, long duration) { + if (key == null) { + return null; + } + CacheEntry value = cache.get(key); + return (value!= null && value.valid(duration))? value.getEstimatedHistogram() : null; + } + JsonReaderFactory factory = Json.createReaderFactory(null); private static final java.util.logging.Logger logger = java.util.logging.Logger .getLogger(APIClient.class.getName()); @@ -131,7 +142,7 @@ public class APIClient { return ""; } String key = getCacheKey(string, queryParams, duration); - String res = getFromCache(key, duration); + String res = getStringFromCache(key, duration); if (res != null) { return res; } @@ -611,6 +622,20 @@ public class APIClient { return getHistogramValue(url, null); } + public EstimatedHistogram getEstimatedHistogram(String string, + MultivaluedMap queryParams, long duration) { + String key = getCacheKey(string, queryParams, duration); + EstimatedHistogram res = getEstimatedHistogramFromCache(key, duration); + if (res != null) { + return res; + } + res = new EstimatedHistogram(getEstimatedHistogramAsLongArrValue(string, queryParams)); + if (duration > 0) { + cache.put(key, new CacheEntry(res)); + } + return res; + + } public long[] getEstimatedHistogramAsLongArrValue(String string, MultivaluedMap queryParams) { JsonObject obj = getJsonObj(string, queryParams); diff --git a/src/main/java/com/cloudius/urchin/api/CacheEntry.java b/src/main/java/com/cloudius/urchin/api/CacheEntry.java index e89b834..89d5285 100644 --- a/src/main/java/com/cloudius/urchin/api/CacheEntry.java +++ b/src/main/java/com/cloudius/urchin/api/CacheEntry.java @@ -1,20 +1,46 @@ /* - * Copyright 2015 Cloudius Systems + * Copyright (C) 2015 ScyllaDB + */ + +/* + * This file is part of Scylla. + * + * Scylla is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Scylla is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Scylla. If not, see . */ package com.cloudius.urchin.api; +import com.cloudius.urchin.utils.EstimatedHistogram; + public class CacheEntry { long time; - public String value; + Object value; - CacheEntry(String value) { + CacheEntry(Object res) { time = System.currentTimeMillis(); - this.value = value; + this.value = res; } public boolean valid(long duration) { return (System.currentTimeMillis() - time) < duration; } + public String stringValue() { + return (String) value; + } + + public EstimatedHistogram getEstimatedHistogram() { + return (EstimatedHistogram)value; + } } From 6df716e5f7ede2b91b73f734dda48f709a8bcda3 Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Wed, 21 Oct 2015 12:29:58 +0300 Subject: [PATCH 4/7] Add an EstimateHistogramWrapper The EstimatedHistogramWrapper is a helper class that holds the API related data, so that a class that uses an EstimatedHistogram can replace it with the wrapper and keep most of its code as is. Signed-off-by: Amnon Heiman --- .../metrics/EstimatedHistogramWrapper.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/org/apache/cassandra/metrics/EstimatedHistogramWrapper.java diff --git a/src/main/java/org/apache/cassandra/metrics/EstimatedHistogramWrapper.java b/src/main/java/org/apache/cassandra/metrics/EstimatedHistogramWrapper.java new file mode 100644 index 0000000..7e80a57 --- /dev/null +++ b/src/main/java/org/apache/cassandra/metrics/EstimatedHistogramWrapper.java @@ -0,0 +1,55 @@ +package org.apache.cassandra.metrics; +/* + * Copyright (C) 2015 ScyllaDB + */ + +/* + * This file is part of Scylla. + * + * Scylla is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Scylla is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Scylla. If not, see . + */ + +import javax.ws.rs.core.MultivaluedMap; + +import com.cloudius.urchin.api.APIClient; +import com.cloudius.urchin.utils.EstimatedHistogram; + +public class EstimatedHistogramWrapper { + private APIClient c = new APIClient(); + private String url; + private MultivaluedMap queryParams; + private static final int DURATION = 50; + private int duration; + public EstimatedHistogramWrapper(String url, MultivaluedMap queryParams, int duration) { + this.url = url; + this.queryParams = queryParams; + this.duration = duration; + + } + public EstimatedHistogramWrapper(String url) { + this(url, null, DURATION); + + } + public EstimatedHistogramWrapper(String url, int duration) { + this(url, null, duration); + + } + public EstimatedHistogram get() { + return c.getEstimatedHistogram(url, queryParams, duration); + } + + public long[] getBuckets(boolean reset) { + return get().getBuckets(reset); + } +} From 960aa6f5096f0ea5f4e2b642e8f841bf863b5245 Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Thu, 8 Oct 2015 15:08:45 +0300 Subject: [PATCH 5/7] LatencyMetrics: Support totalLatencyHistogram and recentLatencyHistogram This adds the depricated total and recent estimated histogram. It uses the new RecentEstimatedHistogram for the recent value and the API based estimated histogram for the total latency. Signed-off-by: Amnon Heiman --- .../apache/cassandra/metrics/LatencyMetrics.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/org/apache/cassandra/metrics/LatencyMetrics.java b/src/main/java/org/apache/cassandra/metrics/LatencyMetrics.java index 596718e..6cce299 100644 --- a/src/main/java/org/apache/cassandra/metrics/LatencyMetrics.java +++ b/src/main/java/org/apache/cassandra/metrics/LatencyMetrics.java @@ -29,6 +29,8 @@ import java.util.concurrent.TimeUnit; import com.cloudius.urchin.metrics.APIMetrics; import com.cloudius.urchin.metrics.DefaultNameFactory; import com.cloudius.urchin.metrics.MetricNameFactory; +import com.cloudius.urchin.utils.EstimatedHistogram; +import com.cloudius.urchin.utils.RecentEstimatedHistogram; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.yammer.metrics.core.Counter; @@ -49,6 +51,12 @@ public class LatencyMetrics { protected final MetricNameFactory factory; protected final String namePrefix; + @Deprecated public EstimatedHistogramWrapper totalLatencyHistogram; + /* + * It should not be called directly, use the getRecentLatencyHistogram + */ + @Deprecated protected final RecentEstimatedHistogram recentLatencyHistogram = new RecentEstimatedHistogram(); + protected long lastLatency; protected long lastOpCount; @@ -106,6 +114,7 @@ public class LatencyMetrics { TimeUnit.MICROSECONDS, TimeUnit.SECONDS); totalLatency = APIMetrics.newCounter(url + paramName, factory.createMetricName(namePrefix + "TotalLatency")); + totalLatencyHistogram = new EstimatedHistogramWrapper(url + "/estimated_histogram" + paramName); } /** @@ -155,4 +164,8 @@ public class LatencyMetrics { lastOpCount = ops; } } + + public long[] getRecentLatencyHistogram() { + return recentLatencyHistogram.getBuckets(totalLatencyHistogram.getBuckets(false)); + } } From db3b3cdeee72a71a39a4c1329da549c63a294a5f Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Thu, 8 Oct 2015 15:16:09 +0300 Subject: [PATCH 6/7] ColumnFamilyMetrics: add estimted per read histogram support This uses the recent estimated histogram and the API based estimated histogram to support the sstable per read recent and total estimated histogram. Signed-off-by: Amnon Heiman --- .../cassandra/metrics/ColumnFamilyMetrics.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java b/src/main/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java index e2f3596..fce9565 100644 --- a/src/main/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java +++ b/src/main/java/org/apache/cassandra/metrics/ColumnFamilyMetrics.java @@ -34,6 +34,7 @@ import com.cloudius.urchin.api.APIClient; import com.cloudius.urchin.metrics.APIMetrics; import com.cloudius.urchin.metrics.MetricNameFactory; import com.cloudius.urchin.utils.EstimatedHistogram; +import com.cloudius.urchin.utils.RecentEstimatedHistogram; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.yammer.metrics.Metrics; @@ -166,10 +167,10 @@ public class ColumnFamilyMetrics { // for backward compatibility @Deprecated - public final EstimatedHistogram sstablesPerRead = new EstimatedHistogram(35); + public final EstimatedHistogramWrapper sstablesPerRead; + // it should not be called directly @Deprecated - public final EstimatedHistogram recentSSTablesPerRead = new EstimatedHistogram( - 35); + protected final RecentEstimatedHistogram recentSSTablesPerRead = new RecentEstimatedHistogram(35); private String cfName; public final static LatencyMetrics globalReadLatency = new LatencyMetrics( @@ -362,6 +363,7 @@ public class ColumnFamilyMetrics { + cfName, factory, "CasPropose"); casCommit = new LatencyMetrics("/column_family/metrics/cas_commit/" + cfName, factory, "CasCommit"); + sstablesPerRead = new EstimatedHistogramWrapper("/column_family/metrics/sstables_per_read_histogram/" + cfName); } /** @@ -508,7 +510,7 @@ public class ColumnFamilyMetrics { /** * Registers a metric to be removed when unloading CF. - * + * * @return true if first time metric with that name has been registered */ private boolean register(String name, Metric metric) { @@ -519,6 +521,11 @@ public class ColumnFamilyMetrics { return ret; } + public long[] getRecentSSTablesPerRead() { + return recentSSTablesPerRead + .getBuckets(sstablesPerRead.getBuckets(false)); + } + public class ColumnFamilyHistogram { public final Histogram[] all; public final Histogram cf; From 3667682075773d1feda949aac4361e49a8be12bf Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Wed, 21 Oct 2015 12:48:28 +0300 Subject: [PATCH 7/7] ColumnFamilyStore: add support for estimated latency This patch uses the estimated latency that was added to the column family metrics to get the recent and estimated latency. It follows the same logic as origin does to call the logic in metrics. The following method implementation will be added: getMemtableColumnsCount getRecentSSTablesPerReadHistogram getSSTablesPerReadHistogram getLifetimeReadLatencyHistogramMicros getRecentReadLatencyHistogramMicros getRecentReadLatencyMicros getLifetimeWriteLatencyHistogramMicros getRecentWriteLatencyHistogramMicros getRecentWriteLatencyMicros getRangeCount getTotalRangeLatencyMicros getLifetimeRangeLatencyHistogramMicros getRecentRangeLatencyHistogramMicros getRecentRangeLatencyMicros Signed-off-by: Amnon Heiman --- .../cassandra/db/ColumnFamilyStore.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/main/java/org/apache/cassandra/db/ColumnFamilyStore.java index 2ee5cfd..339802e 100644 --- a/src/main/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/main/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -172,7 +172,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long getMemtableColumnsCount() { log(" getMemtableColumnsCount()"); - return c.getLongValue(""); + return metric.memtableColumnsCount.value(); } /** @@ -196,7 +196,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getRecentSSTablesPerReadHistogram() { log(" getRecentSSTablesPerReadHistogram()"); - return c.getLongArrValue(""); + return metric.getRecentSSTablesPerRead(); } /** @@ -206,7 +206,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getSSTablesPerReadHistogram() { log(" getSSTablesPerReadHistogram()"); - return c.getEstimatedHistogramAsLongArrValue("/column_family/metrics/sstables_per_read_histogram/" +getCFName()); + return metric.sstablesPerRead.getBuckets(false); } /** @@ -236,7 +236,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getLifetimeReadLatencyHistogramMicros() { log(" getLifetimeReadLatencyHistogramMicros()"); - return c.getLongArrValue(""); + return metric.readLatency.totalLatencyHistogram.getBuckets(false); } /** @@ -246,7 +246,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getRecentReadLatencyHistogramMicros() { log(" getRecentReadLatencyHistogramMicros()"); - return c.getLongArrValue(""); + return metric.readLatency.getRecentLatencyHistogram(); } /** @@ -256,7 +256,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public double getRecentReadLatencyMicros() { log(" getRecentReadLatencyMicros()"); - return c.getDoubleValue(""); + return metric.readLatency.getRecentLatency(); } /** @@ -286,7 +286,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getLifetimeWriteLatencyHistogramMicros() { log(" getLifetimeWriteLatencyHistogramMicros()"); - return c.getLongArrValue(""); + return metric.writeLatency.totalLatencyHistogram.getBuckets(false); } /** @@ -296,7 +296,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getRecentWriteLatencyHistogramMicros() { log(" getRecentWriteLatencyHistogramMicros()"); - return c.getLongArrValue(""); + return metric.writeLatency.getRecentLatencyHistogram(); } /** @@ -306,7 +306,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public double getRecentWriteLatencyMicros() { log(" getRecentWriteLatencyMicros()"); - return c.getDoubleValue(""); + return metric.writeLatency.getRecentLatency(); } /** @@ -594,7 +594,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getEstimatedRowSizeHistogram() { log(" getEstimatedRowSizeHistogram()"); - return c.getEstimatedHistogramAsLongArrValue("/column_family/metrics/estimated_row_size_histogram/" + getCFName()); + return metric.estimatedRowSizeHistogram.value(); } /** @@ -603,7 +603,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { @Deprecated public long[] getEstimatedColumnCountHistogram() { log(" getEstimatedColumnCountHistogram()"); - return c.getLongArrValue("/column_family/metrics/estimated_column_count_histogram/" + getCFName()); + return metric.estimatedColumnCountHistogram.value(); } /** @@ -696,35 +696,35 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean { public long getRangeCount() { // TODO Auto-generated method stub log("getRangeCount()"); - return c.getLongValue(""); + return metric.rangeLatency.latency.count(); } @Override public long getTotalRangeLatencyMicros() { // TODO Auto-generated method stub log("getTotalRangeLatencyMicros()"); - return c.getLongValue(""); + return metric.rangeLatency.totalLatency.count(); } @Override public long[] getLifetimeRangeLatencyHistogramMicros() { // TODO Auto-generated method stub log("getLifetimeRangeLatencyHistogramMicros()"); - return c.getLongArrValue(""); + return metric.rangeLatency.totalLatencyHistogram.getBuckets(false); } @Override public long[] getRecentRangeLatencyHistogramMicros() { // TODO Auto-generated method stub log("getRecentRangeLatencyHistogramMicros()"); - return c.getLongArrValue(""); + return metric.rangeLatency.getRecentLatencyHistogram(); } @Override public double getRecentRangeLatencyMicros() { // TODO Auto-generated method stub log("getRecentRangeLatencyMicros()"); - return c.getDoubleValue(""); + return metric.rangeLatency.getRecentLatency(); } @Override