|
@@ -26,6 +26,7 @@ import java.util.List;
|
|
|
import java.util.concurrent.Executor;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
+import java.util.function.LongSupplier;
|
|
|
import java.util.function.Supplier;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
@@ -49,6 +50,22 @@ public class Driver implements Releasable, Describable {
|
|
|
public static final TimeValue DEFAULT_STATUS_INTERVAL = TimeValue.timeValueSeconds(1);
|
|
|
|
|
|
private final String sessionId;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The wall clock time when this driver was created in milliseconds since epoch.
|
|
|
+ * Compared to {@link #startNanos} this is less accurate and is measured by a
|
|
|
+ * timer that can go backwards. This is only useful for presenting times to a
|
|
|
+ * user, like over the status API.
|
|
|
+ */
|
|
|
+ private final long startTime;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The time when this driver was created in nanos. This time is relative to
|
|
|
+ * some arbitrary point - imagine its program startup. The timer that generates
|
|
|
+ * this is monotonically increasing so even if NTP or something changes the
|
|
|
+ * clock it won't change. As such, this is only useful for measuring durations.
|
|
|
+ */
|
|
|
+ private final long startNanos;
|
|
|
private final DriverContext driverContext;
|
|
|
private final Supplier<String> description;
|
|
|
private final List<Operator> activeOperators;
|
|
@@ -69,6 +86,13 @@ public class Driver implements Releasable, Describable {
|
|
|
*/
|
|
|
private final AtomicReference<DriverStatus> status;
|
|
|
|
|
|
+ /**
|
|
|
+ * The time this driver finished. Only set once the driver is finished, defaults to 0
|
|
|
+ * which is *possibly* a valid value, so always use the driver status to check
|
|
|
+ * if the driver is actually finished.
|
|
|
+ */
|
|
|
+ private long finishNanos;
|
|
|
+
|
|
|
/**
|
|
|
* Creates a new driver with a chain of operators.
|
|
|
* @param sessionId session Id
|
|
@@ -81,6 +105,8 @@ public class Driver implements Releasable, Describable {
|
|
|
*/
|
|
|
public Driver(
|
|
|
String sessionId,
|
|
|
+ long startTime,
|
|
|
+ long startNanos,
|
|
|
DriverContext driverContext,
|
|
|
Supplier<String> description,
|
|
|
SourceOperator source,
|
|
@@ -90,6 +116,8 @@ public class Driver implements Releasable, Describable {
|
|
|
Releasable releasable
|
|
|
) {
|
|
|
this.sessionId = sessionId;
|
|
|
+ this.startTime = startTime;
|
|
|
+ this.startNanos = startNanos;
|
|
|
this.driverContext = driverContext;
|
|
|
this.description = description;
|
|
|
this.activeOperators = new ArrayList<>();
|
|
@@ -99,7 +127,7 @@ public class Driver implements Releasable, Describable {
|
|
|
this.statusNanos = statusInterval.nanos();
|
|
|
this.releasable = releasable;
|
|
|
this.status = new AtomicReference<>(
|
|
|
- new DriverStatus(sessionId, System.currentTimeMillis(), DriverStatus.Status.QUEUED, List.of(), List.of())
|
|
|
+ new DriverStatus(sessionId, startTime, System.currentTimeMillis(), 0, 0, DriverStatus.Status.QUEUED, List.of(), List.of())
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -118,7 +146,18 @@ public class Driver implements Releasable, Describable {
|
|
|
SinkOperator sink,
|
|
|
Releasable releasable
|
|
|
) {
|
|
|
- this("unset", driverContext, () -> null, source, intermediateOperators, sink, DEFAULT_STATUS_INTERVAL, releasable);
|
|
|
+ this(
|
|
|
+ "unset",
|
|
|
+ System.currentTimeMillis(),
|
|
|
+ System.nanoTime(),
|
|
|
+ driverContext,
|
|
|
+ () -> null,
|
|
|
+ source,
|
|
|
+ intermediateOperators,
|
|
|
+ sink,
|
|
|
+ DEFAULT_STATUS_INTERVAL,
|
|
|
+ releasable
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
public DriverContext driverContext() {
|
|
@@ -130,38 +169,39 @@ public class Driver implements Releasable, Describable {
|
|
|
* Returns a blocked future when the chain of operators is blocked, allowing the caller
|
|
|
* thread to do other work instead of blocking or busy-spinning on the blocked operator.
|
|
|
*/
|
|
|
- private SubscribableListener<Void> run(TimeValue maxTime, int maxIterations) {
|
|
|
+ SubscribableListener<Void> run(TimeValue maxTime, int maxIterations, LongSupplier nowSupplier) {
|
|
|
long maxTimeNanos = maxTime.nanos();
|
|
|
- long startTime = System.nanoTime();
|
|
|
+ long startTime = nowSupplier.getAsLong();
|
|
|
long nextStatus = startTime + statusNanos;
|
|
|
int iter = 0;
|
|
|
- while (isFinished() == false) {
|
|
|
+ while (true) {
|
|
|
SubscribableListener<Void> fut = runSingleLoopIteration();
|
|
|
+ iter++;
|
|
|
if (fut.isDone() == false) {
|
|
|
- status.set(updateStatus(DriverStatus.Status.ASYNC));
|
|
|
+ updateStatus(nowSupplier.getAsLong() - startTime, iter, DriverStatus.Status.ASYNC);
|
|
|
return fut;
|
|
|
}
|
|
|
+ if (isFinished()) {
|
|
|
+ finishNanos = nowSupplier.getAsLong();
|
|
|
+ updateStatus(finishNanos - startTime, iter, DriverStatus.Status.DONE);
|
|
|
+ driverContext.finish();
|
|
|
+ Releasables.close(releasable, driverContext.getSnapshot());
|
|
|
+ return Operator.NOT_BLOCKED;
|
|
|
+ }
|
|
|
+ long now = nowSupplier.getAsLong();
|
|
|
if (iter >= maxIterations) {
|
|
|
- break;
|
|
|
+ updateStatus(now - startTime, iter, DriverStatus.Status.WAITING);
|
|
|
+ return Operator.NOT_BLOCKED;
|
|
|
+ }
|
|
|
+ if (now - startTime >= maxTimeNanos) {
|
|
|
+ updateStatus(now - startTime, iter, DriverStatus.Status.WAITING);
|
|
|
+ return Operator.NOT_BLOCKED;
|
|
|
}
|
|
|
- long now = System.nanoTime();
|
|
|
if (now > nextStatus) {
|
|
|
- status.set(updateStatus(DriverStatus.Status.RUNNING));
|
|
|
+ updateStatus(now - startTime, iter, DriverStatus.Status.RUNNING);
|
|
|
nextStatus = now + statusNanos;
|
|
|
}
|
|
|
- iter++;
|
|
|
- if (now - startTime > maxTimeNanos) {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (isFinished()) {
|
|
|
- status.set(updateStatus(DriverStatus.Status.DONE));
|
|
|
- driverContext.finish();
|
|
|
- Releasables.close(releasable, driverContext.getSnapshot());
|
|
|
- } else {
|
|
|
- status.set(updateStatus(DriverStatus.Status.WAITING));
|
|
|
}
|
|
|
- return Operator.NOT_BLOCKED;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -180,6 +220,7 @@ public class Driver implements Releasable, Describable {
|
|
|
* Abort the driver and wait for it to finish
|
|
|
*/
|
|
|
public void abort(Exception reason, ActionListener<Void> listener) {
|
|
|
+ finishNanos = System.nanoTime();
|
|
|
completionListener.addListener(listener);
|
|
|
if (started.compareAndSet(false, true)) {
|
|
|
drainAndCloseOperators(reason);
|
|
@@ -286,7 +327,7 @@ public class Driver implements Releasable, Describable {
|
|
|
) {
|
|
|
driver.completionListener.addListener(listener);
|
|
|
if (driver.started.compareAndSet(false, true)) {
|
|
|
- driver.status.set(driver.updateStatus(DriverStatus.Status.STARTING));
|
|
|
+ driver.updateStatus(0, 0, DriverStatus.Status.STARTING);
|
|
|
schedule(DEFAULT_TIME_BEFORE_YIELDING, maxIterations, threadContext, executor, driver, driver.completionListener);
|
|
|
}
|
|
|
}
|
|
@@ -324,7 +365,7 @@ public class Driver implements Releasable, Describable {
|
|
|
onComplete(listener);
|
|
|
return;
|
|
|
}
|
|
|
- SubscribableListener<Void> fut = driver.run(maxTime, maxIterations);
|
|
|
+ SubscribableListener<Void> fut = driver.run(maxTime, maxIterations, System::nanoTime);
|
|
|
if (fut.isDone()) {
|
|
|
schedule(maxTime, maxIterations, threadContext, executor, driver, listener);
|
|
|
} else {
|
|
@@ -384,23 +425,42 @@ public class Driver implements Releasable, Describable {
|
|
|
/**
|
|
|
* Get the last status update from the driver. These updates are made
|
|
|
* when the driver is queued and after every
|
|
|
- * processing {@link #run(TimeValue, int) batch}.
|
|
|
+ * processing {@link #run batch}.
|
|
|
*/
|
|
|
public DriverStatus status() {
|
|
|
return status.get();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Build a "profile" of this driver's operations after it's been completed.
|
|
|
+ * This doesn't make sense to call before the driver is done.
|
|
|
+ */
|
|
|
+ public DriverProfile profile() {
|
|
|
+ DriverStatus status = status();
|
|
|
+ if (status.status() != DriverStatus.Status.DONE) {
|
|
|
+ throw new IllegalStateException("can only get profile from finished driver");
|
|
|
+ }
|
|
|
+ return new DriverProfile(finishNanos - startNanos, status.cpuNanos(), status.iterations(), status.completedOperators());
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Update the status.
|
|
|
+ * @param extraCpuNanos how many cpu nanoseconds to add to the previous status
|
|
|
+ * @param extraIterations how many iterations to add to the previous status
|
|
|
* @param status the status of the overall driver request
|
|
|
*/
|
|
|
- private DriverStatus updateStatus(DriverStatus.Status status) {
|
|
|
- return new DriverStatus(
|
|
|
- sessionId,
|
|
|
- System.currentTimeMillis(),
|
|
|
- status,
|
|
|
- statusOfCompletedOperators,
|
|
|
- activeOperators.stream().map(op -> new DriverStatus.OperatorStatus(op.toString(), op.status())).toList()
|
|
|
- );
|
|
|
+ private void updateStatus(long extraCpuNanos, int extraIterations, DriverStatus.Status status) {
|
|
|
+ this.status.getAndUpdate(prev -> {
|
|
|
+ return new DriverStatus(
|
|
|
+ sessionId,
|
|
|
+ startTime,
|
|
|
+ System.currentTimeMillis(),
|
|
|
+ prev.cpuNanos() + extraCpuNanos,
|
|
|
+ prev.iterations() + extraIterations,
|
|
|
+ status,
|
|
|
+ statusOfCompletedOperators,
|
|
|
+ activeOperators.stream().map(op -> new DriverStatus.OperatorStatus(op.toString(), op.status())).toList()
|
|
|
+ );
|
|
|
+ });
|
|
|
}
|
|
|
}
|