temporalio 0.5.0-x86_64-linux → 1.0.0-x86_64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/temporalio/activity/info.rb +5 -0
- data/lib/temporalio/api/batch/v1/message.rb +4 -1
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +12 -1
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +1 -1
- data/lib/temporalio/api/cloud/connectivityrule/v1/message.rb +29 -0
- data/lib/temporalio/api/cloud/identity/v1/message.rb +1 -1
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +2 -1
- data/lib/temporalio/api/cloud/operation/v1/message.rb +1 -1
- data/lib/temporalio/api/common/v1/message.rb +2 -1
- data/lib/temporalio/api/enums/v1/batch_operation.rb +1 -1
- data/lib/temporalio/api/enums/v1/task_queue.rb +2 -1
- data/lib/temporalio/api/history/v1/message.rb +1 -1
- data/lib/temporalio/api/payload_visitor.rb +19 -1
- data/lib/temporalio/api/sdk/v1/worker_config.rb +23 -0
- data/lib/temporalio/api/taskqueue/v1/message.rb +5 -1
- data/lib/temporalio/api/worker/v1/message.rb +2 -1
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +10 -1
- data/lib/temporalio/api/workflowservice/v1/service.rb +1 -1
- data/lib/temporalio/cancellation.rb +16 -12
- data/lib/temporalio/client/async_activity_handle.rb +1 -0
- data/lib/temporalio/client/connection/cloud_service.rb +75 -0
- data/lib/temporalio/client/connection/workflow_service.rb +45 -0
- data/lib/temporalio/client/connection.rb +2 -1
- data/lib/temporalio/contrib/open_telemetry.rb +9 -13
- data/lib/temporalio/converters/payload_converter/json_plain.rb +22 -5
- data/lib/temporalio/env_config.rb +343 -0
- data/lib/temporalio/error.rb +5 -1
- data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +1 -1
- data/lib/temporalio/internal/bridge/worker.rb +50 -0
- data/lib/temporalio/internal/client/implementation.rb +7 -2
- data/lib/temporalio/internal/worker/activity_worker.rb +1 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +7 -4
- data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +2 -0
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +18 -11
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +6 -5
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +5 -2
- data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +10 -4
- data/lib/temporalio/internal/worker/workflow_instance.rb +77 -80
- data/lib/temporalio/priority.rb +47 -6
- data/lib/temporalio/scoped_logger.rb +1 -1
- data/lib/temporalio/testing/activity_environment.rb +1 -0
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/illegal_workflow_call_validator.rb +9 -0
- data/lib/temporalio/worker/interceptor.rb +1 -0
- data/lib/temporalio/worker/tuner.rb +185 -16
- data/lib/temporalio/worker.rb +10 -1
- data/lib/temporalio/workflow/definition.rb +4 -6
- data/lib/temporalio/workflow/info.rb +3 -0
- data/lib/temporalio/workflow.rb +80 -6
- metadata +5 -2
| @@ -40,6 +40,7 @@ module Temporalio | |
| 40 40 | 
             
                    TunerSlotSupplierOptions = Struct.new(
         | 
| 41 41 | 
             
                      :fixed_size,
         | 
| 42 42 | 
             
                      :resource_based,
         | 
| 43 | 
            +
                      :custom,
         | 
| 43 44 | 
             
                      keyword_init: true
         | 
| 44 45 | 
             
                    )
         | 
| 45 46 |  | 
| @@ -103,6 +104,55 @@ module Temporalio | |
| 103 104 | 
             
                      # TODO(cretz): Log error on this somehow?
         | 
| 104 105 | 
             
                      async_complete_activity_task(proto.to_proto, queue)
         | 
| 105 106 | 
             
                    end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    class CustomSlotSupplier
         | 
| 109 | 
            +
                      def initialize(slot_supplier:, thread_pool:)
         | 
| 110 | 
            +
                        @slot_supplier = slot_supplier
         | 
| 111 | 
            +
                        @thread_pool = thread_pool
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      def reserve_slot(context, cancellation, &block)
         | 
| 115 | 
            +
                        run_user_code do
         | 
| 116 | 
            +
                          @slot_supplier.reserve_slot(context, cancellation) { |v| block.call(v) }
         | 
| 117 | 
            +
                        rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 118 | 
            +
                          block.call(e)
         | 
| 119 | 
            +
                        end
         | 
| 120 | 
            +
                      end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                      def try_reserve_slot(context, &block)
         | 
| 123 | 
            +
                        run_user_code do
         | 
| 124 | 
            +
                          block.call(@slot_supplier.try_reserve_slot(context))
         | 
| 125 | 
            +
                        rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 126 | 
            +
                          block.call(e)
         | 
| 127 | 
            +
                        end
         | 
| 128 | 
            +
                      end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                      def mark_slot_used(context, &block)
         | 
| 131 | 
            +
                        run_user_code do
         | 
| 132 | 
            +
                          block.call(@slot_supplier.mark_slot_used(context))
         | 
| 133 | 
            +
                        rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 134 | 
            +
                          block.call(e)
         | 
| 135 | 
            +
                        end
         | 
