From 94687a6ea4d7b68e0ddf4f157c3cf73dbcb9f944 Mon Sep 17 00:00:00 2001 From: Morgan hunter Date: Mon, 23 Sep 2024 10:57:29 +1000 Subject: [PATCH 1/5] fix: add a loop to await_workflow_result so it will keep retrying until the workflow is completed - this is based on this PR: https://github.com/coinbase/temporal-ruby/pull/305/files --- lib/temporal/client.rb | 40 +++++++++++++++------------ spec/unit/lib/temporal/client_spec.rb | 25 +++++++++++++++++ 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/lib/temporal/client.rb b/lib/temporal/client.rb index fc390e92..b4f7dd78 100644 --- a/lib/temporal/client.rb +++ b/lib/temporal/client.rb @@ -230,25 +230,31 @@ def await_workflow_result(workflow, workflow_id:, run_id: nil, timeout: nil, nam execution_options = ExecutionOptions.new(workflow, options, config.default_execution_options) max_timeout = Temporal::Connection::GRPC::SERVER_MAX_GET_WORKFLOW_EXECUTION_HISTORY_POLL history_response = nil - begin - history_response = connection.get_workflow_execution_history( - namespace: execution_options.namespace, - workflow_id: workflow_id, - run_id: run_id, - wait_for_new_event: true, - event_type: :close, - timeout: timeout || max_timeout, - ) - rescue GRPC::DeadlineExceeded => e - message = if timeout - "Timed out after your specified limit of timeout: #{timeout} seconds" - else - "Timed out after #{max_timeout} seconds, which is the maximum supported amount." + closed_event = nil + loop do + begin + history_response = connection.get_workflow_execution_history( + namespace: execution_options.namespace, + workflow_id: workflow_id, + run_id: run_id, + wait_for_new_event: true, + event_type: :close, + timeout: timeout || max_timeout, + ) + rescue GRPC::DeadlineExceeded => e + message = if timeout + "Timed out after your specified limit of timeout: #{timeout} seconds" + else + "Timed out after #{max_timeout} seconds, which is the maximum supported amount." + end + raise TimeoutError.new(message) end - raise TimeoutError.new(message) + history = Workflow::History.new(history_response.history.events) + closed_event = history.events.first + + break if closed_event end - history = Workflow::History.new(history_response.history.events) - closed_event = history.events.first + case closed_event.type when 'WORKFLOW_EXECUTION_COMPLETED' payloads = closed_event.attributes.result diff --git a/spec/unit/lib/temporal/client_spec.rb b/spec/unit/lib/temporal/client_spec.rb index a0ba4dc1..6e0b53e8 100644 --- a/spec/unit/lib/temporal/client_spec.rb +++ b/spec/unit/lib/temporal/client_spec.rb @@ -449,6 +449,31 @@ class NamespacedWorkflow < Temporal::Workflow ) end + it 'retries if there is till there is no closed event' do + completed_event = Fabricate(:workflow_completed_event, result: nil) + response_with_no_closed_event = Fabricate(:workflow_execution_history, events: []) + response_with_closed_event = Fabricate(:workflow_execution_history, events: [completed_event]) + + expect(connection) + .to receive(:get_workflow_execution_history) + .with( + namespace: 'some-namespace', + workflow_id: workflow_id, + run_id: run_id, + wait_for_new_event: true, + event_type: :close, + timeout: 30, + ) + .and_return(response_with_no_closed_event, response_with_closed_event) + + + subject.await_workflow_result( + NamespacedWorkflow, + workflow_id: workflow_id, + run_id: run_id, + ) + end + it 'can override the namespace' do completed_event = Fabricate(:workflow_completed_event, result: nil) response = Fabricate(:workflow_execution_history, events: [completed_event]) From 7438ac16e48bc8a2ca8968b06ef4f6ad7a6f3b24 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Tue, 4 Feb 2025 11:01:01 -0600 Subject: [PATCH 2/5] Support disabling proto load via environment variable --- Makefile | 6 ++++++ .../api/operatorservice/v1/service_services_pb.rb | 2 +- .../api/workflowservice/v1/service_services_pb.rb | 2 +- lib/temporal.rb | 6 ++++-- lib/temporal/connection/grpc.rb | 10 +++++++--- lib/temporal/connection/serializer/base.rb | 8 ++++++-- lib/temporal/testing/replay_tester.rb | 6 +++++- 7 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 6967cb5b..6c3d76fa 100644 --- a/Makefile +++ b/Makefile @@ -6,4 +6,10 @@ PROTO_OUT := lib/gen proto: $(foreach PROTO_DIR,$(PROTO_DIRS),bundle exec grpc_tools_ruby_protoc -Iproto --ruby_out=$(PROTO_OUT) --grpc_out=$(PROTO_OUT) $(PROTO_DIR)*.proto;) + # Need to only load imports if not disabled + sed -i "/require 'temporal/ s|\(.*\)|\1 unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1'|" \ + lib/gen/temporal/api/operatorservice/v1/service_services_pb.rb + sed -i "/require 'temporal/ s|\(.*\)|\1 unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1'|" \ + lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb + .PHONY: proto diff --git a/lib/gen/temporal/api/operatorservice/v1/service_services_pb.rb b/lib/gen/temporal/api/operatorservice/v1/service_services_pb.rb index 44a8c571..1dad652f 100644 --- a/lib/gen/temporal/api/operatorservice/v1/service_services_pb.rb +++ b/lib/gen/temporal/api/operatorservice/v1/service_services_pb.rb @@ -25,7 +25,7 @@ # require 'grpc' -require 'temporal/api/operatorservice/v1/service_pb' +require 'temporal/api/operatorservice/v1/service_pb' unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' module Temporalio module Api diff --git a/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb b/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb index 3acb9d26..e7a42c93 100644 --- a/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb +++ b/lib/gen/temporal/api/workflowservice/v1/service_services_pb.rb @@ -25,7 +25,7 @@ # require 'grpc' -require 'temporal/api/workflowservice/v1/service_pb' +require 'temporal/api/workflowservice/v1/service_pb' unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' module Temporalio module Api diff --git a/lib/temporal.rb b/lib/temporal.rb index b9f49d55..91b69cb3 100644 --- a/lib/temporal.rb +++ b/lib/temporal.rb @@ -1,5 +1,7 @@ -# Protoc wants all of its generated files on the LOAD_PATH -$LOAD_PATH << File.expand_path('./gen', __dir__) +# Protoc wants all of its generated files on the LOAD_PATH. Only require protos if not disabled. +unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' + $LOAD_PATH << File.expand_path('./gen', __dir__) +end require 'securerandom' require 'forwardable' diff --git a/lib/temporal/connection/grpc.rb b/lib/temporal/connection/grpc.rb index 1f817f51..7c9a8c36 100644 --- a/lib/temporal/connection/grpc.rb +++ b/lib/temporal/connection/grpc.rb @@ -3,11 +3,8 @@ require 'google/protobuf/well_known_types' require 'securerandom' require 'json' -require 'gen/temporal/api/filter/v1/message_pb' require 'gen/temporal/api/workflowservice/v1/service_services_pb' require 'gen/temporal/api/operatorservice/v1/service_services_pb' -require 'gen/temporal/api/enums/v1/workflow_pb' -require 'gen/temporal/api/enums/v1/common_pb' require 'temporal/connection/errors' require 'temporal/connection/interceptors/client_name_version_interceptor' require 'temporal/connection/serializer' @@ -16,6 +13,13 @@ require 'temporal/connection/serializer/schedule' require 'temporal/connection/serializer/workflow_id_reuse_policy' +# Only require protos if not disabled +unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' + require 'gen/temporal/api/filter/v1/message_pb' + require 'gen/temporal/api/enums/v1/workflow_pb' + require 'gen/temporal/api/enums/v1/common_pb' +end + module Temporal module Connection class GRPC diff --git a/lib/temporal/connection/serializer/base.rb b/lib/temporal/connection/serializer/base.rb index 79e8767a..595a73b6 100644 --- a/lib/temporal/connection/serializer/base.rb +++ b/lib/temporal/connection/serializer/base.rb @@ -1,6 +1,10 @@ require 'oj' -require 'gen/temporal/api/common/v1/message_pb' -require 'gen/temporal/api/command/v1/message_pb' + +# Only require protos if not disabled +unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' + require 'gen/temporal/api/common/v1/message_pb' + require 'gen/temporal/api/command/v1/message_pb' +end module Temporal module Connection diff --git a/lib/temporal/testing/replay_tester.rb b/lib/temporal/testing/replay_tester.rb index 6a98c86e..a08617a8 100644 --- a/lib/temporal/testing/replay_tester.rb +++ b/lib/temporal/testing/replay_tester.rb @@ -1,4 +1,3 @@ -require "gen/temporal/api/history/v1/message_pb" require "json" require "temporal/errors" require "temporal/metadata/workflow_task" @@ -6,6 +5,11 @@ require "temporal/workflow/executor" require "temporal/workflow/stack_trace_tracker" +# Only require protos if not disabled +unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' + require "gen/temporal/api/history/v1/message_pb" +end + module Temporal module Testing class ReplayError < StandardError From 501fa78902b9a095ec58d8136cbb8a11325160ac Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Tue, 4 Feb 2025 14:24:33 -0600 Subject: [PATCH 3/5] Add comments --- lib/temporal/connection/grpc.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/temporal/connection/grpc.rb b/lib/temporal/connection/grpc.rb index 7c9a8c36..eb2e23f6 100644 --- a/lib/temporal/connection/grpc.rb +++ b/lib/temporal/connection/grpc.rb @@ -3,8 +3,6 @@ require 'google/protobuf/well_known_types' require 'securerandom' require 'json' -require 'gen/temporal/api/workflowservice/v1/service_services_pb' -require 'gen/temporal/api/operatorservice/v1/service_services_pb' require 'temporal/connection/errors' require 'temporal/connection/interceptors/client_name_version_interceptor' require 'temporal/connection/serializer' @@ -13,13 +11,17 @@ require 'temporal/connection/serializer/schedule' require 'temporal/connection/serializer/workflow_id_reuse_policy' -# Only require protos if not disabled +# Only require protos if not disabled. This env var is commonly set to work alongside the temporalio/sdk-ruby project. unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' require 'gen/temporal/api/filter/v1/message_pb' require 'gen/temporal/api/enums/v1/workflow_pb' require 'gen/temporal/api/enums/v1/common_pb' end +# These are gRPC stubs, not protos, and therefore are not disabled when protos are +require 'gen/temporal/api/workflowservice/v1/service_services_pb' +require 'gen/temporal/api/operatorservice/v1/service_services_pb' + module Temporal module Connection class GRPC From a750c3b4f05825ec032d9f63b7ae6e99549cd09d Mon Sep 17 00:00:00 2001 From: Andrew Buntine Date: Mon, 29 Jun 2026 14:25:19 +1000 Subject: [PATCH 4/5] fix: fix load-order issue --- lib/temporal/connection/serializer/schedule_overlap_policy.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/temporal/connection/serializer/schedule_overlap_policy.rb b/lib/temporal/connection/serializer/schedule_overlap_policy.rb index a866c8ee..24609c66 100644 --- a/lib/temporal/connection/serializer/schedule_overlap_policy.rb +++ b/lib/temporal/connection/serializer/schedule_overlap_policy.rb @@ -1,4 +1,5 @@ require "temporal/connection/serializer/base" +require "gen/temporal/api/enums/v1/schedule_pb" module Temporal module Connection From 61a3e263842e7bfee7e9c9673e25ac3bbef696fb Mon Sep 17 00:00:00 2001 From: Andrew Buntine Date: Tue, 30 Jun 2026 10:47:09 +1000 Subject: [PATCH 5/5] fix: only require protos if they have not been disabled --- .../connection/serializer/schedule_overlap_policy.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/temporal/connection/serializer/schedule_overlap_policy.rb b/lib/temporal/connection/serializer/schedule_overlap_policy.rb index 24609c66..8e7aba32 100644 --- a/lib/temporal/connection/serializer/schedule_overlap_policy.rb +++ b/lib/temporal/connection/serializer/schedule_overlap_policy.rb @@ -1,5 +1,9 @@ require "temporal/connection/serializer/base" -require "gen/temporal/api/enums/v1/schedule_pb" + +# Only require protos if not disabled +unless ENV['COINBASE_TEMPORAL_RUBY_DISABLE_PROTO_LOAD'] == '1' + require "gen/temporal/api/enums/v1/schedule_pb" +end module Temporal module Connection