Skip to content

Commit cef37ac

Browse files
add updatable timer sample
1 parent befdaaa commit cef37ac

7 files changed

Lines changed: 184 additions & 0 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
3+
require 'test'
4+
require 'updatable_timer/updatable_timer_workflow'
5+
require 'securerandom'
6+
require 'temporalio/client'
7+
require 'temporalio/testing'
8+
require 'temporalio/worker'
9+
10+
module UpdatableTimer
11+
class UpdatableTimerWorkflowTest < Test
12+
def test_workflow
13+
Temporalio::Testing::WorkflowEnvironment.start_time_skipping do |env|
14+
# Run workflow in a worker
15+
worker = Temporalio::Worker.new(
16+
client: env.client,
17+
task_queue: "tq-#{SecureRandom.uuid}",
18+
workflows: [UpdatableTimerWorkflow]
19+
)
20+
worker.run do
21+
day_from_now = (Time.now(in: 'utc') + (24 * 60 * 60)).to_r
22+
hour_from_now = (Time.now(in: 'utc') + (60 * 60)).to_r
23+
handle = env.client.start_workflow(
24+
UpdatableTimerWorkflow, day_from_now,
25+
id: "wf-#{SecureRandom.uuid}", task_queue: worker.task_queue
26+
)
27+
assert_equal day_from_now, Rational(handle.query(UpdatableTimerWorkflow.wake_up_time))
28+
handle.signal(UpdatableTimerWorkflow.update_wake_up_time, hour_from_now)
29+
assert_equal hour_from_now, Rational(handle.query(UpdatableTimerWorkflow.wake_up_time))
30+
31+
handle.result
32+
end
33+
end
34+
end
35+
end
36+
end

updatable_timer/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Updatable Timer Sample
2+
3+
Demonstrates a helper class which relies on `Temporalio::Workflow.wait_condition` to implement a blocking sleep that can be updated at any moment.
4+
5+
To run, first see [README.md](../README.md) for prerequisites. Then, in another terminal, start the Ruby worker from this directory:
6+
7+
```bash
8+
bundle exec ruby worker.rb
9+
```
10+
11+
Then in another terminal, use the Ruby client to the workflow from this directory:
12+
13+
```bash
14+
bundle exec ruby starter.rb
15+
```
16+
17+
The Ruby code will invoke the workflow which will create a timer that will resolve in a day.
18+
19+
Finally run the updater to change the timer to 10 seconds from now:
20+
21+
```bash
22+
bundle exec ruby wake_up_timer_updater.rb
23+
```
24+
25+
There is also a [test](../test/updatable_timer/updatable_timer_workflow_test.rb) that demonstrates querying the wake up time.

updatable_timer/starter.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
require 'temporalio/client'
4+
require_relative 'updatable_timer_workflow'
5+
6+
# Create a Temporal client
7+
logger = Logger.new($stdout, level: Logger::INFO)
8+
client = Temporalio::Client.connect('localhost:7233', 'default', logger:)
9+
10+
# Run workflow
11+
logger.info('Starting timer')
12+
client.execute_workflow(
13+
UpdatableTimer::UpdatableTimerWorkflow, (Time.now(in: 'utc') + (24 * 60 * 60)).to_r,
14+
id: 'updatable-timer-sample-workflow-id', task_queue: 'updatable-timer'
15+
)
16+
logger.info('Timer complete')

updatable_timer/updatable_timer.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
require 'temporalio/workflow'
4+
require 'temporalio/error'
5+
6+
module UpdatableTimer
7+
class UpdatableTimer
8+
def initialize(wake_up_time)
9+
@wake_up_time = wake_up_time
10+
@wake_up_time_updated = false
11+
end
12+
13+
attr_reader :wake_up_time
14+
15+
def wake_up_time=(wake_up_time)
16+
Temporalio::Workflow.logger.info("update_wake_up_time: #{wake_up_time}")
17+
@wake_up_time = wake_up_time
18+
@wake_up_time_updated = true
19+
end
20+
21+
def sleep
22+
Temporalio::Workflow.logger.info("sleep until: #{@wake_up_time}")
23+
loop do
24+
now = Temporalio::Workflow.now
25+
sleep_interval = @wake_up_time - now
26+
27+
break if sleep_interval.negative?
28+
29+
Temporalio::Workflow.logger.info("going to sleep for: #{sleep_interval}")
30+
31+
begin
32+
@wake_up_time_updated = false
33+
Temporalio::Workflow.timeout(sleep_interval) do
34+
Temporalio::Workflow.wait_condition { @wake_up_time_updated }
35+
end
36+
rescue Timeout::Error
37+
next
38+
end
39+
end
40+
Temporalio::Workflow.logger.info('sleep_until completed')
41+
end
42+
end
43+
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
require 'temporalio/workflow'
4+
require_relative 'updatable_timer'
5+
6+
module UpdatableTimer
7+
class UpdatableTimerWorkflow < Temporalio::Workflow::Definition
8+
workflow_arg_hint Time
9+
10+
def execute(wake_up_time)
11+
@timer = UpdatableTimer.new(Time.at(Rational(wake_up_time)))
12+
@timer.sleep
13+
end
14+
15+
workflow_query
16+
def wake_up_time
17+
Temporalio::Workflow.logger.info('get_wake_up_time')
18+
@timer.wake_up_time.to_r
19+
end
20+
21+
workflow_signal
22+
def update_wake_up_time(wake_up_time)
23+
wake_up_time = Time.at(Rational(wake_up_time))
24+
Temporalio::Workflow.logger.info("update_wake_up_time: #{wake_up_time}")
25+
@timer.wake_up_time = wake_up_time
26+
end
27+
end
28+
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
require 'temporalio/client'
4+
require_relative 'updatable_timer_workflow'
5+
6+
# Create a Temporal client
7+
logger = Logger.new($stdout, level: Logger::INFO)
8+
client = Temporalio::Client.connect('localhost:7233', 'default', logger:)
9+
handle = client.workflow_handle('updatable-timer-sample-workflow-id')
10+
11+
handle.signal(UpdatableTimer::UpdatableTimerWorkflow.update_wake_up_time, (Time.now(in: 'utc') + 10).to_r)
12+
logger.info('Updated wake up time to 10 seconds from now')

updatable_timer/worker.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
require_relative 'updatable_timer_workflow'
4+
require 'logger'
5+
require 'temporalio/client'
6+
require 'temporalio/worker'
7+
8+
# Create a Temporal client
9+
client = Temporalio::Client.connect(
10+
'localhost:7233',
11+
'default',
12+
logger: Logger.new($stdout, level: Logger::INFO)
13+
)
14+
15+
# Create worker with the activities and workflow
16+
worker = Temporalio::Worker.new(
17+
client:,
18+
task_queue: 'updatable-timer',
19+
workflows: [UpdatableTimer::UpdatableTimerWorkflow]
20+
)
21+
22+
# Run the worker until SIGINT
23+
puts 'Starting worker (ctrl+c to exit)'
24+
worker.run(shutdown_signals: ['SIGINT'])

0 commit comments

Comments
 (0)