| 136 | 
            +
                      end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                      def release_slot(context, &block)
         | 
| 139 | 
            +
                        run_user_code do
         | 
| 140 | 
            +
                          block.call(@slot_supplier.release_slot(context))
         | 
| 141 | 
            +
                        rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 142 | 
            +
                          block.call(e)
         | 
| 143 | 
            +
                        end
         | 
| 144 | 
            +
                      end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                      private
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                      def run_user_code(&)
         | 
| 149 | 
            +
                        if @thread_pool
         | 
| 150 | 
            +
                          @thread_pool.execute(&)
         | 
| 151 | 
            +
                        else
         | 
| 152 | 
            +
                          yield
         | 
| 153 | 
            +
                        end
         | 
| 154 | 
            +
                      end
         | 
| 155 | 
            +
                    end
         | 
| 106 156 | 
             
                  end
         | 
| 107 157 | 
             
                end
         | 
| 108 158 | 
             
              end
         | 
| @@ -2,6 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require 'google/protobuf/well_known_types'
         | 
| 4 4 | 
             
            require 'securerandom'
         | 
| 5 | 
            +
            require 'temporalio/activity'
         | 
| 5 6 | 
             
            require 'temporalio/api'
         | 
| 6 7 | 
             
            require 'temporalio/client/activity_id_reference'
         | 
| 7 8 | 
             
            require 'temporalio/client/async_activity_handle'
         | 
| @@ -829,9 +830,13 @@ module Temporalio | |
| 829 830 | 
             
                                 rpc_options: Implementation.with_default_rpc_options(input.rpc_options)
         | 
| 830 831 | 
             
                               )
         | 
| 831 832 | 
             
                             end
         | 
| 832 | 
            -
                       | 
| 833 | 
            +
                      return unless resp.cancel_requested || resp.activity_paused || resp.activity_reset
         | 
| 833 834 |  | 
| 834 | 
            -
                       | 
| 835 | 
            +
                      raise Error::AsyncActivityCanceledError, Activity::CancellationDetails.new(
         | 
| 836 | 
            +
                        cancel_requested: resp.cancel_requested,
         | 
| 837 | 
            +
                        paused: resp.activity_paused,
         | 
| 838 | 
            +
                        reset: resp.activity_reset
         | 
| 839 | 
            +
                      )
         | 
| 835 840 | 
             
                    end
         | 
| 836 841 |  | 
| 837 842 | 
             
                    def complete_async_activity(input)
         | 
| @@ -185,6 +185,7 @@ module Temporalio | |
| 185 185 | 
             
                          payloads = codec.decode(payloads) if codec
         | 
| 186 186 | 
             
                          payloads.map { |p| Temporalio::Converters::RawValue.new(p) }
         | 
| 187 187 | 
             
                        end,
         | 
| 188 | 
            +
                        retry_policy: (RetryPolicy._from_proto(start.retry_policy) if start.retry_policy),
         | 
| 188 189 | 
             
                        schedule_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.schedule_to_close_timeout),
         | 
| 189 190 | 
             
                        scheduled_time: Internal::ProtoUtils.timestamp_to_time(start.scheduled_time) || raise, # Never nil
         | 
| 190 191 | 
             
                        start_to_close_timeout: Internal::ProtoUtils.duration_to_seconds(start.start_to_close_timeout),
         | 
| @@ -63,12 +63,13 @@ module Temporalio | |
| 63 63 | 
             
                      end
         | 
| 64 64 |  | 
| 65 65 | 
             
                      def durable_scheduler_disabled(&)
         | 
| 66 | 
            -
                        prev = Fiber. | 
| 67 | 
            -
                         | 
| 68 | 
            -
                         | 
| 66 | 
            +
                        prev = Fiber.scheduler
         | 
| 67 | 
            +
                        # Imply illegal call tracing disabled
         | 
| 68 | 
            +
                        illegal_call_tracing_disabled do
         | 
| 69 | 
            +
                          Fiber.set_scheduler(nil)
         | 
| 69 70 | 
             
                          yield
         | 
| 70 71 | 
             
                        ensure
         | 
| 71 | 
            -
                           | 
| 72 | 
            +
                          Fiber.set_scheduler(prev)
         | 
| 72 73 | 
             
                        end
         | 
| 73 74 | 
             
                      end
         | 
