• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

uber / cadence-java-client / 1747

pending completion
1747

push

buildkite

GitHub
Addition of license (#795)

11113 of 18396 relevant lines covered (60.41%)

0.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.17
/src/main/java/com/uber/cadence/internal/sync/DeterministicRunnerImpl.java
1
/*
2
 *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
 *
4
 *  Modifications copyright (C) 2017 Uber Technologies, Inc.
5
 *
6
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not
7
 *  use this file except in compliance with the License. A copy of the License is
8
 *  located at
9
 *
10
 *  http://aws.amazon.com/apache2.0
11
 *
12
 *  or in the "license" file accompanying this file. This file is distributed on
13
 *  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14
 *  express or implied. See the License for the specific language governing
15
 *  permissions and limitations under the License.
16
 */
17

18
package com.uber.cadence.internal.sync;
19

20
import com.uber.cadence.SearchAttributes;
21
import com.uber.cadence.WorkflowExecution;
22
import com.uber.cadence.WorkflowType;
23
import com.uber.cadence.context.ContextPropagator;
24
import com.uber.cadence.converter.DataConverter;
25
import com.uber.cadence.converter.JsonDataConverter;
26
import com.uber.cadence.internal.common.CheckedExceptionWrapper;
27
import com.uber.cadence.internal.context.ContextThreadLocal;
28
import com.uber.cadence.internal.metrics.NoopScope;
29
import com.uber.cadence.internal.replay.ContinueAsNewWorkflowExecutionParameters;
30
import com.uber.cadence.internal.replay.DeciderCache;
31
import com.uber.cadence.internal.replay.DecisionContext;
32
import com.uber.cadence.internal.replay.ExecuteActivityParameters;
33
import com.uber.cadence.internal.replay.ExecuteLocalActivityParameters;
34
import com.uber.cadence.internal.replay.SignalExternalWorkflowParameters;
35
import com.uber.cadence.internal.replay.StartChildWorkflowExecutionParameters;
36
import com.uber.cadence.workflow.Functions.Func;
37
import com.uber.cadence.workflow.Functions.Func1;
38
import com.uber.cadence.workflow.Promise;
39
import com.uber.m3.tally.Scope;
40
import java.time.Duration;
41
import java.util.ArrayDeque;
42
import java.util.ArrayList;
43
import java.util.Collections;
44
import java.util.Deque;
45
import java.util.HashMap;
46
import java.util.HashSet;
47
import java.util.Iterator;
48
import java.util.List;
49
import java.util.Map;
50
import java.util.Optional;
51
import java.util.Random;
52
import java.util.Set;
53
import java.util.UUID;
54
import java.util.concurrent.ExecutionException;
55
import java.util.concurrent.ExecutorService;
56
import java.util.concurrent.Future;
57
import java.util.concurrent.SynchronousQueue;
58
import java.util.concurrent.ThreadFactory;
59
import java.util.concurrent.ThreadPoolExecutor;
60
import java.util.concurrent.TimeUnit;
61
import java.util.concurrent.locks.Lock;
62
import java.util.concurrent.locks.ReentrantLock;
63
import java.util.function.BiConsumer;
64
import java.util.function.Consumer;
65
import java.util.function.Supplier;
66
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
68

69
/** Throws Error in case of any unexpected condition. It is to fail a decision, not a workflow. */
70
class DeterministicRunnerImpl implements DeterministicRunner {
71

72
  private static class NamedRunnable {
73
    private final String name;
74
    private final Runnable runnable;
75

76
    private NamedRunnable(String name, Runnable runnable) {
1✔
77
      this.name = name;
1✔
78
      this.runnable = runnable;
1✔
79
    }
1✔
80
  }
81

82
  private static final Logger log = LoggerFactory.getLogger(DeterministicRunnerImpl.class);
1✔
83
  static final String WORKFLOW_ROOT_THREAD_NAME = "workflow-root";
84
  private static final ThreadLocal<WorkflowThread> currentThreadThreadLocal = new ThreadLocal<>();
1✔
85

86
  private final Lock lock = new ReentrantLock();
1✔
87
  private final ExecutorService threadPool;
88
  private final SyncDecisionContext decisionContext;
89
  private final Deque<WorkflowThread> threads = new ArrayDeque<>(); // protected by lock
1✔
90
  // Values from RunnerLocalInternal
91
  private final Map<RunnerLocalInternal<?>, Object> runnerLocalMap = new HashMap<>();
1✔
92

93
  private final List<WorkflowThread> threadsToAdd = Collections.synchronizedList(new ArrayList<>());
1✔
94
  private final List<NamedRunnable> toExecuteInWorkflowThread = new ArrayList<>();
1✔
95
  private final Supplier<Long> clock;
96
  private DeciderCache cache;
97
  private boolean inRunUntilAllBlocked;
98
  private boolean closeRequested;
99
  private boolean closed;
100

101
  static WorkflowThread currentThreadInternal() {
102
    WorkflowThread result = currentThreadThreadLocal.get();
1✔
103
    if (result == null) {
1✔
104
      throw new Error("Called from non workflow or workflow callback thread");
×
105
    }
106
    return result;
1✔
107
  }
108

109
  static void setCurrentThreadInternal(WorkflowThread coroutine) {
110
    currentThreadThreadLocal.set(coroutine);
1✔
111
  }
1✔
112

113
  /**
114
   * Time at which any thread that runs under sync can make progress. For example when {@link
115
   * com.uber.cadence.workflow.Workflow#sleep(long)} expires. 0 means no blocked threads.
116
   */
117
  private long nextWakeUpTime;
118
  /**
119
   * Used to check for failedPromises that contain an error, but never where accessed. It is to
120
   * avoid failure swallowing by failedPromises which is very hard to troubleshoot.
121
   */
122
  private Set<Promise> failedPromises = new HashSet<>();
1✔
123

124
  private boolean exitRequested;
125
  private Object exitValue;
126
  private WorkflowThread rootWorkflowThread;
127
  private final CancellationScopeImpl runnerCancellationScope;
128

129
  DeterministicRunnerImpl(Runnable root) {
130
    this(System::currentTimeMillis, root);
1✔
131
  }
1✔
132

133
  DeterministicRunnerImpl(Supplier<Long> clock, Runnable root) {
134
    this(getDefaultThreadPool(), newDummySyncDecisionContext(), clock, root, null);
1✔
135
  }
1✔
136

137
  private static ThreadPoolExecutor getDefaultThreadPool() {
138
    ThreadPoolExecutor result =
1✔
139
        new ThreadPoolExecutor(0, 1000, 1, TimeUnit.SECONDS, new SynchronousQueue<>());
140
    result.setThreadFactory(
1✔
141
        new ThreadFactory() {
1✔
142
          @Override
143
          public Thread newThread(Runnable r) {
144
            return new Thread(r, "deterministic runner thread");
1✔
145
          }
146
        });
147
    return result;
1✔
148
  }
149

150
  DeterministicRunnerImpl(
151
      ExecutorService threadPool,
152
      SyncDecisionContext decisionContext,
153
      Supplier<Long> clock,
154
      Runnable root) {
155
    this(threadPool, decisionContext, clock, root, null);
1✔
156
  }
1✔
157

158
  DeterministicRunnerImpl(
159
      ExecutorService threadPool,
160
      SyncDecisionContext decisionContext,
161
      Supplier<Long> clock,
162
      Runnable root,
163
      DeciderCache cache) {
1✔
164
    this.threadPool = threadPool;
1✔
165
    this.decisionContext =
1✔
166
        decisionContext != null ? decisionContext : newDummySyncDecisionContext();
1✔
167
    this.clock = clock;
1✔
168
    this.cache = cache;
1✔
169
    runnerCancellationScope = new CancellationScopeImpl(true, null, null);
1✔
170

171
    // TODO: workflow instance specific thread name
172
    rootWorkflowThread =
1✔
173
        new WorkflowThreadImpl(
174
            true,
175
            threadPool,
176
            this,
177
            WORKFLOW_ROOT_THREAD_NAME,
178
            false,
179
            runnerCancellationScope,
180
            root,
181
            cache,
182
            getContextPropagators(),
1✔
183
            getPropagatedContexts());
1✔
184
    threads.addLast(rootWorkflowThread);
1✔
185
    rootWorkflowThread.start();
1✔
186
  }
1✔
187

188
  private static SyncDecisionContext newDummySyncDecisionContext() {
189
    return new SyncDecisionContext(
1✔
190
        new DummyDecisionContext(),
191
        JsonDataConverter.getInstance(),
1✔
192
        null,
193
        (next) -> next,
1✔
194
        null,
195
        null);
196
  }
197

198
  SyncDecisionContext getDecisionContext() {
199
    return decisionContext;
1✔
200
  }
201

202
  @Override
203
  public void runUntilAllBlocked() throws Throwable {
204
    lock.lock();
1✔
205
    try {
206
      checkClosed();
1✔
207

208
      inRunUntilAllBlocked = true;
1✔
209
      Throwable unhandledException = null;
1✔
210
      // Keep repeating until at least one of the threads makes progress.
211
      boolean progress;
212
      outerLoop:
213
      do {
214
        threadsToAdd.clear();
1✔
215

216
        if (!toExecuteInWorkflowThread.isEmpty()) {
1✔
217
          List<WorkflowThread> callbackThreads = new ArrayList<>(toExecuteInWorkflowThread.size());
1✔
218
          for (NamedRunnable nr : toExecuteInWorkflowThread) {
1✔
219
            WorkflowThread thread =
1✔
220
                new WorkflowThreadImpl(
221
                    false,
222
                    threadPool,
223
                    this,
224
                    nr.name,
1✔
225
                    false,
226
                    runnerCancellationScope,
227
                    nr.runnable,
1✔
228
                    cache,
229
                    getContextPropagators(),
1✔
230
                    getPropagatedContexts());
1✔
231
            callbackThreads.add(thread);
1✔
232
          }
1✔
233

234
          // It is important to prepend threads as there are callbacks
235
          // like signals that have to run before any other threads.
236
          // Otherwise signal might be never processed if it was received
237
          // after workflow decided to close.
238
          // Adding the callbacks in the same order as they appear in history.
239

240
          for (int i = callbackThreads.size() - 1; i >= 0; i--) {
1✔
241
            threads.addFirst(callbackThreads.get(i));
1✔
242
          }
243
        }
244

245
        toExecuteInWorkflowThread.clear();
1✔
246
        progress = false;
1✔
247
        Iterator<WorkflowThread> ci = threads.iterator();
1✔
248
        nextWakeUpTime = Long.MAX_VALUE;
1✔
249
        while (ci.hasNext()) {
1✔
250
          WorkflowThread c = ci.next();
1✔
251
          progress = c.runUntilBlocked() || progress;
1✔
252
          if (exitRequested) {
1✔
253
            close();
1✔
254
            break outerLoop;
1✔
255
          }
256
          if (c.isDone()) {
1✔
257
            ci.remove();
1✔
258
            if (c.getUnhandledException() != null) {
1✔
259
              unhandledException = c.getUnhandledException();
1✔
260
              break;
1✔
261
            }
262
          } else {
263
            long t = c.getBlockedUntil();
1✔
264
            if (t > currentTimeMillis() && t < nextWakeUpTime) {
1✔
265
              nextWakeUpTime = t;
1✔
266
            }
267
          }
268
        }
1✔
269
        if (unhandledException != null) {
1✔
270
          close();
1✔
271
          throw unhandledException;
1✔
272
        }
273
        for (WorkflowThread c : threadsToAdd) {
1✔
274
          threads.addLast(c);
1✔
275
        }
1✔
276
      } while (progress && !threads.isEmpty());
1✔
277

278
      if (nextWakeUpTime < currentTimeMillis() || nextWakeUpTime == Long.MAX_VALUE) {
1✔
279
        nextWakeUpTime = 0;
1✔
280
      }
281

282
    } finally {
283
      inRunUntilAllBlocked = false;
1✔
284
      // Close was requested while running
285
      if (closeRequested) {
1✔
286
        close();
1✔
287
      }
288
      lock.unlock();
1✔
289
    }
290
  }
1✔
291

292
  @Override
293
  public boolean isDone() {
294
    lock.lock();
1✔
295
    try {
296
      return closed || threads.isEmpty();
1✔
297
    } finally {
298
      lock.unlock();
1✔
299
    }
300
  }
301

302
  @Override
303
  @SuppressWarnings("unchecked")
304
  public Object getExitValue() {
305
    lock.lock();
1✔
306
    try {
307
      if (!closed) {
1✔
308
        throw new Error("not done");
×
309
      }
310
    } finally {
311
      lock.unlock();
1✔
312
    }
313
    return exitValue;
1✔
314
  }
315

316
  @Override
317
  public void cancel(String reason) {
318
    executeInWorkflowThread("cancel workflow callback", () -> rootWorkflowThread.cancel(reason));
1✔
319
  }
1✔
320

321
  @Override
322
  public void close() {
323
    List<Future<?>> threadFutures = new ArrayList<>();
1✔
324
    lock.lock();
1✔
325
    if (closed) {
1✔
326
      lock.unlock();
1✔
327
      return;
1✔
328
    }
329
    // Do not close while runUntilAllBlocked executes.
330
    // closeRequested tells it to call close() at the end.
331
    closeRequested = true;
1✔
332
    if (inRunUntilAllBlocked) {
1✔
333
      lock.unlock();
1✔
334
      return;
1✔
335
    }
336
    try {
337
      for (WorkflowThread c : threadsToAdd) {
1✔
338
        threads.addLast(c);
1✔
339
      }
1✔
340
      threadsToAdd.clear();
1✔
341

342
      for (WorkflowThread c : threads) {
1✔
343
        threadFutures.add(c.stopNow());
1✔
344
      }
1✔
345
      threads.clear();
1✔
346

347
      // We cannot use an iterator to unregister failed Promises since f.get()
348
      // will remove the promise directly from failedPromises. This causes an
349
      // ConcurrentModificationException
350
      // For this reason we will loop over a copy of failedPromises.
351
      Set<Promise> failedPromisesLoop = new HashSet<>(failedPromises);
1✔
352
      for (Promise f : failedPromisesLoop) {
1✔
353
        if (!f.isCompleted()) {
1✔
354
          throw new Error("expected failed");
×
355
        }
356
        try {
357
          f.get();
×
358
          throw new Error("unreachable");
×
359
        } catch (RuntimeException e) {
1✔
360
          log.warn(
1✔
361
              "Promise that was completedExceptionally was never accessed. "
362
                  + "The ignored exception:",
363
              CheckedExceptionWrapper.unwrap(e));
1✔
364
        }
365
      }
1✔
366
    } finally {
367
      closed = true;
1✔
368
      lock.unlock();
1✔
369
    }
370

371
    // Context is destroyed in c.StopNow(). Wait on all tasks outside the lock since
372
    // these tasks use the same lock to execute.
373
    for (Future<?> future : threadFutures) {
1✔
374
      try {
375
        future.get();
1✔
376
      } catch (InterruptedException e) {
×
377
        throw new Error("Unexpected interrupt", e);
×
378
      } catch (ExecutionException e) {
×
379
        throw new Error("Unexpected failure stopping coroutine", e);
×
380
      }
1✔
381
    }
1✔
382
  }
1✔
383

384
  @Override
385
  public String stackTrace() {
386
    StringBuilder result = new StringBuilder();
1✔
387
    lock.lock();
1✔
388
    try {
389
      for (WorkflowThread coroutine : threads) {
1✔
390
        if (result.length() > 0) {
1✔
391
          result.append("\n");
1✔
392
        }
393
        coroutine.addStackTrace(result);
1✔
394
      }
1✔
395
    } finally {
396
      lock.unlock();
1✔
397
    }
398
    return result.toString();
1✔
399
  }
400

401
  private void checkClosed() {
402
    if (closed) {
1✔
403
      throw new Error("closed");
×
404
    }
405
  }
1✔
406

407
  @Override
408
  public long currentTimeMillis() {
409
    return clock.get();
1✔
410
  }
411

412
  @Override
413
  public long getNextWakeUpTime() {
414
    lock.lock();
1✔
415
    try {
416
      checkClosed();
1✔
417
      if (decisionContext != null) {
1✔
418
        long nextFireTime = decisionContext.getNextFireTime();
1✔
419
        if (nextWakeUpTime == 0) {
1✔
420
          return nextFireTime;
1✔
421
        }
422
        if (nextFireTime == 0) {
1✔
423
          return nextWakeUpTime;
1✔
424
        }
425
        return Math.min(nextWakeUpTime, nextFireTime);
×
426
      }
427
      return nextWakeUpTime;
×
428
    } finally {
429
      lock.unlock();
1✔
430
    }
431
  }
432

433
  WorkflowThread newThread(Runnable runnable, boolean detached, String name) {
434
    checkWorkflowThreadOnly();
1✔
435
    checkClosed();
1✔
436
    WorkflowThread result =
1✔
437
        new WorkflowThreadImpl(
438
            false,
439
            threadPool,
440
            this,
441
            name,
442
            detached,
443
            CancellationScopeImpl.current(),
1✔
444
            runnable,
445
            cache,
446
            getContextPropagators(),
1✔
447
            getPropagatedContexts());
1✔
448
    threadsToAdd.add(result); // This is synchronized collection.
1✔
449
    return result;
1✔
450
  }
451

452
  /**
453
   * Executes before any other threads next time runUntilBlockedCalled. Must never be called from
454
   * any workflow threads.
455
   */
456
  @Override
457
  public void executeInWorkflowThread(String name, Runnable runnable) {
458
    lock.lock();
1✔
459
    try {
460
      checkClosed();
1✔
461
      toExecuteInWorkflowThread.add(new NamedRunnable(name, runnable));
1✔
462
    } finally {
463
      lock.unlock();
1✔
464
    }
465
  }
1✔
466

467
  Lock getLock() {
468
    return lock;
1✔
469
  }
470

471
  /** Register a promise that had failed but wasn't accessed yet. */
472
  void registerFailedPromise(Promise promise) {
473
    failedPromises.add(promise);
1✔
474
  }
1✔
475

476
  /** Forget a failed promise as it was accessed. */
477
  void forgetFailedPromise(Promise promise) {
478
    failedPromises.remove(promise);
1✔
479
  }
1✔
480

481
  <R> void exit(R value) {
482
    checkClosed();
1✔
483
    checkWorkflowThreadOnly();
1✔
484
    this.exitValue = value;
1✔
485
    this.exitRequested = true;
1✔
486
  }
1✔
487

488
  private void checkWorkflowThreadOnly() {
489
    if (!inRunUntilAllBlocked) {
1✔
490
      throw new Error("called from non workflow thread");
×
491
    }
492
  }
1✔
493

494
  @SuppressWarnings("unchecked")
495
  <T> Optional<T> getRunnerLocal(RunnerLocalInternal<T> key) {
496
    if (!runnerLocalMap.containsKey(key)) {
1✔
497
      return Optional.empty();
1✔
498
    }
499
    return Optional.of((T) runnerLocalMap.get(key));
1✔
500
  }
501

502
  <T> void setRunnerLocal(RunnerLocalInternal<T> key, T value) {
503
    runnerLocalMap.put(key, value);
1✔
504
  }
1✔
505

506
  /**
507
   * If we're executing as part of a workflow, get the current thread's context. Otherwise get the
508
   * context info from the DecisionContext
509
   */
510
  private Map<String, Object> getPropagatedContexts() {
511
    if (currentThreadThreadLocal.get() != null) {
1✔
512
      return ContextThreadLocal.getCurrentContextForPropagation();
1✔
513
    } else {
514
      return decisionContext.getContext().getPropagatedContexts();
1✔
515
    }
516
  }
517

518
  private List<ContextPropagator> getContextPropagators() {
519
    if (currentThreadThreadLocal.get() != null) {
1✔
520
      return ContextThreadLocal.getContextPropagators();
1✔
521
    } else {
522
      return decisionContext.getContext().getContextPropagators();
1✔
523
    }
524
  }
525

526
  private static final class DummyDecisionContext implements DecisionContext {
527

528
    @Override
529
    public WorkflowExecution getWorkflowExecution() {
530
      throw new UnsupportedOperationException("not implemented");
×
531
    }
532

533
    @Override
534
    public WorkflowExecution getParentWorkflowExecution() {
535
      throw new UnsupportedOperationException("not implemented");
×
536
    }
537

538
    @Override
539
    public WorkflowType getWorkflowType() {
540
      return new WorkflowType().setName("dummy-workflow");
1✔
541
    }
542

543
    @Override
544
    public boolean isCancelRequested() {
545
      throw new UnsupportedOperationException("not implemented");
×
546
    }
547

548
    @Override
549
    public ContinueAsNewWorkflowExecutionParameters getContinueAsNewOnCompletion() {
550
      throw new UnsupportedOperationException("not implemented");
×
551
    }
552

553
    @Override
554
    public void setContinueAsNewOnCompletion(
555
        ContinueAsNewWorkflowExecutionParameters continueParameters) {
556
      throw new UnsupportedOperationException("not implemented");
×
557
    }
558

559
    @Override
560
    public int getExecutionStartToCloseTimeoutSeconds() {
561
      throw new UnsupportedOperationException("not implemented");
×
562
    }
563

564
    @Override
565
    public String getTaskList() {
566
      return "dummy-task-list";
1✔
567
    }
568

569
    @Override
570
    public String getDomain() {
571
      return "dummy-domain";
1✔
572
    }
573

574
    @Override
575
    public String getWorkflowId() {
576
      return "dummy-workflow-id";
1✔
577
    }
578

579
    @Override
580
    public String getRunId() {
581
      return "dummy-run-id";
1✔
582
    }
583

584
    @Override
585
    public Duration getExecutionStartToCloseTimeout() {
586
      throw new UnsupportedOperationException("not implemented");
×
587
    }
588

589
    @Override
590
    public Duration getDecisionTaskTimeout() {
591
      throw new UnsupportedOperationException("not implemented");
×
592
    }
593

594
    @Override
595
    public SearchAttributes getSearchAttributes() {
596
      throw new UnsupportedOperationException("not implemented");
×
597
    }
598

599
    @Override
600
    public Map<String, Object> getPropagatedContexts() {
601
      return null;
1✔
602
    }
603

604
    @Override
605
    public List<ContextPropagator> getContextPropagators() {
606
      return null;
1✔
607
    }
608

609
    @Override
610
    public Consumer<Exception> scheduleActivityTask(
611
        ExecuteActivityParameters parameters, BiConsumer<byte[], Exception> callback) {
612
      throw new UnsupportedOperationException("not implemented");
×
613
    }
614

615
    @Override
616
    public Consumer<Exception> scheduleLocalActivityTask(
617
        ExecuteLocalActivityParameters parameters, BiConsumer<byte[], Exception> callback) {
618
      throw new UnsupportedOperationException("not implemented");
×
619
    }
620

621
    @Override
622
    public Consumer<Exception> startChildWorkflow(
623
        StartChildWorkflowExecutionParameters parameters,
624
        Consumer<WorkflowExecution> executionCallback,
625
        BiConsumer<byte[], Exception> callback) {
626
      throw new UnsupportedOperationException("not implemented");
×
627
    }
628

629
    @Override
630
    public boolean isServerSideChildWorkflowRetry() {
631
      throw new UnsupportedOperationException("not implemented");
×
632
    }
633

634
    @Override
635
    public boolean isServerSideActivityRetry() {
636
      throw new UnsupportedOperationException("not implemented");
×
637
    }
638

639
    @Override
640
    public Consumer<Exception> signalWorkflowExecution(
641
        SignalExternalWorkflowParameters signalParameters, BiConsumer<Void, Exception> callback) {
642
      throw new UnsupportedOperationException("not implemented");
×
643
    }
644

645
    @Override
646
    public Promise<Void> requestCancelWorkflowExecution(WorkflowExecution execution) {
647
      throw new UnsupportedOperationException("not implemented");
×
648
    }
649

650
    @Override
651
    public void continueAsNewOnCompletion(ContinueAsNewWorkflowExecutionParameters parameters) {
652
      throw new UnsupportedOperationException("not implemented");
×
653
    }
654

655
    @Override
656
    public Optional<byte[]> mutableSideEffect(
657
        String id, DataConverter converter, Func1<Optional<byte[]>, Optional<byte[]>> func) {
658
      return func.apply(Optional.empty());
1✔
659
    }
660

661
    @Override
662
    public long currentTimeMillis() {
663
      throw new UnsupportedOperationException("not implemented");
×
664
    }
665

666
    @Override
667
    public boolean isReplaying() {
668
      throw new UnsupportedOperationException("not implemented");
×
669
    }
670

671
    @Override
672
    public Consumer<Exception> createTimer(long delaySeconds, Consumer<Exception> callback) {
673
      throw new UnsupportedOperationException("not implemented");
×
674
    }
675

676
    @Override
677
    public byte[] sideEffect(Func<byte[]> func) {
678
      throw new UnsupportedOperationException("not implemented");
×
679
    }
680

681
    @Override
682
    public int getVersion(
683
        String changeID, DataConverter converter, int minSupported, int maxSupported) {
684
      throw new UnsupportedOperationException("not implemented");
×
685
    }
686

687
    @Override
688
    public Random newRandom() {
689
      throw new UnsupportedOperationException("not implemented");
×
690
    }
691

692
    @Override
693
    public Scope getMetricsScope() {
694
      return NoopScope.getInstance();
1✔
695
    }
696

697
    @Override
698
    public boolean getEnableLoggingInReplay() {
699
      return false;
×
700
    }
701

702
    @Override
703
    public UUID randomUUID() {
704
      return UUID.randomUUID();
1✔
705
    }
706

707
    @Override
708
    public void upsertSearchAttributes(SearchAttributes searchAttributes) {
709
      throw new UnsupportedOperationException("not implemented");
×
710
    }
711
  }
712
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc