Repository: asterixdb Updated Branches: refs/heads/master 71589e574 -> 016603909
[NO ISSUE][TEST][CLUS] Update cluster state on shutdown, testfwk looping - Set ClusterState to SHUTTING_DOWN when the cluster is shutting down - Add ability to TestExecutor to loop over test files based on iteration count or duration Change-Id: I2bd67cb92a60d76eabe1e7649d16193dd72615dd Reviewed-on: https://asterix-gerrit.ics.uci.edu/1978 Integration-Tests: Jenkins <[email protected]> Tested-by: Jenkins <[email protected]> Reviewed-by: Murtadha Hubail <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/01660390 Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/01660390 Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/01660390 Branch: refs/heads/master Commit: 016603909756fef20f4504d4645adea60a9467e4 Parents: 71589e5 Author: Michael Blow <[email protected]> Authored: Sun Aug 27 16:03:57 2017 -0400 Committer: Michael Blow <[email protected]> Committed: Sun Aug 27 15:45:42 2017 -0700 ---------------------------------------------------------------------- .../asterix/api/http/server/Duration.java | 236 ------------------- .../api/http/server/NCQueryServiceServlet.java | 1 + .../api/http/server/QueryServiceServlet.java | 1 + .../api/http/server/ShutdownApiServlet.java | 3 + .../hyracks/bootstrap/CCApplication.java | 8 +- .../asterix/runtime/ParseDurationTest.java | 2 +- .../asterix/test/common/TestExecutor.java | 121 +++++++++- .../api/diagnostics_1/diagnostics_1.2.loop.cmd | 21 ++ .../org/apache/asterix/common/api/Duration.java | 236 +++++++++++++++++++ .../common/api/IClusterManagementWork.java | 3 +- 10 files changed, 389 insertions(+), 243 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/Duration.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/Duration.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/Duration.java deleted file mode 100644 index bdda750..0000000 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/Duration.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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 - * - * 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 org.apache.asterix.api.http.server; - -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.exceptions.RuntimeDataException; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.lang3.tuple.Triple; -import org.apache.hyracks.api.exceptions.HyracksDataException; - -public enum Duration { - SEC("s", 9), - MILLI("ms", 6), - MICRO("µs", 3), - NANO("ns", 0); - - static final long NANOSECONDS = 1; - static final long MICROSECONDS = 1000 * NANOSECONDS; - static final long MILLISECONDS = 1000 * MICROSECONDS; - static final long SECONDS = 1000 * MILLISECONDS; - static final long MINUTES = 60 * SECONDS; - static final long HOURS = 60 * MINUTES; - - String unit; - int nanoDigits; - - Duration(String unit, int nanoDigits) { - this.unit = unit; - this.nanoDigits = nanoDigits; - } - - public static String formatNanos(long nanoTime) { - final String strTime = String.valueOf(nanoTime); - final int len = strTime.length(); - for (Duration tu : Duration.values()) { - if (len > tu.nanoDigits) { - final String integer = strTime.substring(0, len - tu.nanoDigits); - final String fractional = strTime.substring(len - tu.nanoDigits); - return integer + (fractional.length() > 0 ? "." + fractional : "") + tu.unit; - } - } - return "illegal string value: " + strTime; - } - - // ParseDuration parses a duration string. - // A duration string is a possibly signed sequence of - // decimal numbers, each with optional fraction and a unit suffix, - // such as "300ms", "-1.5h" or "2h45m". - // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". - // returns the duration in nano seconds - public static long parseDurationStringToNanos(String orig) throws HyracksDataException { - // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ - String s = orig; - long d = 0; - boolean neg = false; - char c; - // Consume [-+]? - if (!s.isEmpty()) { - c = s.charAt(0); - if (c == '-' || c == '+') { - neg = c == '-'; - s = s.substring(1); - } - } - - // Special case: if all that is left is "0", this is zero. - if ("0".equals(s)) { - return 0L; - } - - if (s.isEmpty()) { - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - - while (!s.isEmpty()) { - long v = 0L; // integers before decimal - long f = 0L; // integers after decimal - double scale = 1.0; // value = v + f/scale - // The next character must be [0-9.] - if (!(s.charAt(0) == '.' || '0' <= s.charAt(0) && s.charAt(0) <= '9')) { - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - // Consume [0-9]* - int pl = s.length(); - Pair<Long, String> pair = leadingInt(s); - v = pair.getLeft(); - s = pair.getRight(); - boolean pre = pl != s.length(); // whether we consumed anything before a period - - // Consume (\.[0-9]*)? - boolean post = false; - if (!s.isEmpty() && s.charAt(0) == '.') { - s = s.substring(1); - pl = s.length(); - Triple<Long, Double, String> triple = leadingFraction(s); - f = triple.getLeft(); - scale = triple.getMiddle(); - s = triple.getRight(); - post = pl != s.length(); - } - if (!pre && !post) { - // no digits (e.g. ".s" or "-.s") - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - - // Consume unit. - int i = 0; - for (; i < s.length(); i++) { - c = s.charAt(i); - if (c == '.' || '0' <= c && c <= '9') { - break; - } - } - if (i == 0) { - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - String u = s.substring(0, i); - s = s.substring(i); - long unit = getUnit(u); - if (v > Long.MAX_VALUE / unit) { - // overflow - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - v *= unit; - if (f > 0) { - // float64 is needed to be nanosecond accurate for fractions of hours. - // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) - v += (long) (((double) f * (double) unit) / scale); - if (v < 0) { - // overflow - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - } - d += v; - if (d < 0) { - // overflow - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); - } - } - - if (neg) { - d = -d; - } - return d; - } - - private static final long getUnit(String unit) throws HyracksDataException { - switch (unit) { - case "ns": - return NANOSECONDS; - case "us": - case "µs":// U+00B5 = micro symbol - case "μs":// U+03BC = Greek letter mu - return MICROSECONDS; - case "ms": - return MILLISECONDS; - case "s": - return SECONDS; - case "m": - return MINUTES; - case "h": - return HOURS; - default: - throw new RuntimeDataException(ErrorCode.UNKNOWN_DURATION_UNIT, unit); - } - } - - // leadingInt consumes the leading [0-9]* from s. - static Pair<Long, String> leadingInt(String origin) throws HyracksDataException { - String s = origin; - long x = 0L; - int i = 0; - for (; i < s.length(); i++) { - char c = s.charAt(i); - if (c < '0' || c > '9') { - break; - } - if (x > Long.MAX_VALUE / 10) { - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, origin); - } - x = x * 10 + Character.getNumericValue(c); - if (x < 0) { - throw new RuntimeDataException(ErrorCode.INVALID_DURATION, origin); - } - } - return Pair.of(x, s.substring(i)); - } - - // leadingFraction consumes the leading [0-9]* from s. - // It is used only for fractions, so does not return an error on overflow, - // it just stops accumulating precision. - static Triple<Long, Double, String> leadingFraction(String s) { - int i = 0; - long x = 0L; - double scale = 1.0; - boolean overflow = false; - for (; i < s.length(); i++) { - char c = s.charAt(i); - if (c < '0' || c > '9') { - break; - } - if (overflow) { - continue; - } - if (x > (1 << 63 - 1) / 10) { - // It's possible for overflow to give a positive number, so take care. - overflow = true; - continue; - } - long y = x * 10 + Character.getNumericValue(c); - if (y < 0) { - overflow = true; - continue; - } - x = y; - scale *= 10; - } - return Triple.of(x, scale, s.substring(i)); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java index 69e995b..8d355ef 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/NCQueryServiceServlet.java @@ -29,6 +29,7 @@ import org.apache.asterix.app.message.CancelQueryRequest; import org.apache.asterix.app.message.ExecuteStatementRequestMessage; import org.apache.asterix.app.message.ExecuteStatementResponseMessage; import org.apache.asterix.app.result.ResultReader; +import org.apache.asterix.common.api.Duration; import org.apache.asterix.common.api.IApplicationContext; import org.apache.asterix.common.config.GlobalConfig; import org.apache.asterix.common.exceptions.ErrorCode; http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java index c630636..fa67190 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java @@ -27,6 +27,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.asterix.algebra.base.ILangExtension; +import org.apache.asterix.common.api.Duration; import org.apache.asterix.common.api.IApplicationContext; import org.apache.asterix.common.api.IClusterManagementWork; import org.apache.asterix.common.config.GlobalConfig; http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java index 06e2383..38f6691 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ShutdownApiServlet.java @@ -19,6 +19,7 @@ package org.apache.asterix.api.http.server; import static org.apache.asterix.api.http.server.ServletConstants.HYRACKS_CONNECTION_ATTR; +import static org.apache.asterix.common.api.IClusterManagementWork.ClusterState.SHUTTING_DOWN; import java.io.IOException; import java.io.PrintWriter; @@ -92,6 +93,8 @@ public class ShutdownApiServlet extends AbstractServlet { jsonObject.set("cluster", clusterState); final PrintWriter writer = response.writer(); writer.print(JSONUtil.convertNode(jsonObject)); + // accept no further queries once this servlet returns + ClusterStateManager.INSTANCE.setState(SHUTTING_DOWN); writer.close(); } catch (Exception e) { GlobalConfig.ASTERIX_LOGGER.log(Level.INFO, "Exception writing response", e); http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java index b08c1e2..6d817b8 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java @@ -23,6 +23,7 @@ import static org.apache.asterix.algebra.base.ILangExtension.Language.AQL; import static org.apache.asterix.algebra.base.ILangExtension.Language.SQLPP; import static org.apache.asterix.api.http.server.ServletConstants.ASTERIX_APP_CONTEXT_INFO_ATTR; import static org.apache.asterix.api.http.server.ServletConstants.HYRACKS_CONNECTION_ATTR; +import static org.apache.asterix.common.api.IClusterManagementWork.ClusterState.SHUTTING_DOWN; import java.util.Arrays; import java.util.List; @@ -187,12 +188,13 @@ public class CCApplication extends BaseCCApplication { @Override public void stop() throws Exception { - if (appCtx != null) { - ((ActiveNotificationHandler) appCtx.getActiveNotificationHandler()).stop(); - } if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Stopping Asterix cluster controller"); } + ClusterStateManager.INSTANCE.setState(SHUTTING_DOWN); + if (appCtx != null) { + ((ActiveNotificationHandler) appCtx.getActiveNotificationHandler()).stop(); + } AsterixStateProxy.unregisterRemoteObject(); webManager.stop(); } http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/ParseDurationTest.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/ParseDurationTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/ParseDurationTest.java index 12c61d6..f2fb580 100644 --- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/ParseDurationTest.java +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/runtime/ParseDurationTest.java @@ -18,7 +18,7 @@ */ package org.apache.asterix.runtime; -import org.apache.asterix.api.http.server.Duration; +import org.apache.asterix.common.api.Duration; import org.apache.hyracks.api.exceptions.HyracksDataException; import org.junit.Assert; import org.junit.Test; http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java index e07ec72..cfd01ac 100644 --- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -56,6 +57,7 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.asterix.common.api.Duration; import org.apache.asterix.common.config.GlobalConfig; import org.apache.asterix.common.utils.Servlets; import org.apache.asterix.test.server.ITestServer; @@ -131,6 +133,7 @@ public class TestExecutor { protected final List<InetSocketAddress> endpoints; protected int endpointSelector; protected ITestLibrarian librarian; + private Map<File, TestLoop> testLoops = new HashMap<>(); public TestExecutor() { this(Inet4Address.getLoopbackAddress().getHostAddress(), 19002); @@ -1080,6 +1083,59 @@ public class TestExecutor { killNC(nodeId, cUnit); } break; + case "loop": + TestLoop testLoop = testLoops.get(testFile); + if (testLoop == null) { + lines = stripAllComments(statement).trim().split("\n"); + String target = null; + int count = -1; + long durationSecs = -1; + for (String line : lines) { + command = line.trim().split(" "); + switch (command[0]) { + case "target": + if (target != null) { + throw new IllegalStateException("duplicate target"); + } + target = command[1]; + break; + case "count": + if (count != -1) { + throw new IllegalStateException("duplicate count"); + } + count = Integer.parseInt(command[1]); + break; + case "duration": + if (durationSecs != -1) { + throw new IllegalStateException("duplicate duration"); + } + long duration = Duration.parseDurationStringToNanos(command[1]); + durationSecs = TimeUnit.NANOSECONDS.toSeconds(duration); + if (durationSecs < 1) { + throw new IllegalArgumentException("duration cannot be shorter than 1s"); + } else if (TimeUnit.SECONDS.toDays(durationSecs) > 1) { + throw new IllegalArgumentException("duration cannot be exceed 1d"); + } + break; + default: + throw new IllegalArgumentException("unknown directive: " + command[0]); + } + } + if (target == null || (count == -1 && durationSecs == -1) || (count != -1 && durationSecs != -1)) { + throw new IllegalStateException("Must specify 'target' and exactly one of 'count', 'duration'"); + } + if (count != -1) { + testLoop = TestLoop.createLoop(target, count); + } else { + testLoop = TestLoop.createLoop(target, durationSecs, TimeUnit.SECONDS); + } + testLoops.put(testFile, testLoop); + } + testLoop.executeLoop(); + // we only reach here if the loop is over + testLoops.remove(testFile); + break; + default: throw new IllegalArgumentException("No statements of type " + ctx.getType()); } @@ -1089,7 +1145,7 @@ public class TestExecutor { String reqType, File testFile, File expectedResultFile, File actualResultFile, MutableInt queryCount, int numResultFiles, String extension, ComparisonEnum compare) throws Exception { String handleVar = getHandleVariable(statement); - final String trimmedPathAndQuery = stripLineComments(stripJavaComments(statement)).trim(); + final String trimmedPathAndQuery = stripAllComments(statement).trim(); final String variablesReplaced = replaceVarRef(trimmedPathAndQuery, variableCtx); final List<Parameter> params = extractParameters(statement); final Predicate<Integer> statusCodePredicate = extractStatusCodePredicate(statement); @@ -1361,13 +1417,26 @@ public class TestExecutor { Map<String, Object> variableCtx = new HashMap<>(); List<TestFileContext> testFileCtxs = testCaseCtx.getTestFiles(cUnit); List<TestFileContext> expectedResultFileCtxs = testCaseCtx.getExpectedResultFiles(cUnit); - for (TestFileContext ctx : testFileCtxs) { + int[] savedQueryCounts = new int[numOfFiles + testFileCtxs.size()]; + for (ListIterator<TestFileContext> iter = testFileCtxs.listIterator(); iter.hasNext();) { + TestFileContext ctx = iter.next(); + savedQueryCounts[numOfFiles] = queryCount.getValue(); numOfFiles++; final File testFile = ctx.getFile(); final String statement = readTestFile(testFile); try { executeTestFile(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, expectedResultFileCtxs, testFile, actualPath); + } catch (TestLoop loop) { + // rewind the iterator until we find our target + while (!ctx.getFile().getName().equals(loop.getTarget())) { + if (!iter.hasPrevious()) { + throw new IllegalStateException("unable to find loop target '" + loop.getTarget() + "'!"); + } + ctx = iter.previous(); + numOfFiles--; + queryCount.setValue(savedQueryCounts[numOfFiles]); + } } catch (Exception e) { System.err.println("testFile " + testFile.toString() + " raised an exception: " + e); numOfErrors++; @@ -1442,6 +1511,10 @@ public class TestExecutor { return JAVA_LINE_COMMENT_PATTERN.matcher(s).replaceAll(""); } + public static String stripAllComments(String statement) { + return stripLineComments(stripJavaComments(statement)); + } + public void cleanup(String testCase, List<String> badtestcases) throws Exception { try { ArrayList<String> toBeDropped = new ArrayList<>(); @@ -1531,4 +1604,48 @@ public class TestExecutor { LOGGER.info("Cluster state now " + desiredState); } + abstract static class TestLoop extends Exception { + + private final String target; + + TestLoop(String target) { + this.target = target; + } + + static TestLoop createLoop(String target, final int count) { + LOGGER.info("Starting loop '" + count + " times back to '" + target + "'..."); + return new TestLoop(target) { + int remainingLoops = count; + + @Override + void executeLoop() throws TestLoop { + if (remainingLoops-- > 0) { + throw this; + } + LOGGER.info("Loop to '" + target + "' complete!"); + } + }; + } + + static TestLoop createLoop(String target, long duration, TimeUnit unit) { + LOGGER.info("Starting loop for " + unit.toSeconds(duration) + "s back to '" + target + "'..."); + return new TestLoop(target) { + long endTime = unit.toMillis(duration) + System.currentTimeMillis(); + + @Override + void executeLoop() throws TestLoop { + if (System.currentTimeMillis() < endTime) { + throw this; + } + LOGGER.info("Loop to '" + target + "' complete!"); + } + }; + } + + abstract void executeLoop() throws TestLoop; + + public String getTarget() { + return target; + } + } } http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.2.loop.cmd ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.2.loop.cmd b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.2.loop.cmd new file mode 100644 index 0000000..2660b46 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries/api/diagnostics_1/diagnostics_1.2.loop.cmd @@ -0,0 +1,21 @@ +/* + * 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 + * + * 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. + */ + +target diagnostics_1.1.get.http +count 2 http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/Duration.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/Duration.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/Duration.java new file mode 100644 index 0000000..4338222 --- /dev/null +++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/Duration.java @@ -0,0 +1,236 @@ +/* + * 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 + * + * 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 org.apache.asterix.common.api; + +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.common.exceptions.RuntimeDataException; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.apache.hyracks.api.exceptions.HyracksDataException; + +public enum Duration { + SEC("s", 9), + MILLI("ms", 6), + MICRO("µs", 3), + NANO("ns", 0); + + static final long NANOSECONDS = 1; + static final long MICROSECONDS = 1000 * NANOSECONDS; + static final long MILLISECONDS = 1000 * MICROSECONDS; + static final long SECONDS = 1000 * MILLISECONDS; + static final long MINUTES = 60 * SECONDS; + static final long HOURS = 60 * MINUTES; + + String unit; + int nanoDigits; + + Duration(String unit, int nanoDigits) { + this.unit = unit; + this.nanoDigits = nanoDigits; + } + + public static String formatNanos(long nanoTime) { + final String strTime = String.valueOf(nanoTime); + final int len = strTime.length(); + for (Duration tu : Duration.values()) { + if (len > tu.nanoDigits) { + final String integer = strTime.substring(0, len - tu.nanoDigits); + final String fractional = strTime.substring(len - tu.nanoDigits); + return integer + (fractional.length() > 0 ? "." + fractional : "") + tu.unit; + } + } + return "illegal string value: " + strTime; + } + + // ParseDuration parses a duration string. + // A duration string is a possibly signed sequence of + // decimal numbers, each with optional fraction and a unit suffix, + // such as "300ms", "-1.5h" or "2h45m". + // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + // returns the duration in nano seconds + public static long parseDurationStringToNanos(String orig) throws HyracksDataException { + // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ + String s = orig; + long d = 0; + boolean neg = false; + char c; + // Consume [-+]? + if (!s.isEmpty()) { + c = s.charAt(0); + if (c == '-' || c == '+') { + neg = c == '-'; + s = s.substring(1); + } + } + + // Special case: if all that is left is "0", this is zero. + if ("0".equals(s)) { + return 0L; + } + + if (s.isEmpty()) { + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + + while (!s.isEmpty()) { + long v = 0L; // integers before decimal + long f = 0L; // integers after decimal + double scale = 1.0; // value = v + f/scale + // The next character must be [0-9.] + if (!(s.charAt(0) == '.' || '0' <= s.charAt(0) && s.charAt(0) <= '9')) { + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + // Consume [0-9]* + int pl = s.length(); + Pair<Long, String> pair = leadingInt(s); + v = pair.getLeft(); + s = pair.getRight(); + boolean pre = pl != s.length(); // whether we consumed anything before a period + + // Consume (\.[0-9]*)? + boolean post = false; + if (!s.isEmpty() && s.charAt(0) == '.') { + s = s.substring(1); + pl = s.length(); + Triple<Long, Double, String> triple = leadingFraction(s); + f = triple.getLeft(); + scale = triple.getMiddle(); + s = triple.getRight(); + post = pl != s.length(); + } + if (!pre && !post) { + // no digits (e.g. ".s" or "-.s") + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + + // Consume unit. + int i = 0; + for (; i < s.length(); i++) { + c = s.charAt(i); + if (c == '.' || '0' <= c && c <= '9') { + break; + } + } + if (i == 0) { + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + String u = s.substring(0, i); + s = s.substring(i); + long unit = getUnit(u); + if (v > Long.MAX_VALUE / unit) { + // overflow + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + v *= unit; + if (f > 0) { + // float64 is needed to be nanosecond accurate for fractions of hours. + // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) + v += (long) (((double) f * (double) unit) / scale); + if (v < 0) { + // overflow + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + } + d += v; + if (d < 0) { + // overflow + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, orig); + } + } + + if (neg) { + d = -d; + } + return d; + } + + private static final long getUnit(String unit) throws HyracksDataException { + switch (unit) { + case "ns": + return NANOSECONDS; + case "us": + case "µs":// U+00B5 = micro symbol + case "μs":// U+03BC = Greek letter mu + return MICROSECONDS; + case "ms": + return MILLISECONDS; + case "s": + return SECONDS; + case "m": + return MINUTES; + case "h": + return HOURS; + default: + throw new RuntimeDataException(ErrorCode.UNKNOWN_DURATION_UNIT, unit); + } + } + + // leadingInt consumes the leading [0-9]* from s. + static Pair<Long, String> leadingInt(String origin) throws HyracksDataException { + String s = origin; + long x = 0L; + int i = 0; + for (; i < s.length(); i++) { + char c = s.charAt(i); + if (c < '0' || c > '9') { + break; + } + if (x > Long.MAX_VALUE / 10) { + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, origin); + } + x = x * 10 + Character.getNumericValue(c); + if (x < 0) { + throw new RuntimeDataException(ErrorCode.INVALID_DURATION, origin); + } + } + return Pair.of(x, s.substring(i)); + } + + // leadingFraction consumes the leading [0-9]* from s. + // It is used only for fractions, so does not return an error on overflow, + // it just stops accumulating precision. + static Triple<Long, Double, String> leadingFraction(String s) { + int i = 0; + long x = 0L; + double scale = 1.0; + boolean overflow = false; + for (; i < s.length(); i++) { + char c = s.charAt(i); + if (c < '0' || c > '9') { + break; + } + if (overflow) { + continue; + } + if (x > (1 << 63 - 1) / 10) { + // It's possible for overflow to give a positive number, so take care. + overflow = true; + continue; + } + long y = x * 10 + Character.getNumericValue(c); + if (y < 0) { + overflow = true; + continue; + } + x = y; + scale *= 10; + } + return Triple.of(x, scale, s.substring(i)); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/asterixdb/blob/01660390/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IClusterManagementWork.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IClusterManagementWork.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IClusterManagementWork.java index 323df65..bdbf4a5 100644 --- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IClusterManagementWork.java +++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/IClusterManagementWork.java @@ -30,7 +30,8 @@ public interface IClusterManagementWork { PENDING, ACTIVE, UNUSABLE, - REBALANCING + REBALANCING, + SHUTTING_DOWN } public WorkType getClusterManagementWorkType();