| 74 75 |  | 
| @@ -129,6 +130,7 @@ module Temporalio | |
| 129 130 | 
             
                      def execute_local_activity(
         | 
| 130 131 | 
             
                        activity,
         | 
| 131 132 | 
             
                        *args,
         | 
| 133 | 
            +
                        summary:,
         | 
| 132 134 | 
             
                        schedule_to_close_timeout:,
         | 
| 133 135 | 
             
                        schedule_to_start_timeout:,
         | 
| 134 136 | 
             
                        start_to_close_timeout:,
         | 
| @@ -156,6 +158,7 @@ module Temporalio | |
| 156 158 | 
             
                          Temporalio::Worker::Interceptor::Workflow::ExecuteLocalActivityInput.new(
         | 
| 157 159 | 
             
                            activity:,
         | 
| 158 160 | 
             
                            args:,
         | 
| 161 | 
            +
                            summary:,
         | 
| 159 162 | 
             
                            schedule_to_close_timeout:,
         | 
| 160 163 | 
             
                            schedule_to_start_timeout:,
         | 
| 161 164 | 
             
                            start_to_close_timeout:,
         | 
| @@ -16,7 +16,7 @@ module Temporalio | |
| 16 16 | 
             
                          # @type var fixed_val: :all | Worker::IllegalWorkflowCallValidator | Hash[Symbol, TrueClass | Worker::IllegalWorkflowCallValidator] # rubocop:disable Layout/LineLength
         | 
| 17 17 | 
             
                          fixed_val = case val
         | 
| 18 18 | 
             
                                      when Temporalio::Worker::IllegalWorkflowCallValidator
         | 
| 19 | 
            -
                                        if  | 
| 19 | 
            +
                                        if val.method_name
         | 
| 20 20 | 
             
                                          raise ArgumentError,
         | 
| 21 21 | 
             
                                                'Top level IllegalWorkflowCallValidator instances cannot have method name'
         | 
| 22 22 | 
             
                                        end
         | 
| @@ -84,7 +84,7 @@ module Temporalio | |
| 84 84 | 
             
                            when :all
         | 
| 85 85 | 
             
                              ''
         | 
| 86 86 | 
             
                            when Temporalio::Worker::IllegalWorkflowCallValidator
         | 
| 87 | 
            -
                               | 
| 87 | 
            +
                              disable_temporarily do
         | 
| 88 88 | 
             
                                vals.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
         | 
| 89 89 | 
             
                                                  class_name:, method_name: tp.callee_id, trace_point: tp
         | 
| 90 90 | 
             
                                                ))
         | 
| @@ -98,7 +98,7 @@ module Temporalio | |
| 98 98 | 
             
                              when true
         | 
| 99 99 | 
             
                                ''
         | 
| 100 100 | 
             
                              when Temporalio::Worker::IllegalWorkflowCallValidator
         | 
| 101 | 
            -
                                 | 
| 101 | 
            +
                                disable_temporarily do
         | 
| 102 102 | 
             
                                  per_method.block.call(Temporalio::Worker::IllegalWorkflowCallValidator::CallInfo.new(
         | 
| 103 103 | 
             
                                                          class_name:, method_name: tp.callee_id, trace_point: tp
         | 
| 104 104 | 
             
                                                        ))
         | 
| @@ -118,8 +118,11 @@ module Temporalio | |
| 118 118 | 
             
                      end
         | 
| 119 119 |  | 
| 120 120 | 
             
                      def enable(&block)
         | 
| 121 | 
            -
                        # We've seen leaking issues in Ruby 3.2 where | 
| 122 | 
            -
                        # that it was not started on. So we will check | 
| 121 | 
            +
                        # This is not reentrant and not expected to be called as such. We've seen leaking issues in Ruby 3.2 where
         | 
| 122 | 
            +
                        # the TracePoint inadvertently remains enabled even for threads that it was not started on. So we will check
         | 
| 123 | 
            +
                        # the thread ourselves. We also use the "enabled thread" concept for disabling checks too, see
         | 
| 124 | 
            +
                        # disable_temporarily for more details.
         | 
| 125 | 
            +
             | 
| 123 126 | 
             
                        @enabled_thread = Thread.current
         | 
| 124 127 | 
             
                        @tracepoint.enable do
         | 
| 125 128 | 
             
                          block.call
         | 
| @@ -128,13 +131,17 @@ module Temporalio | |
| 128 131 | 
             
                        end
         | 
| 129 132 | 
             
                      end
         | 
| 130 133 |  | 
| 131 | 
            -
                      def  | 
| 134 | 
            +
                      def disable_temporarily(&)
         | 
| 135 | 
            +
                        # An earlier version of this used @tracepoint.disable, but in some versions of Ruby, the observed behavior
         | 
| 136 | 
            +
                        # is confusingly not reentrant or at least not predictable. Therefore, instead of calling
         | 
| 137 | 
            +
                        # @tracepoint.disable, we are just unsetting the enabled thread. This means the tracer is still running, but
         | 
| 138 | 
            +
                        # no checks are performed. This is effectively a no-op if tracing was never enabled.
         | 
| 139 | 
            +
             | 
| 132 140 | 
             
                        previous_thread = @enabled_thread
         | 
| 133 | 
            -
                        @ | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
                        end
         | 
| 141 | 
            +
                        @enabled_thread = nil
         | 
| 142 | 
            +
                        yield
         | 
| 143 | 
            +
                      ensure
         | 
| 144 | 
            +
                        @enabled_thread = previous_thread
         | 
| 138 145 | 
             
                      end
         | 
| 139 146 | 
             
                    end
         | 
| 140 147 | 
             
                  end
         | 
| @@ -114,14 +114,15 @@ module Temporalio | |
| 114 114 | 
             
                                local_retry_threshold: ProtoUtils.seconds_to_duration(input.local_retry_threshold),
         | 
| 115 115 | 
             
                                attempt: do_backoff&.attempt || 0,
         | 
| 116 116 | 
             
                                original_schedule_time: do_backoff&.original_schedule_time
         | 
| 117 | 
            -
                              )
         | 
| 117 | 
            +
                              ),
         | 
| 118 | 
            +
                              user_metadata: ProtoUtils.to_user_metadata(input.summary, nil, @instance.payload_converter)
         | 
| 118 119 | 
             
                            )
         | 
| 119 120 | 
             
                          )
         | 
| 120 121 | 
             
                          seq
         | 
| 121 122 | 
             
                        end
         | 
| 122 123 | 
             
                      end
         | 
| 123 124 |  | 
| 124 | 
            -
                      def execute_activity_with_local_backoffs(local:, cancellation:, result_hint:, &)
         | 
| 125 | 
            +
                      def execute_activity_with_local_backoffs(local:, cancellation:, result_hint:, &block)
         | 
| 125 126 | 
             
                        # We do not even want to schedule if the cancellation is already cancelled. We choose to use canceled
         | 
| 126 127 | 
             
                        # failure instead of wrapping in activity failure which is similar to what other SDKs do, with the accepted
         | 
| 127 128 | 
             
                        # tradeoff that it makes rescue more difficult (hence the presence of Error.canceled? helper).
         | 
| @@ -130,7 +131,7 @@ module Temporalio | |
| 130 131 | 
             
                        # This has to be done in a loop for local activity backoff
         | 
| 131 132 | 
             
                        last_local_backoff = nil
         | 
| 132 133 | 
             
                        loop do
         | 
| 133 | 
            -
                          result = execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &)
         | 
| 134 | 
            +
                          result = execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &block)
         | 
| 134 135 | 
             
                          return result unless result.is_a?(Bridge::Api::ActivityResult::DoBackoff)
         | 
| 135 136 |  | 
| 136 137 | 
             
                          # @type var result: untyped
         | 
| @@ -142,9 +143,9 @@ module Temporalio | |
| 142 143 | 
             
                      end
         | 
| 143 144 |  | 
| 144 145 | 
             
                      # If this doesn't raise, it returns success | DoBackoff
         | 
| 145 | 
            -
                      def execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &)
         | 
| 146 | 
            +
                      def execute_activity_once(local:, cancellation:, last_local_backoff:, result_hint:, &block)
         | 
| 146 147 | 
             
                        # Add to pending activities (removed by the resolver)
         | 
| 147 | 
            -
                        seq =  | 
| 148 | 
            +
                        seq = block.call(last_local_backoff)
         | 
| 148 149 | 
             
                        @instance.pending_activities[seq] = Fiber.current
         | 
| 149 150 |  | 
| 150 151 | 
             
                        # Add cancellation hook
         | 
| @@ -27,8 +27,11 @@ module Temporalio | |
| 27 27 | 
             
                          return true
         | 
| 28 28 | 
             
                        end
         | 
| 29 29 |  | 
| 30 | 
            -
                        # Disable  | 
| 31 | 
            -
                         | 
| 30 | 
            +
                        # Disable scheduler since logs technically have local mutexes in them that cannot be done durably or they
         | 
| 31 | 
            +
                        # will block workflows
         | 
| 32 | 
            +
                        @instance.context.durable_scheduler_disabled do
         | 
| 33 | 
            +
                          super
         | 
| 34 | 
            +
                        end
         | 
| 32 35 | 
             
                      end
         | 
| 33 36 | 
             
                    end
         | 
| 34 37 | 
             
                  end
         | 
| @@ -40,10 +40,15 @@ module Temporalio | |
| 40 40 | 
             
                          cond_fiber = nil
         | 
| 41 41 | 
             
                          cond_result = nil
         | 
| 42 42 | 
             
                          @wait_conditions.each do |seq, cond|
         | 
| 43 | 
            +
                            # Evaluate condition or skip if not true
         | 
| 43 44 | 
             
                            next unless (cond_result = cond.first.call)
         | 
| 44 45 |  | 
| 45 | 
            -
                             | 
| 46 | 
            -
                             | 
| 46 | 
            +
                            # There have been reports of this fiber being completed already, so we make sure not to process if it
         | 
| 47 | 
            +
                            # has, but we still delete it
         | 
| 48 | 
            +
                            deleted_cond = @wait_conditions.delete(seq)
         | 
| 49 | 
            +
                            next unless deleted_cond&.last&.alive?
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                            cond_fiber = deleted_cond.last
         | 
| 47 52 | 
             
                            break
         | 
| 48 53 | 
             
                          end
         | 
| 49 54 | 
             
                          return unless cond_fiber
         | 
| @@ -106,7 +111,7 @@ module Temporalio | |
| 106 111 | 
             
                        # We just yield because unblock will resume this. We will just wrap in timeout if needed.
         | 
| 107 112 | 
             
                        if timeout
         | 
| 108 113 | 
             
                          begin
         | 
| 109 | 
            -
                             | 
| 114 | 
            +
                            Workflow.timeout(timeout) { Fiber.yield }
         | 
| 110 115 | 
             
                            true
         | 
