>From Michael Blow <[email protected]>:
Michael Blow has uploaded this change for review. (
https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21185?usp=email )
Change subject: [NO ISSUE][HYR][MISC] Fix Span lifefycle / API issues
......................................................................
[NO ISSUE][HYR][MISC] Fix Span lifefycle / API issues
Ext-ref: MB-71012
Change-Id: I1111786ffbdb4edc7839525e8ef7907147314b0b
---
M
asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/azure/blob/BlobUtils.java
M
hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/Span.java
A
hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/SpanTest.java
3 files changed, 208 insertions(+), 6 deletions(-)
git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb
refs/changes/85/21185/1
diff --git
a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/azure/blob/BlobUtils.java
b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/azure/blob/BlobUtils.java
index 8b6b99f..b4caa52 100644
---
a/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/azure/blob/BlobUtils.java
+++
b/asterixdb/asterix-external-data/src/main/java/org/apache/asterix/external/util/azure/blob/BlobUtils.java
@@ -58,6 +58,7 @@
import org.apache.hyracks.api.exceptions.IWarningCollector;
import org.apache.hyracks.api.exceptions.SourceLocation;
import org.apache.hyracks.api.exceptions.Warning;
+import org.apache.hyracks.util.annotations.AiProvenance;
import com.azure.core.credential.AzureSasCredential;
import com.azure.core.http.netty.NettyAsyncHttpClientBuilder;
@@ -75,7 +76,6 @@
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
-import org.apache.hyracks.util.annotations.AiProvenance;
import reactor.netty.http.client.HttpClient;
public class BlobUtils {
diff --git
a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/Span.java
b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/Span.java
index 8c8f1ce..a9eede7 100644
---
a/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/Span.java
+++
b/hyracks-fullstack/hyracks/hyracks-util/src/main/java/org/apache/hyracks/util/Span.java
@@ -83,7 +83,62 @@
}
};
- public static final Span ELAPSED = start(0, TimeUnit.NANOSECONDS);
+ public static final Span ELAPSED = new Span() {
+ @Override
+ public void reset() {
+ // no-op
+ }
+
+ @Override
+ public long getSpanNanos() {
+ return 0;
+ }
+
+ @Override
+ public long getSpan(TimeUnit unit) {
+ return 0;
+ }
+
+ @Override
+ public boolean elapsed() {
+ return true;
+ }
+
+ @Override
+ public long elapsed(TimeUnit unit) {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public void sleep() {
+ // no-op: already elapsed
+ }
+
+ @Override
+ public void sleep(long sleep, TimeUnit unit) {
+ // no-op: already elapsed
+ }
+
+ @Override
+ public long remaining(TimeUnit unit) {
+ return 0;
+ }
+
+ @Override
+ public void wait(Object monitor) {
+ // no-op: already elapsed
+ }
+
+ @Override
+ public boolean await(CountDownLatch latch) {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "<ELAPSED>";
+ }
+ };
private final long spanNanos;
private volatile long startNanos;
@@ -114,12 +169,32 @@
return s;
}
- public static Span init(long span, TimeUnit unit) {
- return new Span(span, unit);
+ /**
+ * Creates a span that is immediately elapsed, analogous to a do/while
loop: the elapsed condition is true on the
+ * first check, triggering immediate action, after which {@link #reset()}
begins the countdown for subsequent
+ * iterations. Contrast with {@link #start(long, TimeUnit)}, which is
analogous to a while/do loop: the span must
+ * actually expire before the elapsed condition becomes true.
+ * <p>
+ * Typical usage: bootstrapping state that has a TTL, where the first
fetch should happen immediately and
+ * subsequent refreshes should be rate-limited by the span duration.
+ * <pre>
+ * Span refreshSpan = Span.startElapsed(ttlSeconds, TimeUnit.SECONDS);
+ * while (running) {
+ * if (refreshSpan.elapsed()) {
+ * refresh();
+ * refreshSpan.reset();
+ * }
+ * }
+ * </pre>
+ */
+ public static Span startElapsed(long span, TimeUnit unit) {
+ Span s = new Span(span, unit);
+ s.startNanos = System.nanoTime() - s.spanNanos;
+ return s;
}
public boolean elapsed() {
- return elapsed(TimeUnit.NANOSECONDS) > spanNanos;
+ return elapsed(TimeUnit.NANOSECONDS) >= spanNanos;
}
public long elapsed(TimeUnit unit) {
@@ -239,7 +314,7 @@
if (micros > 0) {
builder.append(micros).append("us");
}
- if (nanos > 0 || builder.length() == 0) {
+ if (nanos > 0 || builder.isEmpty()) {
builder.append(nanos).append("ns");
}
return builder.toString();
diff --git
a/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/SpanTest.java
b/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/SpanTest.java
new file mode 100644
index 0000000..2e8fd4f
--- /dev/null
+++
b/hyracks-fullstack/hyracks/hyracks-util/src/test/java/org/apache/hyracks/util/SpanTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.hyracks.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hyracks.util.annotations.AiProvenance;
+import org.junit.Test;
+
+@AiProvenance(agent = AiProvenance.Agent.CLAUDE_SONNET_4_6, tool =
AiProvenance.Tool.GITHUB_COPILOT, contributionKind =
AiProvenance.ContributionKind.TEST_GENERATED)
+public class SpanTest {
+
+ // --- Span.ELAPSED ---
+
+ @Test
+ public void elapsedConstant_alwaysElapsed() {
+ assertTrue(Span.ELAPSED.elapsed());
+ }
+
+ @Test
+ public void elapsedConstant_resetIsNoOp() {
+ Span.ELAPSED.reset();
+ assertTrue("ELAPSED.reset() should be a no-op; span should still be
elapsed", Span.ELAPSED.elapsed());
+ }
+
+ @Test
+ public void elapsedConstant_remainingIsZero() {
+ assertEquals(0, Span.ELAPSED.remaining(TimeUnit.NANOSECONDS));
+ assertEquals(0, Span.ELAPSED.remaining(TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void elapsedConstant_elapsedUnitIsMaxValue() {
+ assertEquals(Long.MAX_VALUE,
Span.ELAPSED.elapsed(TimeUnit.NANOSECONDS));
+ }
+
+ @Test
+ public void elapsedConstant_awaitReturnsFalseImmediately() throws
InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ assertFalse(Span.ELAPSED.await(latch));
+ }
+
+ @Test
+ public void elapsedConstant_toStringIsElapsed() {
+ assertEquals("<ELAPSED>", Span.ELAPSED.toString());
+ }
+
+ // --- Span.startElapsed() ---
+
+ @Test
+ public void startElapsed_immediatelyElapsed() {
+ Span span = Span.startElapsed(5, TimeUnit.SECONDS);
+ assertTrue("startElapsed span should immediately report elapsed()",
span.elapsed());
+ }
+
+ @Test
+ public void startElapsed_notElapsedAfterReset() {
+ Span span = Span.startElapsed(5, TimeUnit.SECONDS);
+ span.reset();
+ assertFalse("startElapsed span should not be elapsed immediately after
reset()", span.elapsed());
+ }
+
+ @Test
+ public void startElapsed_elapsedAfterSpanDuration() throws
InterruptedException {
+ Span span = Span.startElapsed(10, TimeUnit.MILLISECONDS);
+ span.reset();
+ assertFalse(span.elapsed());
+ TimeUnit.MILLISECONDS.sleep(15);
+ assertTrue("startElapsed span should be elapsed after duration has
passed", span.elapsed());
+ }
+
+ @Test
+ public void startElapsed_remainingIsZeroBeforeReset() {
+ Span span = Span.startElapsed(5, TimeUnit.SECONDS);
+ assertEquals(0, span.remaining(TimeUnit.NANOSECONDS));
+ }
+
+ @Test
+ public void startElapsed_remainingIsPositiveAfterReset() {
+ Span span = Span.startElapsed(5, TimeUnit.SECONDS);
+ span.reset();
+ assertTrue("remaining should be positive after reset",
span.remaining(TimeUnit.MILLISECONDS) > 0);
+ }
+
+ // --- elapsed() boundary: >= ensures span is elapsed at exactly spanNanos
---
+
+ @Test
+ public void start_elapsedAtExactlySpanDuration() throws
InterruptedException {
+ // A span of 0ns should be immediately elapsed after reset (>= 0)
+ Span span = Span.start(0, TimeUnit.NANOSECONDS);
+ assertTrue("0ns span should be elapsed immediately (elapsed >=
spanNanos)", span.elapsed());
+ }
+
+ // --- Span.INFINITE sanity ---
+
+ @Test
+ public void infiniteConstant_neverElapsed() {
+ assertFalse(Span.INFINITE.elapsed());
+ }
+
+ @Test
+ public void infiniteConstant_remainingIsMaxValue() {
+ assertEquals(Long.MAX_VALUE,
Span.INFINITE.remaining(TimeUnit.NANOSECONDS));
+ }
+}
--
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21185?usp=email
To unsubscribe, or for help writing mail filters, visit
https://asterix-gerrit.ics.uci.edu/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: asterixdb
Gerrit-Branch: phoenix
Gerrit-Change-Id: I1111786ffbdb4edc7839525e8ef7907147314b0b
Gerrit-Change-Number: 21185
Gerrit-PatchSet: 1
Gerrit-Owner: Michael Blow <[email protected]>