|
| 1 | +require 'activities/echo_activity' |
| 2 | +require 'activities/long_running_activity' |
| 3 | + |
| 4 | +# This example workflow exercises all three conditions that can change state that is being |
| 5 | +# awaited upon: activity completion, sleep completion, signal receieved. |
| 6 | +class WaitForWorkflow < Temporal::Workflow |
| 7 | + def execute(total_echos, max_echos_at_once, expected_signal) |
| 8 | + signals_received = {} |
| 9 | + |
| 10 | + workflow.on_signal do |signal, input| |
| 11 | + signals_received[signal] = input |
| 12 | + end |
| 13 | + |
| 14 | + workflow.wait_for do |
| 15 | + workflow.logger.info("Awaiting #{expected_signal}, signals received so far: #{signals_received}") |
| 16 | + signals_received.key?(expected_signal) |
| 17 | + end |
| 18 | + |
| 19 | + # Run an activity but with a max time limit by starting a timer. This activity |
| 20 | + # will not complete before the timer, which may result in a failed activity task after the |
| 21 | + # workflow is completed. |
| 22 | + long_running_future = LongRunningActivity.execute(15, 0.1) |
| 23 | + timeout_timer = workflow.start_timer(1) |
| 24 | + workflow.wait_for(timeout_timer, long_running_future) |
| 25 | + |
| 26 | + timer_beat_activity = timeout_timer.finished? && !long_running_future.finished? |
| 27 | + |
| 28 | + # This should not wait further. The first future has already finished, and therefore |
| 29 | + # the second one should not be awaited upon. |
| 30 | + long_timeout_timer = workflow.start_timer(15) |
| 31 | + workflow.wait_for(timeout_timer, long_timeout_timer) |
| 32 | + raise 'The workflow should not have waited for this timer to complete' if long_timeout_timer.finished? |
| 33 | + |
| 34 | + block_called = false |
| 35 | + workflow.wait_for(timeout_timer) do |
| 36 | + # This should never be called because the timeout_timer future was already |
| 37 | + # finished before the wait was even called. |
| 38 | + block_called = true |
| 39 | + end |
| 40 | + raise 'Block should not have been called' if block_called |
| 41 | + |
| 42 | + workflow.wait_for(long_timeout_timer) do |
| 43 | + # This condition will immediately be true and not result in any waiting or dispatching |
| 44 | + true |
| 45 | + end |
| 46 | + raise 'The workflow should not have waited for this timer to complete' if long_timeout_timer.finished? |
| 47 | + |
| 48 | + activity_futures = {} |
| 49 | + echos_completed = 0 |
| 50 | + |
| 51 | + total_echos.times do |i| |
| 52 | + workflow.wait_for do |
| 53 | + workflow.logger.info("Activities in flight #{activity_futures.length}") |
| 54 | + # Pause workflow until the number of active activity futures is less than 2. This |
| 55 | + # will throttle new activities from being started, guaranteeing that only two of these |
| 56 | + # activities are running at once. |
| 57 | + activity_futures.length < max_echos_at_once |
| 58 | + end |
| 59 | + |
| 60 | + future = EchoActivity.execute("hi #{i}") |
| 61 | + activity_futures[i] = future |
| 62 | + |
| 63 | + future.done do |
| 64 | + activity_futures.delete(i) |
| 65 | + echos_completed += 1 |
| 66 | + end |
| 67 | + end |
| 68 | + |
| 69 | + workflow.wait_for do |
| 70 | + workflow.logger.info("Waiting for queue to drain, size: #{activity_futures.length}") |
| 71 | + activity_futures.empty? |
| 72 | + end |
| 73 | + |
| 74 | + { |
| 75 | + signal: signals_received.key?(expected_signal), |
| 76 | + timer: timer_beat_activity, |
| 77 | + activity: echos_completed == total_echos |
| 78 | + } |
| 79 | + end |
| 80 | +end |
0 commit comments