| 111 116 | 
             
                          rescue Timeout::Error
         | 
| 112 117 | 
             
                            false
         | 
| @@ -141,7 +146,8 @@ module Temporalio | |
| 141 146 | 
             
                        unless @instance.io_enabled
         | 
| 142 147 | 
             
                          raise Workflow::NondeterminismError,
         | 
| 143 148 | 
             
                                'Cannot perform IO from inside a workflow. If this is known to be safe, ' \
         | 
| 144 | 
            -
                                'the code can be run in a Temporalio::Workflow::Unsafe. | 
| 149 | 
            +
                                'the code can be run in a Temporalio::Workflow::Unsafe.durable_scheduler_disabled ' \
         | 
| 150 | 
            +
                                'or Temporalio::Workflow::Unsafe.io_enabled block.'
         | 
| 145 151 | 
             
                        end
         | 
| 146 152 |  | 
| 147 153 | 
             
                        # Use regular Ruby behavior of blocking this thread. There is no Ruby implementation of io_wait we can just
         | 
| @@ -125,6 +125,7 @@ module Temporalio | |
| 125 125 | 
             
                          continued_run_id: ProtoUtils.string_or(@init_job.continued_from_execution_run_id),
         | 
| 126 126 | 
             
                          cron_schedule: ProtoUtils.string_or(@init_job.cron_schedule),
         | 
| 127 127 | 
             
                          execution_timeout: ProtoUtils.duration_to_seconds(@init_job.workflow_execution_timeout),
         | 
| 128 | 
            +
                          first_execution_run_id: @init_job.first_execution_run_id,
         | 
| 128 129 | 
             
                          headers: ProtoUtils.headers_from_proto_map(@init_job.headers, @payload_converter) || {},
         | 
| 129 130 | 
             
                          last_failure: if @init_job.continued_failure
         | 
| 130 131 | 
             
                                          @failure_converter.from_failure(@init_job.continued_failure, @payload_converter)
         | 
| @@ -162,86 +163,9 @@ module Temporalio | |
| 162 163 | 
             
                    end
         | 
| 163 164 |  | 
| 164 165 | 
             
                    def activate(activation)
         | 
| 165 | 
            -
                      # Run inside of scheduler
         | 
| 166 | 
            -
                      run_in_scheduler { activate_internal(activation) }
         | 
| 167 | 
            -
                    end
         | 
| 168 | 
            -
             | 
| 169 | 
            -
                    def add_command(command)
         | 
| 170 | 
            -
                      raise Workflow::InvalidWorkflowStateError, 'Cannot add commands in this context' if @context_frozen
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                      @commands << command
         | 
| 173 | 
            -
                    end
         | 
| 174 | 
            -
             | 
| 175 | 
            -
                    def instance
         | 
| 176 | 
            -
                      @instance or raise 'Instance accessed before created'
         | 
| 177 | 
            -
                    end
         | 
| 178 | 
            -
             | 
| 179 | 
            -
                    def search_attributes
         | 
| 180 | 
            -
                      # Lazy on first access
         | 
| 181 | 
            -
                      @search_attributes ||= SearchAttributes._from_proto(
         | 
| 182 | 
            -
                        @init_job.search_attributes, disable_mutations: true, never_nil: true
         | 
| 183 | 
            -
                      ) || raise
         | 
| 184 | 
            -
                    end
         | 
| 185 | 
            -
             | 
| 186 | 
            -
                    def memo
         | 
| 187 | 
            -
                      # Lazy on first access
         | 
| 188 | 
            -
                      @memo ||= ExternallyImmutableHash.new(ProtoUtils.memo_from_proto(@init_job.memo, payload_converter) || {})
         | 
| 189 | 
            -
                    end
         | 
| 190 | 
            -
             | 
| 191 | 
            -
                    def now
         | 
| 192 | 
            -
                      # Create each time
         | 
| 193 | 
            -
                      ProtoUtils.timestamp_to_time(@now_timestamp) or raise 'Time unexpectedly not present'
         | 
| 194 | 
            -
                    end
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                    def illegal_call_tracing_disabled(&)
         | 
| 197 | 
            -
                      @tracer.disable(&)
         | 
| 198 | 
            -
                    end
         | 
| 199 | 
            -
             | 
| 200 | 
            -
                    def patch(patch_id:, deprecated:)
         | 
| 201 | 
            -
                      # Use memoized result if present. If this is being deprecated, we can still use memoized result and skip the
         | 
| 202 | 
            -
                      # command.
         | 
| 203 | 
            -
                      patch_id = patch_id.to_s
         | 
| 204 | 
            -
                      @patches_memoized ||= {}
         | 
| 205 | 
            -
                      @patches_memoized.fetch(patch_id) do
         | 
| 206 | 
            -
                        patched = !replaying || @patches_notified.include?(patch_id)
         | 
| 207 | 
            -
                        @patches_memoized[patch_id] = patched
         | 
| 208 | 
            -
                        if patched
         | 
