From c440f5b7813e113124c92decf9a68030da20838b Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Tue, 4 Feb 2025 11:58:00 -0600 Subject: [PATCH 1/4] Demonstrate interoperability with Coinbase Ruby SDK --- Gemfile | 7 ++- README.md | 2 + coinbase_ruby/README.md | 24 +++++++++ coinbase_ruby/coinbase_activity.rb | 9 ++++ coinbase_ruby/coinbase_workflow.rb | 15 ++++++ coinbase_ruby/starter.rb | 56 +++++++++++++++++++++ coinbase_ruby/temporal_activity.rb | 11 +++++ coinbase_ruby/temporal_workflow.rb | 19 ++++++++ coinbase_ruby/worker.rb | 47 ++++++++++++++++++ test/coinbase_ruby/workflow_test.rb | 75 +++++++++++++++++++++++++++++ 10 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 coinbase_ruby/README.md create mode 100644 coinbase_ruby/coinbase_activity.rb create mode 100644 coinbase_ruby/coinbase_workflow.rb create mode 100644 coinbase_ruby/starter.rb create mode 100644 coinbase_ruby/temporal_activity.rb create mode 100644 coinbase_ruby/temporal_workflow.rb create mode 100644 coinbase_ruby/worker.rb create mode 100644 test/coinbase_ruby/workflow_test.rb diff --git a/Gemfile b/Gemfile index b42ef5e..274b88d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,12 @@ source 'https://rubygems.org' -gem 'temporalio' +# Primary Temporal dependency +gem 'temporalio', path: '../temporal-sdk-ruby/temporalio' + +# Additional dependencies for certain samples +gem 'google-protobuf', '~> 3.25' +gem 'temporal-ruby', path: '../coinbase-temporal-ruby' group :development do gem 'minitest' diff --git a/README.md b/README.md index 94e6b51..a608a85 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ Prerequisites: * [activity_simple](activity_simple) - Simple workflow that calls two activities. * [activity_worker](activity_worker) - Use Ruby activities from a workflow in another language. +* [coinbase_ruby](coinbase_ruby) - Demonstrate interoperability with the + [Coinbase Ruby SDK](https://github.com/coinbase/temporal-ruby). * [context_propagation](context_propagation) - Use interceptors to propagate thread/fiber local data from clients through workflows/activities. * [message_passing_simple](message_passing_simple) - Simple workflow that accepts signals, queries, and updates. diff --git a/coinbase_ruby/README.md b/coinbase_ruby/README.md new file mode 100644 index 0000000..a33325f --- /dev/null +++ b/coinbase_ruby/README.md @@ -0,0 +1,24 @@ +# Coinbase Ruby + +This sample shows a workflow, activity, and client from [Coinbase Ruby SDK](https://github.com/coinbase/temporal-ruby) +able to interoperate with a workflow, activity, and client from Temporal Ruby SDK. Specifically this sample contains an +activity in both SDKs, a workflow in both SDKs each calling both activities, a worker in both SDKs running in the same +process, and a starter with clients from each SDK each invoking both workflows. + +To run, first see [README.md](../README.md) for prerequisites. Then, in another terminal, start the Ruby worker +from this directory: + + bundle exec ruby worker.rb + +Finally in another terminal, use the Ruby client to the workflow from this directory: + + bundle exec ruby starter.rb + +The Ruby code will invoke 4 workflows. The output of the final command should be: + +``` +Coinbase SDK workflow result from Temporal SDK client: ["Hello from Coinbase Ruby SDK, user1!", "Hello from Temporal Ruby SDK, user1!"] +Temporal SDK workflow result from Temporal SDK client: ["Hello from Coinbase Ruby SDK, user2!", "Hello from Temporal Ruby SDK, user2!"] +Coinbase SDK workflow result from Coinbase SDK client: ["Hello from Coinbase Ruby SDK, user3!", "Hello from Temporal Ruby SDK, user3!"] +Temporal SDK workflow result from Coinbase SDK client: ["Hello from Coinbase Ruby SDK, user4!", "Hello from Temporal Ruby SDK, user4!"] +``` \ No newline at end of file diff --git a/coinbase_ruby/coinbase_activity.rb b/coinbase_ruby/coinbase_activity.rb new file mode 100644 index 0000000..9a5cadb --- /dev/null +++ b/coinbase_ruby/coinbase_activity.rb @@ -0,0 +1,9 @@ +require 'temporal-ruby' + +module CoinbaseRuby + class CoinbaseActivity < Temporal::Activity + def execute(name) + "Hello from Coinbase Ruby SDK, #{name}!" + end + end +end diff --git a/coinbase_ruby/coinbase_workflow.rb b/coinbase_ruby/coinbase_workflow.rb new file mode 100644 index 0000000..65145fe --- /dev/null +++ b/coinbase_ruby/coinbase_workflow.rb @@ -0,0 +1,15 @@ +require 'temporal-ruby' +require_relative 'coinbase_activity' + +module CoinbaseRuby + class CoinbaseWorkflow < Temporal::Workflow + def execute(name) + [ + # Execute activity on Coinbase SDK worker + CoinbaseActivity.execute!(name), + # Execute activity on Temporal SDK worker + workflow.execute_activity!(:TemporalActivity, name, options: { task_queue: 'coinbase-ruby-sample-temporal' }) + ] + end + end +end diff --git a/coinbase_ruby/starter.rb b/coinbase_ruby/starter.rb new file mode 100644 index 0000000..9007df2 --- /dev/null +++ b/coinbase_ruby/starter.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# We must require Temporal SDK first and set the env var to prevent Coinbase SDK from trying to load its protos +require 'temporalio/client' +ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] = '1' + +require_relative 'coinbase_workflow' +require_relative 'temporal_workflow' +require 'logger' +require 'temporal-ruby' + +# Create Temporal SDK client +client = Temporalio::Client.connect('localhost:7233', 'default') + +# Run Coinbase workflow +result = client.execute_workflow( + CoinbaseRuby::CoinbaseWorkflow.name, 'user1', + id: 'coinbase-ruby-sample-workflow-id-1', task_queue: 'coinbase-ruby-sample-coinbase' +) +puts "Coinbase SDK workflow result from Temporal SDK client: #{result}" + +# Run Temporal workflow +result = client.execute_workflow( + CoinbaseRuby::TemporalWorkflow, 'user2', + id: 'coinbase-ruby-sample-workflow-id-2', task_queue: 'coinbase-ruby-sample-temporal' +) +puts "Temporal SDK workflow result from Temporal SDK client: #{result}" + +# Now do the same with Coinbase SDK, first configuring the client +Temporal.configure do |config| + config.host = 'localhost' + config.port = 7233 + config.namespace = 'default' +end + +# Run Coinbase workflow +run_id = Temporal.start_workflow( + CoinbaseRuby::CoinbaseWorkflow, 'user3', + options: { workflow_id: 'coinbase-ruby-sample-workflow-id-3', task_queue: 'coinbase-ruby-sample-coinbase' } +) +result = Temporal.await_workflow_result( + CoinbaseRuby::CoinbaseWorkflow, + workflow_id: 'coinbase-ruby-sample-workflow-id-3', run_id: +) +puts "Coinbase SDK workflow result from Coinbase SDK client: #{result}" + +# Run Temporal workflow +run_id = Temporal.start_workflow( + :TemporalWorkflow, 'user4', + options: { workflow_id: 'coinbase-ruby-sample-workflow-id-4', task_queue: 'coinbase-ruby-sample-temporal' } +) +result = Temporal.await_workflow_result( + :TemporalWorkflow, + workflow_id: 'coinbase-ruby-sample-workflow-id-4', run_id: +) +puts "Temporal SDK workflow result from Coinbase SDK client: #{result}" diff --git a/coinbase_ruby/temporal_activity.rb b/coinbase_ruby/temporal_activity.rb new file mode 100644 index 0000000..ab43333 --- /dev/null +++ b/coinbase_ruby/temporal_activity.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'temporalio/activity' + +module CoinbaseRuby + class TemporalActivity < Temporalio::Activity::Definition + def execute(name) + "Hello from Temporal Ruby SDK, #{name}!" + end + end +end diff --git a/coinbase_ruby/temporal_workflow.rb b/coinbase_ruby/temporal_workflow.rb new file mode 100644 index 0000000..40dcde3 --- /dev/null +++ b/coinbase_ruby/temporal_workflow.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'temporalio/workflow' +require_relative 'coinbase_activity' +require_relative 'temporal_activity' + +module CoinbaseRuby + class TemporalWorkflow < Temporalio::Workflow::Definition + def execute(name) + [ + # Execute activity on Coinbase SDK worker + Temporalio::Workflow.execute_activity(CoinbaseActivity.name, name, + start_to_close_timeout: 10, task_queue: 'coinbase-ruby-sample-coinbase'), + # Execute activity on Temporal SDK worker + Temporalio::Workflow.execute_activity(TemporalActivity, name, start_to_close_timeout: 10) + ] + end + end +end diff --git a/coinbase_ruby/worker.rb b/coinbase_ruby/worker.rb new file mode 100644 index 0000000..1c97c71 --- /dev/null +++ b/coinbase_ruby/worker.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# We must require Temporal SDK first and set the env var to prevent Coinbase SDK from trying to load its protos +require 'temporalio/client' +require 'temporalio/worker' +ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] = '1' + +require_relative 'coinbase_activity' +require_relative 'coinbase_workflow' +require_relative 'temporal_activity' +require_relative 'temporal_workflow' +require 'logger' +require 'temporal-ruby' +require 'temporal/worker' + +# Create a Temporal client +client = Temporalio::Client.connect( + 'localhost:7233', + 'default', + logger: Logger.new($stdout, level: Logger::INFO) +) + +# Create Temporal worker with the activity and workflow on the coinbase-ruby-sample-temporal task queue +worker = Temporalio::Worker.new( + client:, + task_queue: 'coinbase-ruby-sample-temporal', + activities: [CoinbaseRuby::TemporalActivity], + workflows: [CoinbaseRuby::TemporalWorkflow] +) + +# Run the Temporal worker and inside it run the Coinbase worker +puts 'Starting worker on both Temporal Ruby SDK and Coinbase Ruby SDK' +worker.run do + # Configure Coinbase client/worker on the coinbase-ruby-sample-coinbase task queue + Temporal.configure do |config| + config.host = 'localhost' + config.port = 7233 + config.namespace = 'default' + config.task_queue = 'coinbase-ruby-sample-coinbase' + end + + # Run the Coinbase worker + worker = Temporal::Worker.new + worker.register_activity(CoinbaseRuby::CoinbaseActivity) + worker.register_workflow(CoinbaseRuby::CoinbaseWorkflow) + worker.start +end diff --git a/test/coinbase_ruby/workflow_test.rb b/test/coinbase_ruby/workflow_test.rb new file mode 100644 index 0000000..c67f903 --- /dev/null +++ b/test/coinbase_ruby/workflow_test.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'securerandom' +require 'test' +require 'temporalio/testing' +require 'temporalio/worker' +ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] = '1' +require 'temporal-ruby' +require 'temporal/worker' + +require 'coinbase_ruby/coinbase_workflow' +require 'coinbase_ruby/temporal_workflow' + +module CoinbaseRuby + class WorkflowTest < Test + def test_both_sdks + # Start a local env + Temporalio::Testing::WorkflowEnvironment.start_local do |env| + # Create Coinbase config, client, and worker + coinbase_config = Temporal::Configuration.new + host, port = env.client.connection.options.target_host.split(':') + coinbase_config.host = host + coinbase_config.port = port.to_i + coinbase_config.namespace = 'default' + coinbase_config.task_queue = 'coinbase-ruby-sample-coinbase' + coinbase_client = Temporal::Client.new(coinbase_config) + coinbase_worker = Temporal::Worker.new(coinbase_config) + coinbase_worker.register_activity(CoinbaseRuby::CoinbaseActivity) + coinbase_worker.register_workflow(CoinbaseRuby::CoinbaseWorkflow) + + # Run all inside Temporal worker + worker = Temporalio::Worker.new( + client: env.client, + task_queue: 'coinbase-ruby-sample-temporal', + activities: [CoinbaseRuby::TemporalActivity], + workflows: [CoinbaseRuby::TemporalWorkflow] + ) + worker.run do + # Run Coinbase worker in background, stop it when done + Thread.new { coinbase_worker.start } + + # Run both workflows from Temporal client + assert_equal ['Hello from Coinbase Ruby SDK, user-a!', 'Hello from Temporal Ruby SDK, user-a!'], + env.client.execute_workflow( + CoinbaseRuby::CoinbaseWorkflow.name, 'user-a', + id: "wf-#{SecureRandom.uuid}", task_queue: 'coinbase-ruby-sample-coinbase' + ) + assert_equal ['Hello from Coinbase Ruby SDK, user-b!', 'Hello from Temporal Ruby SDK, user-b!'], + env.client.execute_workflow( + CoinbaseRuby::TemporalWorkflow, 'user-b', + id: "wf-#{SecureRandom.uuid}", task_queue: 'coinbase-ruby-sample-temporal' + ) + + # Run both workflows from Coinbase client + workflow_id = "wf-#{SecureRandom.uuid}" + run_id = coinbase_client.start_workflow( + CoinbaseRuby::CoinbaseWorkflow, 'user-c', + options: { workflow_id:, task_queue: 'coinbase-ruby-sample-coinbase' } + ) + assert_equal ['Hello from Coinbase Ruby SDK, user-c!', 'Hello from Temporal Ruby SDK, user-c!'], + coinbase_client.await_workflow_result(CoinbaseRuby::CoinbaseWorkflow, workflow_id:, run_id:) + workflow_id = "wf-#{SecureRandom.uuid}" + run_id = coinbase_client.start_workflow( + :TemporalWorkflow, 'user-d', + options: { workflow_id:, task_queue: 'coinbase-ruby-sample-temporal' } + ) + assert_equal ['Hello from Coinbase Ruby SDK, user-d!', 'Hello from Temporal Ruby SDK, user-d!'], + coinbase_client.await_workflow_result(:TemporalWorkflow, workflow_id:, run_id:) + ensure + coinbase_worker.stop + end + end + end + end +end From 89c344704453ea1c4527cafd7eecc63660e1ef27 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Mon, 28 Apr 2025 14:17:27 -0500 Subject: [PATCH 2/4] Update to use fork and latest SDK --- Gemfile | 9 ++--- Gemfile.lock | 55 ++++++++++++++++++++---------- coinbase_ruby/coinbase_activity.rb | 2 ++ coinbase_ruby/coinbase_workflow.rb | 2 ++ 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 274b88d..3255745 100644 --- a/Gemfile +++ b/Gemfile @@ -3,11 +3,12 @@ source 'https://rubygems.org' # Primary Temporal dependency -gem 'temporalio', path: '../temporal-sdk-ruby/temporalio' +gem 'temporalio' -# Additional dependencies for certain samples -gem 'google-protobuf', '~> 3.25' -gem 'temporal-ruby', path: '../coinbase-temporal-ruby' +# Some samples require certain dependencies, so they are in groups below alphabetically by group +# TODO(cretz): Move this to coinbase/temporal-ruby and off this fork/branch +# when https://github.com/coinbase/temporal-ruby/pull/335 merged +gem 'temporal-ruby', github: 'cretz/coinbase-temporal-ruby', branch: 'disable-proto-load-option', group: :coinbase_ruby group :development do gem 'minitest' diff --git a/Gemfile.lock b/Gemfile.lock index a73699d..65ea626 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,30 +1,48 @@ +GIT + remote: https://github.com/cretz/coinbase-temporal-ruby.git + revision: debe21843b6214995fdcb1e4ea052f2b1075cc18 + branch: disable-proto-load-option + specs: + temporal-ruby (0.1.1) + base64 + grpc + oj + GEM remote: https://rubygems.org/ specs: ast (2.4.2) + base64 (0.2.0) bigdecimal (3.1.9) - google-protobuf (4.30.2) - bigdecimal - rake (>= 13) - google-protobuf (4.30.2-aarch64-linux) - bigdecimal - rake (>= 13) - google-protobuf (4.30.2-arm64-darwin) - bigdecimal - rake (>= 13) - google-protobuf (4.30.2-x86-linux) - bigdecimal - rake (>= 13) - google-protobuf (4.30.2-x86_64-darwin) - bigdecimal - rake (>= 13) - google-protobuf (4.30.2-x86_64-linux) - bigdecimal - rake (>= 13) + google-protobuf (3.25.7) + googleapis-common-protos-types (1.19.0) + google-protobuf (>= 3.18, < 5.a) + grpc (1.71.0) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) + grpc (1.71.0-aarch64-linux) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) + grpc (1.71.0-arm64-darwin) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) + grpc (1.71.0-x86-linux) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) + grpc (1.71.0-x86_64-darwin) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) + grpc (1.71.0-x86_64-linux) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) json (2.9.1) language_server-protocol (3.17.0.3) logger (1.7.0) minitest (5.25.4) + oj (3.16.10) + bigdecimal (>= 3.0) + ostruct (>= 0.2) + ostruct (0.6.1) parallel (1.26.3) parser (3.3.6.0) ast (~> 2.4.1) @@ -77,6 +95,7 @@ DEPENDENCIES minitest rake rubocop + temporal-ruby! temporalio BUNDLED WITH diff --git a/coinbase_ruby/coinbase_activity.rb b/coinbase_ruby/coinbase_activity.rb index 9a5cadb..fc717de 100644 --- a/coinbase_ruby/coinbase_activity.rb +++ b/coinbase_ruby/coinbase_activity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'temporal-ruby' module CoinbaseRuby diff --git a/coinbase_ruby/coinbase_workflow.rb b/coinbase_ruby/coinbase_workflow.rb index 65145fe..7deca2f 100644 --- a/coinbase_ruby/coinbase_workflow.rb +++ b/coinbase_ruby/coinbase_workflow.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'temporal-ruby' require_relative 'coinbase_activity' From 7968c7ea5e1993d25b6cb1db9eb95696478960a6 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Mon, 28 Apr 2025 14:19:55 -0500 Subject: [PATCH 3/4] Typo --- coinbase_ruby/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coinbase_ruby/README.md b/coinbase_ruby/README.md index a33325f..5292a25 100644 --- a/coinbase_ruby/README.md +++ b/coinbase_ruby/README.md @@ -10,7 +10,7 @@ from this directory: bundle exec ruby worker.rb -Finally in another terminal, use the Ruby client to the workflow from this directory: +Finally in another terminal, use the Ruby client to run the workflow from this directory: bundle exec ruby starter.rb From de9d3ce23768fcac1f3329e905d561703f073730 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Mon, 28 Apr 2025 14:24:17 -0500 Subject: [PATCH 4/4] README update --- Gemfile | 2 +- coinbase_ruby/README.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 3255745..becd54d 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'temporalio' # Some samples require certain dependencies, so they are in groups below alphabetically by group # TODO(cretz): Move this to coinbase/temporal-ruby and off this fork/branch -# when https://github.com/coinbase/temporal-ruby/pull/335 merged +# when https://github.com/coinbase/temporal-ruby/pull/335 merged, and then update the coinbase_ruby README gem 'temporal-ruby', github: 'cretz/coinbase-temporal-ruby', branch: 'disable-proto-load-option', group: :coinbase_ruby group :development do diff --git a/coinbase_ruby/README.md b/coinbase_ruby/README.md index 5292a25..10ff98a 100644 --- a/coinbase_ruby/README.md +++ b/coinbase_ruby/README.md @@ -5,6 +5,10 @@ able to interoperate with a workflow, activity, and client from Temporal Ruby SD activity in both SDKs, a workflow in both SDKs each calling both activities, a worker in both SDKs running in the same process, and a starter with clients from each SDK each invoking both workflows. +⚠️ NOTE - this requires disabling the loading of protos from the Coinbase Ruby SDK. As of this writing, +https://github.com/coinbase/temporal-ruby/pull/335 is not merged, so the Gemfile depends on the branch at +https://github.com/cretz/coinbase-temporal-ruby/tree/disable-proto-load-option for now. + To run, first see [README.md](../README.md) for prerequisites. Then, in another terminal, start the Ruby worker from this directory: