temporalio 0.6.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/cancellation.rb +2 -2
- data/lib/temporalio/client/async_activity_handle.rb +1 -0
- 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/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 +2 -0
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +17 -10
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +6 -5
- data/lib/temporalio/internal/worker/workflow_instance.rb +77 -80
- data/lib/temporalio/testing/activity_environment.rb +1 -0
- data/lib/temporalio/version.rb +1 -1
- data/lib/temporalio/worker/interceptor.rb +1 -0
- data/lib/temporalio/worker/tuner.rb +185 -16
- data/lib/temporalio/workflow/info.rb +3 -0
- data/lib/temporalio/workflow.rb +6 -3
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ea2b4c5c638f5d2761f476c9241e1f3d26b02aa7d8bb1abd38ad91cd233e6444
         | 
| 4 | 
            +
              data.tar.gz: 9a4aa656faec6cb22cdaf36a4663b29799303a816c137c4a74189d5b59164c3f
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 77d6dd18e40636f613620e24c61ab2ffbb28eb81e05889df6cdf1e34c8c1ed9af55a02aafdda27bacb3408ed290ac33ffd84b0b2b4a764f8441d609a8227235b
         | 
| 7 | 
            +
              data.tar.gz: 4d240d486ec63f522daf7b772205b1514c85b73037824f611bda43781d4e327db03e261ccb4034f384b01c8ae347476bf62efc5dd0f0a2f82a5d937b9983a311
         | 
| @@ -13,6 +13,7 @@ module Temporalio | |
| 13 13 | 
             
                  :heartbeat_timeout,
         | 
| 14 14 | 
             
                  :local?,
         | 
| 15 15 | 
             
                  :priority,
         | 
| 16 | 
            +
                  :retry_policy,
         | 
| 16 17 | 
             
                  :raw_heartbeat_details,
         | 
| 17 18 | 
             
                  :schedule_to_close_timeout,
         | 
| 18 19 | 
             
                  :scheduled_time,
         | 
| @@ -42,6 +43,10 @@ module Temporalio | |
| 42 43 | 
             
                #   @return [Boolean] Whether the activity is a local activity or not.
         | 
| 43 44 | 
             
                # @!attribute priority
         | 
| 44 45 | 
             
                #   @return [Priority] The priority of this activity.
         | 
| 46 | 
            +
                # @!attribute retry_policy
         | 
| 47 | 
            +
                #   @return [RetryPolicy, nil] Retry policy for the activity. Note that the server may have set a different policy
         | 
| 48 | 
            +
                #     than the one provided when scheduling the activity. If the value is None, it means the server didn't send
         | 
| 49 | 
            +
                #     information about retry policy (e.g. due to old server version), but it may still be defined server-side.
         | 
| 45 50 | 
             
                # @!attribute raw_heartbeat_details
         | 
| 46 51 | 
             
                #   @return [Array<Converter::RawValue>] Raw details from the last heartbeat of the last attempt. Can use
         | 
| 47 52 | 
             
                #     {heartbeat_details} to get lazily-converted values.
         | 
| @@ -167,8 +167,8 @@ module Temporalio | |
| 167 167 | 
             
                  to_return.values
         | 
| 168 168 | 
             
                end
         | 
| 169 169 |  | 
| 170 | 
            -
                def canceled_mutex_synchronize(&)
         | 
| 171 | 
            -
                  Workflow::Unsafe.illegal_call_tracing_disabled { @canceled_mutex.synchronize(&) }
         | 
| 170 | 
            +
                def canceled_mutex_synchronize(&block)
         | 
| 171 | 
            +
                  Workflow::Unsafe.illegal_call_tracing_disabled { @canceled_mutex.synchronize(&block) }
         | 
| 172 172 | 
             
                end
         | 
| 173 173 | 
             
              end
         | 
| 174 174 | 
             
            end
         | 
| @@ -29,6 +29,7 @@ module Temporalio | |
| 29 29 | 
             
                  # @param details [Array<Object>] Details of the heartbeat.
         | 
| 30 30 | 
             
                  # @param detail_hints [Array<Object>, nil] Converter hints for the details.
         | 
| 31 31 | 
             
                  # @param rpc_options [RPCOptions, nil] Advanced RPC options.
         | 
| 32 | 
            +
                  # @raise [Error::AsyncActivityCanceledError] If the activity was canceled, paused, and/or reset.
         | 
| 32 33 | 
             
                  def heartbeat(*details, detail_hints: nil, rpc_options: nil)
         | 