| 209 | 
            -
                          add_command(
         | 
| 210 | 
            -
                            Bridge::Api::WorkflowCommands::WorkflowCommand.new(
         | 
| 211 | 
            -
                              set_patch_marker: Bridge::Api::WorkflowCommands::SetPatchMarker.new(patch_id:, deprecated:)
         | 
| 212 | 
            -
                            )
         | 
| 213 | 
            -
                          )
         | 
| 214 | 
            -
                        end
         | 
| 215 | 
            -
                        patched
         | 
| 216 | 
            -
                      end
         | 
| 217 | 
            -
                    end
         | 
| 218 | 
            -
             | 
| 219 | 
            -
                    def metric_meter
         | 
| 220 | 
            -
                      @metric_meter ||= ReplaySafeMetric::Meter.new(
         | 
| 221 | 
            -
                        @runtime_metric_meter.with_additional_attributes(
         | 
| 222 | 
            -
                          {
         | 
| 223 | 
            -
                            namespace: info.namespace,
         | 
| 224 | 
            -
                            task_queue: info.task_queue,
         | 
| 225 | 
            -
                            workflow_type: info.workflow_type
         | 
| 226 | 
            -
                          }
         | 
| 227 | 
            -
                        )
         | 
| 228 | 
            -
                      )
         | 
| 229 | 
            -
                    end
         | 
| 230 | 
            -
             | 
| 231 | 
            -
                    private
         | 
| 232 | 
            -
             | 
| 233 | 
            -
                    def run_in_scheduler(&)
         | 
| 166 | 
            +
                      # Run inside of scheduler (removed on ensure)
         | 
| 234 167 | 
             
                      Fiber.set_scheduler(@scheduler)
         | 
| 235 | 
            -
                      if @tracer
         | 
| 236 | 
            -
                        @tracer.enable(&)
         | 
| 237 | 
            -
                      else
         | 
| 238 | 
            -
                        yield
         | 
| 239 | 
            -
                      end
         | 
| 240 | 
            -
                    ensure
         | 
| 241 | 
            -
                      Fiber.set_scheduler(nil)
         | 
| 242 | 
            -
                    end
         | 
| 243 168 |  | 
| 244 | 
            -
                    def activate_internal(activation)
         | 
| 245 169 | 
             
                      # Reset some activation state
         | 
| 246 170 | 
             
                      @commands = []
         | 
| 247 171 | 
             
                      @current_activation_error = nil
         | 
| @@ -266,8 +190,12 @@ module Temporalio | |
| 266 190 | 
             
                        # the first activation)
         | 
| 267 191 | 
             
                        @primary_fiber ||= schedule(top_level: true) { run_workflow }
         | 
| 268 192 |  | 
| 269 | 
            -
                        # Run the event loop
         | 
| 270 | 
            -
                        @ | 
| 193 | 
            +
                        # Run the event loop in the tracer if it exists
         | 
| 194 | 
            +
                        if @tracer
         | 
| 195 | 
            +
                          @tracer.enable { @scheduler.run_until_all_yielded }
         | 
| 196 | 
            +
                        else
         | 
| 197 | 
            +
                          @scheduler.run_until_all_yielded
         | 
| 198 | 
            +
                        end
         | 
| 271 199 | 
             
                      rescue Exception => e # rubocop:disable Lint/RescueException
         | 
| 272 200 | 
             
                        on_top_level_exception(e)
         | 
| 273 201 | 
             
                      end
         | 
| @@ -306,8 +234,77 @@ module Temporalio | |
| 306 234 | 
             
                    ensure
         | 
| 307 235 | 
             
                      @commands = nil
         | 
| 308 236 | 
             
                      @current_activation_error = nil
         | 
| 237 | 
            +
                      Fiber.set_scheduler(nil)
         | 
| 238 | 
            +
                    end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                    def add_command(command)
         | 
| 241 | 
            +
                      raise Workflow::InvalidWorkflowStateError, 'Cannot add commands in this context' if @context_frozen
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                      @commands << command
         | 
| 244 | 
            +
                    end
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                    def instance
         | 
| 247 | 
            +
                      @instance or raise 'Instance accessed before created'
         | 
| 248 | 
            +
                    end
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                    def search_attributes
         | 
| 251 | 
            +
                      # Lazy on first access
         | 
| 252 | 
            +
                      @search_attributes ||= SearchAttributes._from_proto(
         | 
| 253 | 
            +
                        @init_job.search_attributes, disable_mutations: true, never_nil: true
         | 
| 254 | 
            +
                      ) || raise
         | 
| 255 | 
            +
                    end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                    def memo
         | 
| 258 | 
            +
                      # Lazy on first access
         | 
| 259 | 
            +
                      @memo ||= ExternallyImmutableHash.new(ProtoUtils.memo_from_proto(@init_job.memo, payload_converter) || {})
         | 
| 260 | 
            +
                    end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                    def now
         | 
| 263 | 
            +
                      # Create each time
         | 
| 264 | 
            +
                      ProtoUtils.timestamp_to_time(@now_timestamp) or raise 'Time unexpectedly not present'
         | 
| 309 265 | 
             
                    end
         | 
| 310 266 |  | 
| 267 | 
            +
                    def illegal_call_tracing_disabled(&)
         | 
