|
@@ -1,44 +1,51 @@
|
|
/**
|
|
/**
|
|
- * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
- * or more contributor license agreements. See the NOTICE file
|
|
|
|
- * distributed with this work for additional information
|
|
|
|
- * regarding copyright ownership. The ASF licenses this file
|
|
|
|
- * to you 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
|
|
|
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
+ * contributor license agreements. See the NOTICE file distributed with this
|
|
|
|
+ * work for additional information regarding copyright ownership. The ASF
|
|
|
|
+ * licenses this file to you 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
|
|
|
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* 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.
|
|
|
|
|
|
+ * 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 org.apache.hadoop.yarn.server.timelineservice.storage.common;
|
|
package org.apache.hadoop.yarn.server.timelineservice.storage.common;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
|
+import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.Set;
|
|
import java.util.SortedSet;
|
|
import java.util.SortedSet;
|
|
import java.util.Map.Entry;
|
|
import java.util.Map.Entry;
|
|
|
|
|
|
-import org.apache.hadoop.classification.InterfaceAudience;
|
|
|
|
-import org.apache.hadoop.classification.InterfaceStability;
|
|
|
|
|
|
+import org.apache.hadoop.classification.InterfaceAudience.Public;
|
|
|
|
+import org.apache.hadoop.classification.InterfaceStability.Unstable;
|
|
import org.apache.hadoop.hbase.Tag;
|
|
import org.apache.hadoop.hbase.Tag;
|
|
|
|
+import org.apache.hadoop.hbase.util.Bytes;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.ApplicationId;
|
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEntity;
|
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEntity;
|
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEntityType;
|
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEntityType;
|
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEvent;
|
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEvent;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.timelineservice.TimelineMetric;
|
|
import org.apache.hadoop.yarn.server.metrics.ApplicationMetricsConstants;
|
|
import org.apache.hadoop.yarn.server.metrics.ApplicationMetricsConstants;
|
|
import org.apache.hadoop.yarn.server.timelineservice.storage.flow.AggregationCompactionDimension;
|
|
import org.apache.hadoop.yarn.server.timelineservice.storage.flow.AggregationCompactionDimension;
|
|
import org.apache.hadoop.yarn.server.timelineservice.storage.flow.AggregationOperation;
|
|
import org.apache.hadoop.yarn.server.timelineservice.storage.flow.AggregationOperation;
|
|
import org.apache.hadoop.yarn.server.timelineservice.storage.flow.Attribute;
|
|
import org.apache.hadoop.yarn.server.timelineservice.storage.flow.Attribute;
|
|
|
|
+import org.apache.hadoop.yarn.util.ConverterUtils;
|
|
|
|
|
|
/**
|
|
/**
|
|
- * bunch of utility functions used across TimelineWriter classes
|
|
|
|
|
|
+ * A bunch of utility functions used across TimelineReader and TimelineWriter.
|
|
*/
|
|
*/
|
|
-@InterfaceAudience.Public
|
|
|
|
-@InterfaceStability.Unstable
|
|
|
|
-public class TimelineWriterUtils {
|
|
|
|
|
|
+@Public
|
|
|
|
+@Unstable
|
|
|
|
+public class TimelineStorageUtils {
|
|
|
|
|
|
/** empty bytes */
|
|
/** empty bytes */
|
|
public static final byte[] EMPTY_BYTES = new byte[0];
|
|
public static final byte[] EMPTY_BYTES = new byte[0];
|
|
@@ -53,8 +60,7 @@ public class TimelineWriterUtils {
|
|
* Splits the source array into multiple array segments using the given
|
|
* Splits the source array into multiple array segments using the given
|
|
* separator, up to a maximum of count items. This will naturally produce
|
|
* separator, up to a maximum of count items. This will naturally produce
|
|
* copied byte arrays for each of the split segments. To identify the split
|
|
* copied byte arrays for each of the split segments. To identify the split
|
|
- * ranges without the array copies, see
|
|
|
|
- * {@link TimelineWriterUtils#splitRanges(byte[], byte[])}.
|
|
|
|
|
|
+ * ranges without the array copies, see {@link #splitRanges(byte[], byte[])}.
|
|
*
|
|
*
|
|
* @param source
|
|
* @param source
|
|
* @param separator
|
|
* @param separator
|
|
@@ -68,8 +74,7 @@ public class TimelineWriterUtils {
|
|
* Splits the source array into multiple array segments using the given
|
|
* Splits the source array into multiple array segments using the given
|
|
* separator, up to a maximum of count items. This will naturally produce
|
|
* separator, up to a maximum of count items. This will naturally produce
|
|
* copied byte arrays for each of the split segments. To identify the split
|
|
* copied byte arrays for each of the split segments. To identify the split
|
|
- * ranges without the array copies, see
|
|
|
|
- * {@link TimelineWriterUtils#splitRanges(byte[], byte[])}.
|
|
|
|
|
|
+ * ranges without the array copies, see {@link #splitRanges(byte[], byte[])}.
|
|
*
|
|
*
|
|
* @param source
|
|
* @param source
|
|
* @param separator
|
|
* @param separator
|
|
@@ -127,8 +132,7 @@ public class TimelineWriterUtils {
|
|
// everything else goes in one final segment
|
|
// everything else goes in one final segment
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
-
|
|
|
|
- segments.add(new Range(start, i));
|
|
|
|
|
|
+ segments.add(new Range(start, i));
|
|
start = i + separator.length;
|
|
start = i + separator.length;
|
|
// i will be incremented again in outer for loop
|
|
// i will be incremented again in outer for loop
|
|
i += separator.length - 1;
|
|
i += separator.length - 1;
|
|
@@ -149,10 +153,70 @@ public class TimelineWriterUtils {
|
|
* a scan.
|
|
* a scan.
|
|
* @return inverted long
|
|
* @return inverted long
|
|
*/
|
|
*/
|
|
- public static long invert(Long key) {
|
|
|
|
|
|
+ public static long invertLong(long key) {
|
|
return Long.MAX_VALUE - key;
|
|
return Long.MAX_VALUE - key;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Converts an int into it's inverse int to be used in (row) keys
|
|
|
|
+ * where we want to have the largest int value in the top of the table
|
|
|
|
+ * (scans start at the largest int first).
|
|
|
|
+ *
|
|
|
|
+ * @param key value to be inverted so that the latest version will be first in
|
|
|
|
+ * a scan.
|
|
|
|
+ * @return inverted int
|
|
|
|
+ */
|
|
|
|
+ public static int invertInt(int key) {
|
|
|
|
+ return Integer.MAX_VALUE - key;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Converts/encodes a string app Id into a byte representation for (row) keys.
|
|
|
|
+ * For conversion, we extract cluster timestamp and sequence id from the
|
|
|
|
+ * string app id (calls {@link ConverterUtils#toApplicationId(String)} for
|
|
|
|
+ * conversion) and then store it in a byte array of length 12 (8 bytes (long)
|
|
|
|
+ * for cluster timestamp followed 4 bytes(int) for sequence id). Both cluster
|
|
|
|
+ * timestamp and sequence id are inverted so that the most recent cluster
|
|
|
|
+ * timestamp and highest sequence id appears first in the table (i.e.
|
|
|
|
+ * application id appears in a descending order).
|
|
|
|
+ *
|
|
|
|
+ * @param appIdStr application id in string format i.e.
|
|
|
|
+ * application_{cluster timestamp}_{sequence id with min 4 digits}
|
|
|
|
+ *
|
|
|
|
+ * @return encoded byte representation of app id.
|
|
|
|
+ */
|
|
|
|
+ public static byte[] encodeAppId(String appIdStr) {
|
|
|
|
+ ApplicationId appId = ConverterUtils.toApplicationId(appIdStr);
|
|
|
|
+ byte[] appIdBytes = new byte[Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT];
|
|
|
|
+ byte[] clusterTs = Bytes.toBytes(invertLong(appId.getClusterTimestamp()));
|
|
|
|
+ System.arraycopy(clusterTs, 0, appIdBytes, 0, Bytes.SIZEOF_LONG);
|
|
|
|
+ byte[] seqId = Bytes.toBytes(invertInt(appId.getId()));
|
|
|
|
+ System.arraycopy(seqId, 0, appIdBytes, Bytes.SIZEOF_LONG, Bytes.SIZEOF_INT);
|
|
|
|
+ return appIdBytes;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Converts/decodes a 12 byte representation of app id for (row) keys to an
|
|
|
|
+ * app id in string format which can be returned back to client.
|
|
|
|
+ * For decoding, 12 bytes are interpreted as 8 bytes of inverted cluster
|
|
|
|
+ * timestamp(long) followed by 4 bytes of inverted sequence id(int). Calls
|
|
|
|
+ * {@link ApplicationId#toString} to generate string representation of app id.
|
|
|
|
+ *
|
|
|
|
+ * @param appIdBytes application id in byte representation.
|
|
|
|
+ *
|
|
|
|
+ * @return decoded app id in string format.
|
|
|
|
+ */
|
|
|
|
+ public static String decodeAppId(byte[] appIdBytes) {
|
|
|
|
+ if (appIdBytes.length != (Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT)) {
|
|
|
|
+ throw new IllegalArgumentException("Invalid app id in byte format");
|
|
|
|
+ }
|
|
|
|
+ long clusterTs = invertLong(Bytes.toLong(appIdBytes, 0, Bytes.SIZEOF_LONG));
|
|
|
|
+ int seqId =
|
|
|
|
+ invertInt(Bytes.toInt(appIdBytes, Bytes.SIZEOF_LONG, Bytes.SIZEOF_INT));
|
|
|
|
+ return ApplicationId.newInstance(clusterTs, seqId).toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* returns the timestamp of that day's start (which is midnight 00:00:00 AM)
|
|
* returns the timestamp of that day's start (which is midnight 00:00:00 AM)
|
|
* for a given input timestamp
|
|
* for a given input timestamp
|
|
@@ -325,4 +389,87 @@ public class TimelineWriterUtils {
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
-}
|
|
|
|
|
|
+ /**
|
|
|
|
+ *
|
|
|
|
+ * @param entityRelations the relations of an entity
|
|
|
|
+ * @param relationFilters the relations for filtering
|
|
|
|
+ * @return a boolean flag to indicate if both match
|
|
|
|
+ */
|
|
|
|
+ public static boolean matchRelations(
|
|
|
|
+ Map<String, Set<String>> entityRelations,
|
|
|
|
+ Map<String, Set<String>> relationFilters) {
|
|
|
|
+ for (Map.Entry<String, Set<String>> relation : relationFilters.entrySet()) {
|
|
|
|
+ Set<String> ids = entityRelations.get(relation.getKey());
|
|
|
|
+ if (ids == null) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ for (String id : relation.getValue()) {
|
|
|
|
+ if (!ids.contains(id)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ *
|
|
|
|
+ * @param map the map of key/value pairs in an entity
|
|
|
|
+ * @param filters the map of key/value pairs for filtering
|
|
|
|
+ * @return a boolean flag to indicate if both match
|
|
|
|
+ */
|
|
|
|
+ public static boolean matchFilters(Map<String, ? extends Object> map,
|
|
|
|
+ Map<String, ? extends Object> filters) {
|
|
|
|
+ for (Map.Entry<String, ? extends Object> filter : filters.entrySet()) {
|
|
|
|
+ Object value = map.get(filter.getKey());
|
|
|
|
+ if (value == null) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ if (!value.equals(filter.getValue())) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ *
|
|
|
|
+ * @param entityEvents the set of event objects in an entity
|
|
|
|
+ * @param eventFilters the set of event Ids for filtering
|
|
|
|
+ * @return a boolean flag to indicate if both match
|
|
|
|
+ */
|
|
|
|
+ public static boolean matchEventFilters(Set<TimelineEvent> entityEvents,
|
|
|
|
+ Set<String> eventFilters) {
|
|
|
|
+ Set<String> eventIds = new HashSet<String>();
|
|
|
|
+ for (TimelineEvent event : entityEvents) {
|
|
|
|
+ eventIds.add(event.getId());
|
|
|
|
+ }
|
|
|
|
+ for (String eventFilter : eventFilters) {
|
|
|
|
+ if (!eventIds.contains(eventFilter)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ *
|
|
|
|
+ * @param metrics the set of metric objects in an entity
|
|
|
|
+ * @param metricFilters the set of metric Ids for filtering
|
|
|
|
+ * @return a boolean flag to indicate if both match
|
|
|
|
+ */
|
|
|
|
+ public static boolean matchMetricFilters(Set<TimelineMetric> metrics,
|
|
|
|
+ Set<String> metricFilters) {
|
|
|
|
+ Set<String> metricIds = new HashSet<String>();
|
|
|
|
+ for (TimelineMetric metric : metrics) {
|
|
|
|
+ metricIds.add(metric.getId());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (String metricFilter : metricFilters) {
|
|
|
|
+ if (!metricIds.contains(metricFilter)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+}
|