| 33 34 | 
             
                    @client._impl.heartbeat_async_activity(Interceptor::HeartbeatAsyncActivityInput.new(
         | 
| 34 35 | 
             
                                                             task_token_or_id_reference:,
         | 
| @@ -0,0 +1,343 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'pathname'
         | 
| 4 | 
            +
            require 'temporalio/internal/bridge'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Temporalio
         | 
| 7 | 
            +
              # Environment and file-based configuration for Temporal clients
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # WARNING: Experimental API.
         | 
| 10 | 
            +
              module EnvConfig
         | 
| 11 | 
            +
                # This module provides utilities to load Temporal client configuration from TOML files
         | 
| 12 | 
            +
                # and environment variables.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # TLS configuration as specified as part of client configuration
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @!attribute [r] disabled
         | 
| 17 | 
            +
                #   @return [Boolean, nil] If true, TLS is explicitly disabled; if nil, not specified
         | 
| 18 | 
            +
                # @!attribute [r] server_name
         | 
| 19 | 
            +
                #   @return [String, nil] SNI override
         | 
| 20 | 
            +
                # @!attribute [r] server_root_ca_cert
         | 
| 21 | 
            +
                #   @return [Pathname, String, nil] Server CA certificate source
         | 
| 22 | 
            +
                # @!attribute [r] client_cert
         | 
| 23 | 
            +
                #   @return [Pathname, String, nil] Client certificate source
         | 
| 24 | 
            +
                # @!attribute [r] client_private_key
         | 
| 25 | 
            +
                #   @return [Pathname, String, nil] Client key source
         | 
| 26 | 
            +
                ClientConfigTLS = Data.define(:disabled, :server_name, :server_root_ca_cert, :client_cert, :client_private_key)
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                # TLS configuration for Temporal client connections.
         | 
| 29 | 
            +
                #
         | 
| 30 | 
            +
                # This class provides methods for creating, serializing, and converting
         | 
| 31 | 
            +
                # TLS configuration objects used by Temporal clients.
         | 
| 32 | 
            +
                class ClientConfigTLS
         | 
| 33 | 
            +
                  # Create a ClientConfigTLS from a hash
         | 
| 34 | 
            +
                  # @param hash [Hash, nil] Hash representation
         | 
| 35 | 
            +
                  # @return [ClientConfigTLS, nil] The TLS configuration or nil if hash is nil/empty
         | 
| 36 | 
            +
                  def self.from_h(hash)
         | 
| 37 | 
            +
                    return nil if hash.nil? || hash.empty?
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    new(
         | 
| 40 | 
            +
                      disabled: hash[:disabled],
         | 
| 41 | 
            +
                      server_name: hash[:server_name],
         | 
| 42 | 
            +
                      server_root_ca_cert: hash_to_source(hash[:server_ca_cert]),
         | 
| 43 | 
            +
                      client_cert: hash_to_source(hash[:client_cert]),
         | 
| 44 | 
            +
                      client_private_key: hash_to_source(hash[:client_key])
         | 
| 45 | 
            +
                    )
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  # Set default values
         | 
| 49 | 
            +
                  def initialize(disabled: nil, server_name: nil, server_root_ca_cert: nil, client_cert: nil,
         | 
| 50 | 
            +
                                 client_private_key: nil)
         | 
| 51 | 
            +
                    super
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  # Convert to a hash that can be used for TOML serialization
         | 
| 55 | 
            +
                  # @return [Hash] Dictionary representation
         | 
| 56 | 
            +
                  def to_h
         | 
| 57 | 
            +
                    {
         | 
| 58 | 
            +
                      disabled:,
         | 
| 59 | 
            +
                      server_name:,
         | 
| 60 | 
            +
                      server_ca_cert: server_root_ca_cert ? source_to_hash(server_root_ca_cert) : nil,
         | 
| 61 | 
            +
                      client_cert: client_cert ? source_to_hash(client_cert) : nil,
         | 
| 62 | 
            +
                      client_key: client_private_key ? source_to_hash(client_private_key) : nil
         | 
| 63 | 
            +
                    }.compact
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # Create a TLS configuration for use with connections
         | 
| 67 | 
            +
                  # @return [Connection::TLSOptions, false] TLS options or false if disabled
         | 
| 68 | 
            +
                  def to_client_tls_options
         | 
| 69 | 
            +
                    return false if disabled
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    Client::Connection::TLSOptions.new(
         | 
| 72 | 
            +
                      domain: server_name,
         | 
| 73 | 
            +
                      server_root_ca_cert: read_source(server_root_ca_cert),
         | 
| 74 | 
            +
                      client_cert: read_source(client_cert),
         | 
| 75 | 
            +
                      client_private_key: read_source(client_private_key)
         | 
| 76 | 
            +
                    )
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  private
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  class << self
         | 
| 82 | 
            +
                    private
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    # Convert hash to source object (Pathname or String)
         | 
| 85 | 
            +
                    def hash_to_source(hash)
         | 
| 86 | 
            +
                      return nil if hash.nil?
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      # Always expect a hash with path or data
         | 
| 89 | 
            +
                      if hash[:path]
         | 
| 90 | 
            +
                        Pathname.new(hash[:path])
         | 
| 91 | 
            +
                      elsif hash[:data]
         | 
| 92 | 
            +
                        hash[:data]
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  def source_to_hash(source)
         | 
| 98 | 
            +
                    case source
         | 
| 99 | 
            +
                    when Pathname
         | 
| 100 | 
            +
                      { path: source.to_s }
         | 
| 101 | 
            +
                    when String
         | 
| 102 | 
            +
                      # String is always treated as data content
         | 
| 103 | 
            +
                      { data: source }
         | 
| 104 | 
            +
                    when nil
         | 
| 105 | 
            +
                      nil
         | 
| 106 | 
            +
                    else
         | 
| 107 | 
            +
                      raise TypeError, "Source must be Pathname, String, or nil, got #{source.class}"
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  def read_source(source)
         | 
| 112 | 
            +
                    case source
         | 
| 113 | 
            +
                    when Pathname
         | 
| 114 | 
            +
                      File.read(source.to_s)
         | 
| 115 | 
            +
                    when String
         | 
| 116 | 
            +
                      # String is always treated as raw data content
         | 
| 117 | 
            +
                      source
         | 
| 118 | 
            +
                    when nil
         | 
| 119 | 
            +
                      nil
         | 
| 120 | 
            +
                    else
         | 
| 121 | 
            +
                      raise TypeError, "Source must be Pathname, String, or nil, got #{source.class}"
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                  end
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                # Represents a client configuration profile.
         | 
| 127 | 
            +
                #
         | 
| 128 | 
            +
                # This class holds the configuration as loaded from a file or environment.
         | 
| 129 | 
            +
                # See #to_client_connect_options to transform the profile to a connect config hash.
         | 
| 130 | 
            +
                #
         | 
| 131 | 
            +
                # @!attribute [r] address
         | 
| 132 | 
            +
                #   @return [String, nil] Client address
         | 
| 133 | 
            +
                # @!attribute [r] namespace
         | 
| 134 | 
            +
                #   @return [String, nil] Client namespace
         | 
| 135 | 
            +
                # @!attribute [r] api_key
         | 
| 136 | 
            +
                #   @return [String, nil] Client API key
         | 
| 137 | 
            +
                # @!attribute [r] tls
         | 
| 138 | 
            +
                #   @return [Boolean, ClientConfigTLS, nil] TLS configuration
         | 
| 139 | 
            +
                # @!attribute [r] grpc_meta
         | 
| 140 | 
            +
                #   @return [Hash] gRPC metadata
         | 
| 141 | 
            +
                ClientConfigProfile = Data.define(:address, :namespace, :api_key, :tls, :grpc_meta)
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                # A client configuration profile loaded from environment and files.
         | 
| 144 | 
            +
                #
         | 
| 145 | 
            +
                # This class represents a complete client configuration profile that can be
         | 
| 146 | 
            +
                # loaded from TOML files and environment variables, and converted to client
         | 
| 147 | 
            +
                # connection options.
         | 
| 148 | 
            +
                class ClientConfigProfile
         | 
| 149 | 
            +
                  # Create a ClientConfigProfile from a hash
         | 
| 150 | 
            +
                  # @param hash [Hash] Hash representation
         | 
| 151 | 
            +
                  # @return [ClientConfigProfile] The client profile
         | 
| 152 | 
            +
                  def self.from_h(hash)
         | 
| 153 | 
            +
                    new(
         | 
| 154 | 
            +
                      address: hash[:address],
         | 
| 155 | 
            +
                      namespace: hash[:namespace],
         | 
| 156 | 
            +
                      api_key: hash[:api_key],
         | 
| 157 | 
            +
                      tls: ClientConfigTLS.from_h(hash[:tls]),
         | 
| 158 | 
            +
                      grpc_meta: hash[:grpc_meta] || {}
         | 
| 159 | 
            +
                    )
         | 
| 160 | 
            +
                  end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                  # Load a single client profile from given sources, applying env overrides.
         | 
| 163 | 
            +
                  #
         | 
| 164 | 
            +
                  # @param profile [String, nil] Profile to load from the config
         | 
| 165 | 
            +
                  # @param config_source [Pathname, String, nil] Configuration source -
         | 
| 166 | 
            +
                  #   Pathname for file path, String for TOML content
         | 
| 167 | 
            +
                  # @param disable_file [Boolean] If true, file loading is disabled
         | 
| 168 | 
            +
                  # @param disable_env [Boolean] If true, environment variable loading and overriding is disabled
         | 
| 169 | 
            +
                  # @param config_file_strict [Boolean] If true, will error on unrecognized keys
         | 
| 170 | 
            +
                  # @param override_env_vars [Hash, nil] Environment variables to use for loading and overrides
         | 
| 171 | 
            +
                  # @return [ClientConfigProfile] The client configuration profile
         | 
| 172 | 
            +
                  def self.load(
         | 
| 173 | 
            +
                    profile: nil,
         | 
| 174 | 
            +
                    config_source: nil,
         | 
| 175 | 
            +
                    disable_file: false,
         | 
| 176 | 
            +
                    disable_env: false,
         | 
| 177 | 
            +
                    config_file_strict: false,
         | 
| 178 | 
            +
                    override_env_vars: nil
         | 
| 179 | 
            +
                  )
         | 
| 180 | 
            +
                    path, data = EnvConfig._source_to_path_and_data(config_source)
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                    raw_profile = Internal::Bridge::EnvConfig.load_client_connect_config(
         | 
| 183 | 
            +
                      profile,
         | 
| 184 | 
            +
                      path,
         | 
| 185 | 
            +
                      data,
         | 
| 186 | 
            +
                      disable_file,
         | 
| 187 | 
            +
                      disable_env,
         | 
| 188 | 
            +
                      config_file_strict,
         | 
| 189 | 
            +
                      override_env_vars || {}
         | 
| 190 | 
            +
                    )
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                    from_h(raw_profile)
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  # Create a ClientConfigProfile instance with defaults
         | 
| 196 | 
            +
                  def initialize(address: nil, namespace: nil, api_key: nil, tls: nil, grpc_meta: {})
         | 
| 197 | 
            +
                    super
         | 
| 198 | 
            +
                  end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                  # Convert to a hash that can be used for TOML serialization
         | 
| 201 | 
            +
                  # @return [Hash] Dictionary representation
         | 
| 202 | 
            +
                  def to_h
         | 
| 203 | 
            +
                    {
         | 
| 204 | 
            +
                      address: address,
         | 
| 205 | 
            +
                      namespace: namespace,
         | 
| 206 | 
            +
                      api_key: api_key,
         | 
| 207 | 
            +
                      tls: tls&.to_h&.then { |tls_hash| tls_hash.empty? ? nil : tls_hash }, # steep:ignore
         | 
| 208 | 
            +
                      grpc_meta: grpc_meta && grpc_meta.empty? ? nil : grpc_meta
         | 
| 209 | 
            +
                    }.compact
         | 
| 210 | 
            +
                  end
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                  # Create a client connect config from this profile
         | 
| 213 | 
            +
                  # @return [Array] Tuple of [positional_args, keyword_args] that can be splatted to Client.connect
         | 
| 214 | 
            +
                  def to_client_connect_options
         | 
| 215 | 
            +
                    positional_args = [address, namespace].compact
         | 
| 216 | 
            +
                    tls_value = false
         | 
| 217 | 
            +
                    if tls
         | 
| 218 | 
            +
                      tls_value = tls.to_client_tls_options
         | 
| 219 | 
            +
                    elsif api_key
         | 
| 220 | 
            +
                      tls_value = true
         | 
| 221 | 
            +
                    end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                    keyword_args = {
         | 
| 224 | 
            +
                      api_key: api_key,
         | 
| 225 | 
            +
                      rpc_metadata: (grpc_meta if grpc_meta && !grpc_meta.empty?),
         | 
| 226 | 
            +
                      tls: tls_value
         | 
| 227 | 
            +
                    }.compact
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                    [positional_args, keyword_args]
         | 
| 230 | 
            +
                  end
         | 
| 231 | 
            +
                end
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                # Client configuration loaded from TOML and environment variables.
         | 
| 234 | 
            +
                #
         | 
| 235 | 
            +
                # This contains a mapping of profile names to client profiles.
         | 
| 236 | 
            +
                #
         | 
| 237 | 
            +
                # @!attribute [r] profiles
         | 
| 238 | 
            +
                #   @return [Hash<String, ClientConfigProfile>] Map of profile name to its corresponding ClientConfigProfile
         | 
| 239 | 
            +
                ClientConfig = Data.define(:profiles)
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                # Container for multiple client configuration profiles.
         | 
| 242 | 
            +
                #
         | 
| 243 | 
            +
                # This class holds a collection of named client profiles loaded from
         | 
| 244 | 
            +
                # configuration sources and provides methods for profile management
         | 
| 245 | 
            +
                # and client connection configuration.
         | 
| 246 | 
            +
                class ClientConfig
         | 
| 247 | 
            +
                  # Create a ClientConfig from a hash
         | 
| 248 | 
            +
                  # @param hash [Hash] Hash representation
         | 
| 249 | 
            +
                  # @return [ClientConfig] The client configuration
         | 
| 250 | 
            +
                  def self.from_h(hash)
         | 
| 251 | 
            +
                    profiles = hash.transform_values do |profile_hash|
         | 
| 252 | 
            +
                      ClientConfigProfile.from_h(profile_hash)
         | 
| 253 | 
            +
                    end
         | 
| 254 | 
            +
                    new(profiles: profiles)
         | 
| 255 | 
            +
                  end
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                  # Load all client profiles from given sources.
         | 
| 258 | 
            +
                  #
         | 
| 259 | 
            +
                  # This does not apply environment variable overrides to the profiles, it
         | 
| 260 | 
            +
                  # only uses an environment variable to find the default config file path
         | 
| 261 | 
            +
                  # (TEMPORAL_CONFIG_FILE).
         | 
| 262 | 
            +
                  #
         | 
| 263 | 
            +
                  # @param config_source [Pathname, String, nil] Configuration source
         | 
| 264 | 
            +
                  # @param config_file_strict [Boolean] If true, will error on unrecognized keys
         | 
| 265 | 
            +
                  # @param override_env_vars [Hash, nil] Environment variables to use
         | 
| 266 | 
            +
                  # @return [ClientConfig] The client configuration
         | 
| 267 | 
            +
                  def self.load(
         | 
| 268 | 
            +
                    config_source: nil,
         | 
| 269 | 
            +
                    config_file_strict: false,
         | 
| 270 | 
            +
                    override_env_vars: nil
         | 
| 271 | 
            +
                  )
         | 
| 272 | 
            +
                    path, data = EnvConfig._source_to_path_and_data(config_source)
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                    loaded_profiles = Internal::Bridge::EnvConfig.load_client_config(
         | 
| 275 | 
            +
                      path,
         | 
| 276 | 
            +
                      data,
         | 
| 277 | 
            +
                      config_file_strict,
         | 
| 278 | 
            +
                      override_env_vars || {}
         | 
| 279 | 
            +
                    )
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                    from_h(loaded_profiles)
         | 
| 282 | 
            +
                  end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                  # Load a single client profile and convert to connect config
         | 
| 285 | 
            +
                  #
         | 
| 286 | 
            +
                  # This is a convenience function that combines loading a profile and
         | 
| 287 | 
            +
                  # converting it to a connect config hash.
         | 
| 288 | 
            +
                  #
         | 
| 289 | 
            +
                  # @param profile [String, nil] The profile to load from the config
         | 
| 290 | 
            +
                  # @param config_source [Pathname, String, nil] Configuration source
         | 
| 291 | 
            +
                  # @param disable_file [Boolean] If true, file loading is disabled
         | 
| 292 | 
            +
                  # @param disable_env [Boolean] If true, environment variable loading and overriding is disabled
         | 
| 293 | 
            +
                  # @param config_file_strict [Boolean] If true, will error on unrecognized keys
         | 
| 294 | 
            +
                  # @param override_env_vars [Hash, nil] Environment variables to use for loading and overrides
         | 
| 295 | 
            +
                  # @return [Array] Tuple of [positional_args, keyword_args] that can be splatted to Client.connect
         | 
| 296 | 
            +
                  def self.load_client_connect_options(
         | 
| 297 | 
            +
                    profile: nil,
         | 
| 298 | 
            +
                    config_source: nil,
         | 
| 299 | 
            +
                    disable_file: false,
         | 
| 300 | 
            +
                    disable_env: false,
         | 
| 301 | 
            +
                    config_file_strict: false,
         | 
| 302 | 
            +
                    override_env_vars: nil
         | 
| 303 | 
            +
                  )
         | 
| 304 | 
            +
                    prof = ClientConfigProfile.load(
         | 
| 305 | 
            +
                      profile: profile,
         | 
| 306 | 
            +
                      config_source: config_source,
         | 
| 307 | 
            +
                      disable_file: disable_file,
         | 
| 308 | 
            +
                      disable_env: disable_env,
         | 
| 309 | 
            +
                      config_file_strict: config_file_strict,
         | 
| 310 | 
            +
                      override_env_vars: override_env_vars
         | 
| 311 | 
            +
                    )
         | 
| 312 | 
            +
                    prof.to_client_connect_options
         | 
| 313 | 
            +
                  end
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                  # Create a ClientConfig instance with defaults
         | 
| 316 | 
            +
                  def initialize(profiles: {})
         | 
| 317 | 
            +
                    super
         | 
| 318 | 
            +
                  end
         | 
| 319 | 
            +
             | 
| 320 | 
            +
                  # Convert to a hash that can be used for TOML serialization
         | 
| 321 | 
            +
                  # @return [Hash] Dictionary representation
         | 
| 322 | 
            +
                  def to_h
         | 
| 323 | 
            +
                    profiles.transform_values(&:to_h)
         | 
| 324 | 
            +
                  end
         | 
| 325 | 
            +
                end
         | 
| 326 | 
            +
             | 
| 327 | 
            +
                # @param source [Pathname, String, nil] Configuration source
         | 
| 328 | 
            +
                # @return [Array<String?, String?>] Tuple of [path, data]
         | 
| 329 | 
            +
                # @!visibility private
         | 
| 330 | 
            +
                def self._source_to_path_and_data(source)
         | 
| 331 | 
            +
                  case source
         | 
| 332 | 
            +
                  when Pathname
         | 
| 333 | 
            +
                    [source.to_s, nil]
         | 
| 334 | 
            +
                  when String
         | 
| 335 | 
            +
                    [nil, source]
         | 
| 336 | 
            +
                  when nil
         | 
| 337 | 
            +
                    [nil, nil]
         | 
| 338 | 
            +
                  else
         | 
| 339 | 
            +
                    raise TypeError, "Must be Pathname, String, or nil, got #{source.class}"
         | 
| 340 | 
            +
                  end
         | 
| 341 | 
            +
                end
         | 
| 342 | 
            +
              end
         | 
| 343 | 
            +
            end
         | 
    
        data/lib/temporalio/error.rb
    CHANGED
    
    | @@ -97,9 +97,13 @@ module Temporalio | |
| 97 97 |  | 
| 98 98 | 
             
                # Error that occurs when an async activity handle tries to heartbeat and the activity is marked as canceled.
         | 
| 99 99 | 
             
                class AsyncActivityCanceledError < Error
         | 
| 100 | 
            +
                  # @return [Activity::CancellationDetails]
         | 
| 101 | 
            +
                  attr_reader :details
         | 
| 102 | 
            +
             | 
| 100 103 | 
             
                  # @!visibility private
         | 
| 101 | 
            -
                  def initialize
         | 
| 104 | 
            +
                  def initialize(details)
         | 
| 102 105 | 
             
                    super('Activity canceled')
         | 
| 106 | 
            +
                    @details = details
         | 
| 103 107 | 
             
                  end
         | 
| 104 108 | 
             
                end
         | 
| 105 109 |  | 
| Binary file | 
| Binary file | 
| Binary file | 
| @@ -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),
         | 
| @@ -130,6 +130,7 @@ module Temporalio | |
| 130 130 | 
             
                      def execute_local_activity(
         | 
| 131 131 | 
             
                        activity,
         | 
| 132 132 | 
             
                        *args,
         | 
| 133 | 
            +
                        summary:,
         | 
| 133 134 | 
             
                        schedule_to_close_timeout:,
         | 
| 134 135 | 
             
                        schedule_to_start_timeout:,
         | 
| 135 136 | 
             
                        start_to_close_timeout:,
         | 
| @@ -157,6 +158,7 @@ module Temporalio | |
| 157 158 | 
             
                          Temporalio::Worker::Interceptor::Workflow::ExecuteLocalActivityInput.new(
         | 
| 158 159 | 
             
                            activity:,
         | 
| 159 160 | 
             
                            args:,
         | 
| 161 | 
            +
                            summary:,
         | 
| 160 162 | 
             
                            schedule_to_close_timeout:,
         | 
| 161 163 | 
             
                            schedule_to_start_timeout:,
         | 
| 162 164 | 
             
                            start_to_close_timeout:,
         | 
| @@ -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
         | 
| @@ -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/version.rb
    CHANGED
    
    
| @@ -22,10 +22,11 @@ module Temporalio | |
| 22 22 | 
             
                      end
         | 
| 23 23 |  | 
| 24 24 | 
             
                      # @!visibility private
         | 
| 25 | 
            -
                      def _to_bridge_options
         | 
| 25 | 
            +
                      def _to_bridge_options(_tuner)
         | 
| 26 26 | 
             
                        Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
         | 
| 27 27 | 
             
                          fixed_size: slots,
         | 
| 28 | 
            -
                          resource_based: nil
         | 
| 28 | 
            +
                          resource_based: nil,
         | 
| 29 | 
            +
                          custom: nil
         | 
| 29 30 | 
             
                        )
         | 
| 30 31 | 
             
                      end
         | 
| 31 32 | 
             
                    end
         | 
| @@ -36,7 +37,7 @@ module Temporalio | |
| 36 37 | 
             
                    class ResourceBased < SlotSupplier
         | 
| 37 38 | 
             
                      attr_reader :tuner_options, :slot_options
         | 
| 38 39 |  | 
| 39 | 
            -
                      # Create a  | 
| 40 | 
            +
                      # Create a resource-based slot supplier.
         | 
| 40 41 | 
             
                      #
         | 
| 41 42 | 
             
                      # @param tuner_options [ResourceBasedTunerOptions] General tuner options.
         | 
| 42 43 | 
             
                      # @param slot_options [ResourceBasedSlotOptions] Slot-supplier-specific tuner options.
         | 
| @@ -46,7 +47,7 @@ module Temporalio | |
| 46 47 | 
             
                      end
         | 
| 47 48 |  | 
| 48 49 | 
             
                      # @!visibility private
         | 
| 49 | 
            -
                      def _to_bridge_options
         | 
| 50 | 
            +
                      def _to_bridge_options(_tuner)
         | 
| 50 51 | 
             
                        Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
         | 
| 51 52 | 
             
                          fixed_size: nil,
         | 
| 52 53 | 
             
                          resource_based: Internal::Bridge::Worker::TunerResourceBasedSlotSupplierOptions.new(
         | 
| @@ -55,14 +56,175 @@ module Temporalio | |
| 55 56 | 
             
                            min_slots: slot_options.min_slots,
         | 
| 56 57 | 
             
                            max_slots: slot_options.max_slots,
         | 
| 57 58 | 
             
                            ramp_throttle: slot_options.ramp_throttle
         | 
| 59 | 
            +
                          ),
         | 
| 60 | 
            +
                          custom: nil
         | 
| 61 | 
            +
                        )
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    # A slot supplier that has callbacks invoked to handle slot supplying.
         | 
| 66 | 
            +
                    #
         | 
| 67 | 
            +
                    # Users should be cautious when implementing this and make sure it is heavily tested and the documentation for
         | 
| 68 | 
            +
                    # every method is well understood.
         | 
| 69 | 
            +
                    #
         | 
| 70 | 
            +
                    # @note WARNING: This API is experimental.
         | 
| 71 | 
            +
                    class Custom < SlotSupplier
         | 
| 72 | 
            +
                      # Context provided for slot reservation on custom slot supplier.
         | 
| 73 | 
            +
                      #
         | 
| 74 | 
            +
                      # @!attribute slot_type
         | 
| 75 | 
            +
                      #   @return [:workflow, :activity, :local_activity, :nexus] Slot type.
         | 
| 76 | 
            +
                      # @!attribute task_queue
         | 
| 77 | 
            +
                      #   @return [String] Task queue.
         | 
| 78 | 
            +
                      # @!attribute worker_identity
         | 
| 79 | 
            +
                      #   @return [String] Worker identity.
         | 
| 80 | 
            +
                      # @!attribute worker_deployment_name
         | 
| 81 | 
            +
                      #   @return [String] Worker deployment name or empty string if not applicable.
         | 
| 82 | 
            +
                      # @!attribute worker_build_id
         | 
| 83 | 
            +
                      #   @return [String] Worker build ID or empty string if not applicable.
         | 
| 84 | 
            +
                      # @!attribute sticky?
         | 
| 85 | 
            +
                      #   @return [Boolean] True if this reservation is for a sticky workflow task.
         | 
| 86 | 
            +
                      ReserveContext = Data.define(
         | 
| 87 | 
            +
                        :slot_type,
         | 
| 88 | 
            +
                        :task_queue,
         | 
| 89 | 
            +
                        :worker_identity,
         | 
| 90 | 
            +
                        :worker_deployment_name,
         | 
| 91 | 
            +
                        :worker_build_id,
         | 
| 92 | 
            +
                        :sticky?
         | 
| 93 | 
            +
                      )
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      # Context provided for marking a slot used.
         | 
| 96 | 
            +
                      #
         | 
| 97 | 
            +
                      # @!attribute slot_info
         | 
| 98 | 
            +
                      #   @return [SlotInfo::Workflow, SlotInfo::Activity, SlotInfo::LocalActivity, SlotInfo::Nexus] Information
         | 
| 99 | 
            +
                      #     about the slot. This is never nil.
         | 
| 100 | 
            +
                      # @!attribute permit
         | 
| 101 | 
            +
                      #   @return [Object] Object that was provided as the permit on reserve.
         | 
| 102 | 
            +
                      MarkUsedContext = Data.define(
         | 
| 103 | 
            +
                        :slot_info,
         | 
| 104 | 
            +
                        :permit
         | 
| 105 | 
            +
                      )
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                      # Context provided for releasing a slot.
         | 
| 108 | 
            +
                      #
         | 
| 109 | 
            +
                      # @!attribute slot_info
         | 
| 110 | 
            +
                      #   @return [SlotInfo::Workflow, SlotInfo::Activity, SlotInfo::LocalActivity, SlotInfo::Nexus, nil]
         | 
| 111 | 
            +
                      #     Information about the slot. This may be nil if the slot was never used.
         | 
| 112 | 
            +
                      # @!attribute permit
         | 
| 113 | 
            +
                      #   @return [Object] Object that was provided as the permit on reserve.
         | 
| 114 | 
            +
                      ReleaseContext = Data.define(
         | 
| 115 | 
            +
                        :slot_info,
         | 
| 116 | 
            +
                        :permit
         | 
| 117 | 
            +
                      )
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      # Reserve a slot.
         | 
| 120 | 
            +
                      #
         | 
| 121 | 
            +
                      # This can/should block and must provide the permit to the (code) block. The permit is any object (including
         | 
| 122 | 
            +
                      # nil) that will be given on the context for mark_slot_used and release_slot.
         | 
| 123 | 
            +
                      #
         | 
| 124 | 
            +
                      # Just returning from this call is not enough to reserve the slot, a permit must be provided to the block
         | 
| 125 | 
            +
                      # (e.g. via yield or block.call). If the call completes, the system will still wait on the block (so this can
         | 
| 126 | 
            +
                      # be backgrounded by passing the block to something else). Reservations may be canceled via the given
         | 
| 127 | 
            +
                      # cancellation. Users can do things like add_cancel_callback, but it is very important that the code in the
         | 
| 128 | 
            +
                      # callback is fast as it is run on the same reactor thread as many other Temporal Ruby worker calls.
         | 
| 129 | 
            +
                      #
         | 
| 130 | 
            +
                      # @note WARNING: This call should never raise an exception. Any exception raised is ignored and this is called
         | 
| 131 | 
            +
                      #   again after 1 second.
         | 
| 132 | 
            +
                      #
         | 
| 133 | 
            +
                      # @param context [ReserveContext] Contextual information about this reserve call.
         | 
| 134 | 
            +
                      # @param cancellation [Cancellation] Cancellation that is canceled when the reservation is no longer being
         | 
| 135 | 
            +
                      #   asked for.
         | 
| 136 | 
            +
                      # @yield [Object] Confirm reservation and provide a permit.
         | 
| 137 | 
            +
                      def reserve_slot(context, cancellation, &)
         | 
| 138 | 
            +
                        raise NotImplementedError
         | 
| 139 | 
            +
                      end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                      # Try to reserve a slot.
         | 
| 142 | 
            +
                      #
         | 
| 143 | 
            +
                      # @note WARNING: This should never block, this should return immediately with a permit, or nil if the
         | 
| 144 | 
            +
                      #   reservation could not occur.
         | 
| 145 | 
            +
                      #
         | 
| 146 | 
            +
                      # @note WARNING: This call should never raise an exception. Any exception raised is ignored and the slot
         | 
| 147 | 
            +
                      #   reservation attempt fails (i.e. same as if this method returned nil).
         | 
| 148 | 
            +
                      #
         | 
| 149 | 
            +
                      # @param context [ReserveContext] Contextual information about this reserve call.
         | 
| 150 | 
            +
                      # @return [Object, nil] A non-nil object to perform the reservation successfully, a nil to fail the
         | 
| 151 | 
            +
                      #   reservation.
         | 
| 152 | 
            +
                      def try_reserve_slot(context)
         | 
| 153 | 
            +
                        raise NotImplementedError
         | 
| 154 | 
            +
                      end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                      # Mark a slot as used.
         | 
| 157 | 
            +
                      #
         | 
| 158 | 
            +
                      # Due to the nature of Temporal polling, slots are reserved before they are used and may never get used. This
         | 
| 159 | 
            +
                      # call is made as just a notification when a slot is actually used.
         | 
| 160 | 
            +
                      #
         | 
| 161 | 
            +
                      # @note WARNING: This should never block, this should return immediately.
         | 
| 162 | 
            +
                      #
         | 
| 163 | 
            +
                      # @note WARNING: This call should never raise an exception. Any exception raised is ignored.
         | 
| 164 | 
            +
                      #
         | 
| 165 | 
            +
                      # @param context [MarkUsedContext] Contextual information about this reserve call.
         | 
| 166 | 
            +
                      def mark_slot_used(context)
         | 
| 167 | 
            +
                        raise NotImplementedError
         | 
| 168 | 
            +
                      end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                      # Release a previously reserved slot.
         | 
| 171 | 
            +
                      #
         | 
| 172 | 
            +
                      # @note WARNING: This should never block, this should return immediately.
         | 
| 173 | 
            +
                      #
         | 
| 174 | 
            +
                      # @note WARNING: This call should never raise an exception. Any exception raised is ignored.
         | 
| 175 | 
            +
                      #
         | 
| 176 | 
            +
                      # @param context [ReleaseContext] Contextual information about this reserve call.
         | 
| 177 | 
            +
                      def release_slot(context)
         | 
| 178 | 
            +
                        raise NotImplementedError
         | 
| 179 | 
            +
                      end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                      # @!visibility private
         | 
| 182 | 
            +
                      def _to_bridge_options(tuner)
         | 
| 183 | 
            +
                        Internal::Bridge::Worker::TunerSlotSupplierOptions.new(
         | 
| 184 | 
            +
                          fixed_size: nil,
         | 
| 185 | 
            +
                          resource_based: nil,
         | 
| 186 | 
            +
                          custom: Internal::Bridge::Worker::CustomSlotSupplier.new(
         | 
| 187 | 
            +
                            slot_supplier: self,
         | 
| 188 | 
            +
                            thread_pool: tuner.custom_slot_supplier_thread_pool
         | 
| 58 189 | 
             
                          )
         | 
| 59 190 | 
             
                        )
         | 
| 60 191 | 
             
                      end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                      # Slot information.
         | 
| 194 | 
            +
                      module SlotInfo
         | 
| 195 | 
            +
                        # Information about a workflow slot.
         | 
| 196 | 
            +
                        #
         | 
| 197 | 
            +
                        # @!attribute workflow_type
         | 
| 198 | 
            +
                        #   @return [String] Workflow type.
         | 
| 199 | 
            +
                        # @!attribute sticky?
         | 
| 200 | 
            +
                        #   @return [Boolean] Whether the slot was for a sticky task.
         | 
| 201 | 
            +
                        Workflow = Data.define(:workflow_type, :sticky?)
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                        # Information about an activity slot.
         | 
| 204 | 
            +
                        #
         | 
| 205 | 
            +
                        # @!attribute activity_type
         | 
| 206 | 
            +
                        #   @return [String] Activity type.
         | 
| 207 | 
            +
                        Activity = Data.define(:activity_type)
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                        # Information about a local activity slot.
         | 
| 210 | 
            +
                        #
         | 
| 211 | 
            +
                        # @!attribute activity_type
         | 
| 212 | 
            +
                        #   @return [String] Activity type.
         | 
| 213 | 
            +
                        LocalActivity = Data.define(:activity_type)
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                        # Information about a Nexus slot.
         | 
| 216 | 
            +
                        #
         | 
| 217 | 
            +
                        # @!attribute service
         | 
| 218 | 
            +
                        #   @return [String] Nexus service.
         | 
| 219 | 
            +
                        # @!attribute operation
         | 
| 220 | 
            +
                        #   @return [String] Nexus operation.
         | 
| 221 | 
            +
                        Nexus = Data.define(:service, :operation)
         | 
| 222 | 
            +
                      end
         | 
| 61 223 | 
             
                    end
         | 
| 62 224 |  | 
| 63 225 | 
             
                    # @!visibility private
         | 
| 64 | 
            -
                    def _to_bridge_options
         | 
| 65 | 
            -
                      raise ArgumentError, 'Tuner slot suppliers must be instances of Fixed or  | 
| 226 | 
            +
                    def _to_bridge_options(_tuner)
         | 
| 227 | 
            +
                      raise ArgumentError, 'Tuner slot suppliers must be instances of Fixed, ResourceBased, or Custom'
         | 
| 66 228 | 
             
                    end
         | 
| 67 229 | 
             
                  end
         | 
| 68 230 |  | 
| @@ -75,10 +237,9 @@ module Temporalio | |
| 75 237 | 
             
                  # @!attribute target_cpu_usage
         | 
| 76 238 | 
             
                  #   @return [Float] A value between 0 and 1 that represents the target (system) CPU usage. This can be set to 1.0
         | 
| 77 239 | 
             
                  #     if desired, but it's recommended to leave some headroom for other processes.
         | 
| 78 | 
            -
                  ResourceBasedTunerOptions =  | 
| 240 | 
            +
                  ResourceBasedTunerOptions = Data.define(
         | 
| 79 241 | 
             
                    :target_memory_usage,
         | 
| 80 | 
            -
                    :target_cpu_usage | 
| 81 | 
            -
                    keyword_init: true
         | 
| 242 | 
            +
                    :target_cpu_usage
         | 
| 82 243 | 
             
                  )
         | 
| 83 244 |  | 
| 84 245 | 
             
                  # Options for a specific slot type being used with {SlotSupplier::ResourceBased}.
         | 
| @@ -94,11 +255,10 @@ module Temporalio | |
| 94 255 | 
             
                  #
         | 
| 95 256 | 
             
                  #     This value matters because how many resources a task will use cannot be determined ahead of time, and thus
         | 
| 96 257 | 
             
                  #     the system should wait to see how much resources are used before issuing more slots.
         | 
| 97 | 
            -
                  ResourceBasedSlotOptions =  | 
| 258 | 
            +
                  ResourceBasedSlotOptions = Data.define(
         | 
| 98 259 | 
             
                    :min_slots,
         | 
| 99 260 | 
             
                    :max_slots,
         | 
| 100 | 
            -
                    :ramp_throttle | 
| 101 | 
            -
                    keyword_init: true
         | 
| 261 | 
            +
                    :ramp_throttle
         | 
| 102 262 | 
             
                  )
         | 
| 103 263 |  | 
| 104 264 | 
             
                  # Create a fixed-size tuner with the provided number of slots.
         | 
| @@ -161,27 +321,36 @@ module Temporalio | |
| 161 321 | 
             
                  # @return [SlotSupplier] Slot supplier for local activities.
         | 
| 162 322 | 
             
                  attr_reader :local_activity_slot_supplier
         | 
| 163 323 |  | 
| 324 | 
            +
                  # @return [ThreadPool, nil] Thread pool for custom slot suppliers.
         | 
| 325 | 
            +
                  attr_reader :custom_slot_supplier_thread_pool
         | 
| 326 | 
            +
             | 
| 164 327 | 
             
                  # Create a tuner from 3 slot suppliers.
         | 
| 165 328 | 
             
                  #
         | 
| 166 329 | 
             
                  # @param workflow_slot_supplier [SlotSupplier] Slot supplier for workflows.
         | 
| 167 330 | 
             
                  # @param activity_slot_supplier [SlotSupplier] Slot supplier for activities.
         | 
| 168 331 | 
             
                  # @param local_activity_slot_supplier [SlotSupplier] Slot supplier for local activities.
         | 
| 332 | 
            +
                  # @param custom_slot_supplier_thread_pool [ThreadPool, nil] Thread pool to make all custom slot supplier calls on.
         | 
| 333 | 
            +
                  #   If there are no custom slot suppliers, this parameter is ignored. Technically users may set this to nil which
         | 
| 334 | 
            +
                  #   will not use a thread pool to make slot supplier calls, but that is dangerous and not advised because even the
         | 
| 335 | 
            +
                  #   slightest blocking call can slow down the system.
         | 
| 169 336 | 
             
                  def initialize(
         | 
| 170 337 | 
             
                    workflow_slot_supplier:,
         | 
| 171 338 | 
             
                    activity_slot_supplier:,
         | 
| 172 | 
            -
                    local_activity_slot_supplier | 
| 339 | 
            +
                    local_activity_slot_supplier:,
         | 
| 340 | 
            +
                    custom_slot_supplier_thread_pool: ThreadPool.default
         | 
| 173 341 | 
             
                  )
         | 
| 174 342 | 
             
                    @workflow_slot_supplier = workflow_slot_supplier
         | 
| 175 343 | 
             
                    @activity_slot_supplier = activity_slot_supplier
         | 
| 176 344 | 
             
                    @local_activity_slot_supplier = local_activity_slot_supplier
         | 
| 345 | 
            +
                    @custom_slot_supplier_thread_pool = custom_slot_supplier_thread_pool
         | 
| 177 346 | 
             
                  end
         | 
| 178 347 |  | 
| 179 348 | 
             
                  # @!visibility private
         | 
| 180 349 | 
             
                  def _to_bridge_options
         | 
| 181 350 | 
             
                    Internal::Bridge::Worker::TunerOptions.new(
         | 
| 182 | 
            -
                      workflow_slot_supplier: workflow_slot_supplier._to_bridge_options,
         | 
| 183 | 
            -
                      activity_slot_supplier: activity_slot_supplier._to_bridge_options,
         | 
| 184 | 
            -
                      local_activity_slot_supplier: local_activity_slot_supplier._to_bridge_options
         | 
| 351 | 
            +
                      workflow_slot_supplier: workflow_slot_supplier._to_bridge_options(self),
         | 
| 352 | 
            +
                      activity_slot_supplier: activity_slot_supplier._to_bridge_options(self),
         | 
| 353 | 
            +
                      local_activity_slot_supplier: local_activity_slot_supplier._to_bridge_options(self)
         | 
| 185 354 | 
             
                    )
         | 
| 186 355 | 
             
                  end
         | 
| 187 356 | 
             
                end
         | 
| @@ -7,6 +7,7 @@ module Temporalio | |
| 7 7 | 
             
                  :continued_run_id,
         | 
| 8 8 | 
             
                  :cron_schedule,
         | 
| 9 9 | 
             
                  :execution_timeout,
         | 
| 10 | 
            +
                  :first_execution_run_id,
         | 
| 10 11 | 
             
                  :headers,
         | 
| 11 12 | 
             
                  :last_failure,
         | 
| 12 13 | 
             
                  :last_result,
         | 
| @@ -35,6 +36,8 @@ module Temporalio | |
| 35 36 | 
             
                #   @return [String, nil] Cron schedule if applicable.
         | 
| 36 37 | 
             
                # @!attribute execution_timeout
         | 
| 37 38 | 
             
                #   @return [Float, nil] Execution timeout for the workflow.
         | 
| 39 | 
            +
                # @!attribute first_execution_run_id
         | 
| 40 | 
            +
                #   @return [String] The very first run ID the workflow ever had, following continuation chains.
         | 
| 38 41 | 
             
                # @!attribute headers
         | 
| 39 42 | 
             
                #   @return [Hash<String, Api::Common::V1::Payload>] Headers.
         | 
| 40 43 | 
             
                # @!attribute last_failure
         | 
    
        data/lib/temporalio/workflow.rb
    CHANGED
    
    | @@ -208,14 +208,16 @@ module Temporalio | |
| 208 208 | 
             
                #
         | 
| 209 209 | 
             
                # @param activity [Class<Activity::Definition>, Symbol, String] Activity definition class or name.
         | 
| 210 210 | 
             
                # @param args [Array<Object>] Arguments to the activity.
         | 
| 211 | 
            +
                # @param summary [String, nil] Single-line summary for this activity that may appear in CLI/UI. This can be in
         | 
| 212 | 
            +
                #   single-line Temporal markdown format. This is currently experimental.
         | 
| 211 213 | 
             
                # @param schedule_to_close_timeout [Float, nil] Max amount of time the activity can take from first being scheduled
         | 
| 212 214 | 
             
                #   to being completed before it times out. This is inclusive of all retries.
         | 
| 213 215 | 
             
                # @param schedule_to_start_timeout [Float, nil] Max amount of time the activity can take to be started from first
         | 
| 214 216 | 
             
                #   being scheduled.
         | 
| 215 217 | 
             
                # @param start_to_close_timeout [Float, nil] Max amount of time a single activity run can take from when it starts
         | 
| 216 218 | 
             
                #   to when it completes. This is per retry.
         | 
| 217 | 
            -
                # @param retry_policy [RetryPolicy] How an activity is retried on failure. If unset, a  | 
| 218 | 
            -
                #    | 
| 219 | 
            +
                # @param retry_policy [RetryPolicy, nil] How an activity is retried on failure. If unset, a default policy is used.
         | 
| 220 | 
            +
                #   Set maximum attempts to 1 to disable retries.
         | 
| 219 221 | 
             
                # @param local_retry_threshold [Float, nil] If the activity is retrying and backoff would exceed this value, a timer
         | 
| 220 222 | 
             
                #   is scheduled and the activity is retried after. Otherwise, backoff will happen internally within the task.
         | 
| 221 223 | 
             
                #   Defaults to 1 minute.
         | 
| @@ -238,6 +240,7 @@ module Temporalio | |
| 238 240 | 
             
                def self.execute_local_activity(
         | 
| 239 241 | 
             
                  activity,
         | 
| 240 242 | 
             
                  *args,
         | 
| 243 | 
            +
                  summary: nil,
         | 
| 241 244 | 
             
                  schedule_to_close_timeout: nil,
         | 
| 242 245 | 
             
                  schedule_to_start_timeout: nil,
         | 
| 243 246 | 
             
                  start_to_close_timeout: nil,
         | 
| @@ -251,7 +254,7 @@ module Temporalio | |
| 251 254 | 
             
                )
         | 
| 252 255 | 
             
                  _current.execute_local_activity(
         | 
| 253 256 | 
             
                    activity, *args,
         | 
| 254 | 
            -
                    schedule_to_close_timeout:, schedule_to_start_timeout:, start_to_close_timeout:,
         | 
| 257 | 
            +
                    summary:, schedule_to_close_timeout:, schedule_to_start_timeout:, start_to_close_timeout:,
         | 
| 255 258 | 
             
                    retry_policy:, local_retry_threshold:, cancellation:, cancellation_type:,
         | 
| 256 259 | 
             
                    activity_id:, arg_hints:, result_hint:
         | 
| 257 260 | 
             
                  )
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: temporalio
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 5 | 
             
            platform: x86_64-linux
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Temporal Technologies Inc
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2025- | 
| 11 | 
            +
            date: 2025-09-29 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: google-protobuf
         | 
| @@ -156,6 +156,7 @@ files: | |
| 156 156 | 
             
            - lib/temporalio/converters/payload_converter/json_plain.rb
         | 
| 157 157 | 
             
            - lib/temporalio/converters/payload_converter/json_protobuf.rb
         | 
| 158 158 | 
             
            - lib/temporalio/converters/raw_value.rb
         | 
| 159 | 
            +
            - lib/temporalio/env_config.rb
         | 
| 159 160 | 
             
            - lib/temporalio/error.rb
         | 
| 160 161 | 
             
            - lib/temporalio/error/failure.rb
         | 
| 161 162 | 
             
            - lib/temporalio/internal.rb
         |