| 268 | 
            +
                      if @tracer
         | 
| 269 | 
            +
                        @tracer.disable_temporarily(&)
         | 
| 270 | 
            +
                      else
         | 
| 271 | 
            +
                        yield
         | 
| 272 | 
            +
                      end
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                    def patch(patch_id:, deprecated:)
         | 
| 276 | 
            +
                      # Use memoized result if present. If this is being deprecated, we can still use memoized result and skip the
         | 
| 277 | 
            +
                      # command.
         | 
| 278 | 
            +
                      patch_id = patch_id.to_s
         | 
| 279 | 
            +
                      @patches_memoized ||= {}
         | 
| 280 | 
            +
                      @patches_memoized.fetch(patch_id) do
         | 
| 281 | 
            +
                        patched = !replaying || @patches_notified.include?(patch_id)
         | 
| 282 | 
            +
                        @patches_memoized[patch_id] = patched
         | 
| 283 | 
            +
                        if patched
         | 
| 284 | 
            +
                          add_command(
         | 
| 285 | 
            +
                            Bridge::Api::WorkflowCommands::WorkflowCommand.new(
         | 
| 286 | 
            +
                              set_patch_marker: Bridge::Api::WorkflowCommands::SetPatchMarker.new(patch_id:, deprecated:)
         | 
| 287 | 
            +
                            )
         | 
| 288 | 
            +
                          )
         | 
| 289 | 
            +
                        end
         | 
| 290 | 
            +
                        patched
         | 
| 291 | 
            +
                      end
         | 
| 292 | 
            +
                    end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                    def metric_meter
         | 
| 295 | 
            +
                      @metric_meter ||= ReplaySafeMetric::Meter.new(
         | 
| 296 | 
            +
                        @runtime_metric_meter.with_additional_attributes(
         | 
| 297 | 
            +
                          {
         | 
| 298 | 
            +
                            namespace: info.namespace,
         | 
| 299 | 
            +
                            task_queue: info.task_queue,
         | 
| 300 | 
            +
                            workflow_type: info.workflow_type
         | 
| 301 | 
            +
                          }
         | 
| 302 | 
            +
                        )
         | 
| 303 | 
            +
                      )
         | 
| 304 | 
            +
                    end
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                    private
         | 
| 307 | 
            +
             | 
| 311 308 | 
             
                    def create_instance
         | 
| 312 309 | 
             
                      # Convert workflow arguments
         | 
| 313 310 | 
             
                      @workflow_arguments = convert_args(payload_array: @init_job.arguments,
         | 
    
        data/lib/temporalio/priority.rb
    CHANGED
    
    | @@ -4,7 +4,9 @@ require 'temporalio/api' | |
| 4 4 |  | 
| 5 5 | 
             
            module Temporalio
         | 
| 6 6 | 
             
              Priority = Data.define(
         | 
| 7 | 
            -
                :priority_key
         | 
| 7 | 
            +
                :priority_key,
         | 
| 8 | 
            +
                :fairness_key,
         | 
| 9 | 
            +
                :fairness_weight
         | 
| 8 10 | 
             
              )
         | 
| 9 11 |  | 
| 10 12 | 
             
              # Priority contains metadata that controls relative ordering of task processing when tasks are
         | 
| @@ -29,31 +31,70 @@ module Temporalio | |
| 29 31 | 
             
              #
         | 
| 30 32 | 
             
              #     The default priority is (min+max)/2. With the default max of 5 and min of 1, that comes
         | 
| 31 33 | 
             
              #     out to 3.
         | 
| 34 | 
            +
              #
         | 
| 35 | 
            +
              # @!attribute fairness_key
         | 
| 36 | 
            +
              #   @return [String, nil] FairnessKey is a short string that's used as a key for a fairness
         | 
| 37 | 
            +
              #     balancing mechanism. It may correspond to a tenant id, or to a fixed
         | 
| 38 | 
            +
              #     string like "high" or "low". The default is the empty string.
         | 
| 39 | 
            +
              #
         | 
| 40 | 
            +
              #     The fairness mechanism attempts to dispatch tasks for a given key in
         | 
| 41 | 
            +
              #     proportion to its weight. For example, using a thousand distinct tenant
         | 
| 42 | 
            +
              #     ids, each with a weight of 1.0 (the default) will result in each tenant
         | 
| 43 | 
            +
              #     getting a roughly equal share of task dispatch throughput.
         | 
| 44 | 
            +
              #
         | 
| 45 | 
            +
              #     Fairness keys are limited to 64 bytes.
         | 
| 46 | 
            +
              #
         | 
| 47 | 
            +
              # @!attribute fairness_weight
         | 
| 48 | 
            +
              #   @return [Float, nil] Weight for a task can come from multiple sources for
         | 
| 49 | 
            +
              #     flexibility. From highest to lowest precedence:
         | 
| 50 | 
            +
              #     1. Weights for a small set of keys can be overridden in task queue
         | 
| 51 | 
            +
              #        configuration with an API.
         | 
| 52 | 
            +
              #     2. It can be attached to the workflow/activity in this field.
         | 
| 53 | 
            +
              #     3. The default fairness_weight of 1.0 will be used.
         | 
| 54 | 
            +
              #
         | 
| 55 | 
            +
              #     Weight values are clamped to the range [0.001, 1000].
         | 
| 32 56 | 
             
              class Priority
         | 
| 33 57 | 
             
                # @!visibility private
         | 
| 34 58 | 
             
                def self._from_proto(priority)
         | 
| 35 59 | 
             
                  return default if priority.nil?
         | 
| 36 60 |  | 
| 37 | 
            -
                  new( | 
| 61 | 
            +
                  new(
         | 
| 62 | 
            +
                    priority_key: priority.priority_key.zero? ? nil : priority.priority_key,
         | 
| 63 | 
            +
                    fairness_key: priority.fairness_key.empty? ? nil : priority.fairness_key,
         | 
| 64 | 
            +
                    fairness_weight: priority.fairness_weight.zero? ? nil : priority.fairness_weight
         | 
| 65 | 
            +
                  )
         | 
| 38 66 | 
             
                end
         | 
| 39 67 |  | 
| 40 68 | 
             
                # The default priority instance.
         | 
| 41 69 | 
             
                #
         | 
| 42 70 | 
             
                # @return [Priority] The default priority
         | 
| 43 71 | 
             
                def self.default
         | 
| 44 | 
            -
                  @default ||= new(priority_key: nil)
         | 
| 72 | 
            +
                  @default ||= new(priority_key: nil, fairness_key: nil, fairness_weight: nil)
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                # Initialize a new Priority instance.
         | 
| 76 | 
            +
                #
         | 
| 77 | 
            +
                # @param priority_key [Integer, nil] The priority key
         | 
| 78 | 
            +
                # @param fairness_key [String, nil] The fairness key
         | 
| 79 | 
            +
                # @param fairness_weight [Float, nil] The fairness weight
         | 
| 80 | 
            +
                def initialize(priority_key: nil, fairness_key: nil, fairness_weight: nil)
         | 
| 81 | 
            +
                  super
         | 
| 45 82 | 
             
                end
         | 
| 46 83 |  | 
| 47 84 | 
             
                # @!visibility private
         | 
| 48 85 | 
             
                def _to_proto
         | 
| 49 | 
            -
                  return nil if  | 
| 86 | 
            +
                  return nil if empty?
         | 
| 50 87 |  | 
| 51 | 
            -
                  Temporalio::Api::Common::V1::Priority.new( | 
| 88 | 
            +
                  Temporalio::Api::Common::V1::Priority.new(
         | 
| 89 | 
            +
                    priority_key: priority_key || 0,
         | 
| 90 | 
            +
                    fairness_key: fairness_key || '',
         | 
| 91 | 
            +
                    fairness_weight: fairness_weight || 0.0
         | 
| 92 | 
            +
                  )
         | 
| 52 93 | 
             
                end
         | 
| 53 94 |  | 
| 54 95 | 
             
                # @return [Boolean] True if this priority is empty/default
         | 
| 55 96 | 
             
                def empty?
         | 
| 56 | 
            -
                  priority_key.nil?
         | 
| 97 | 
            +
                  priority_key.nil? && fairness_key.nil? && fairness_weight.nil?
         | 
| 57 98 | 
             
                end
         | 
| 58 99 | 
             
              end
         | 
| 59 100 | 
             
            end
         | 
| @@ -17,7 +17,7 @@ module Temporalio | |
| 17 17 |  | 
| 18 18 | 
             
                # @see Logger.add
         | 
| 19 19 | 
             
                def add(severity, message = nil, progname = nil)
         | 
| 20 | 
            -
                  return true if (severity || Logger:: | 
| 20 | 
            +
                  return true if (severity || Logger::UNKNOWN) < level
         | 
| 21 21 | 
             
                  return super if scoped_values_getter.nil? || @disable_scoped_values
         | 
| 22 22 |  | 
| 23 23 | 
             
                  scoped_values = scoped_values_getter.call
         | 
    
        data/lib/temporalio/version.rb
    CHANGED
    
    
| @@ -39,6 +39,15 @@ module Temporalio | |
| 39 39 | 
             
                    ]
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 | 
            +
                  # @return [IllegalWorkflowCallValidator] Workflow call validator that is tailored to disallow most Mutex calls,
         | 
| 43 | 
            +
                  #   but let others through for certain situations.
         | 
| 44 | 
            +
                  def self.known_safe_mutex_validator
         | 
| 45 | 
            +
                    @known_safe_mutex_validator ||= IllegalWorkflowCallValidator.new do
         | 
| 46 | 
            +
                      # Only Google Protobuf use of Mutex is known to be safe, fail unless any caller location path has protobuf
         | 
| 47 | 
            +
                      raise 'disallowed' unless caller_locations&.any? { |loc| loc.path&.include?('google/protobuf/') }
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 42 51 | 
             
                  # @return [String, nil] Method name if this validator is specific to a method.
         | 
| 43 52 | 
             
                  attr_reader :method_name
         | 
| 44 53 |  |