Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,9 @@ jobs:
strategy:
fail-fast: false
matrix:
# TODO(cretz): Enable Linux ARM. It's not natively supported with setup-ruby (see
# https://github.com/ruby/setup-ruby#supported-platforms and https://github.com/ruby/setup-ruby/issues/577).
# So we need to set ruby-version to 'none' per
# https://github.com/oxidize-rb/actions/tree/main/setup-ruby-and-rust and install Ruby ourselves maybe. See
# https://github.com/ruby/setup-ruby?tab=readme-ov-file#using-self-hosted-runners. The error states:
# Error: The current runner (ubuntu-24.04-arm64) was detected as self-hosted because the platform does not match a GitHub-hosted runner image (or that image is deprecated and no longer supported).
# In such a case, you should install Ruby in the $RUNNER_TOOL_CACHE yourself, for example using https://github.com/rbenv/ruby-build
# You can take inspiration from this workflow for more details: https://github.com/ruby/ruby-builder/blob/master/.github/workflows/build.yml
#
# TODO(cretz): Enable ubuntu-24.04-arm when oxidize-rb/actions/setup-ruby-and-rust
# supports it
#
# TODO(cretz): Enable x64-mingw-ucrt if we can figure out Windows issue, see
# https://github.com/temporalio/sdk-ruby/issues/172
os: [ubuntu-latest, macos-latest]
Expand All @@ -28,7 +22,7 @@ jobs:

include:
- os: ubuntu-latest
rubyVersion: "3.3"
rubyVersion: "3.4"
checkTarget: true
runs-on: ${{ matrix.os }}
steps:
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,7 @@ worker = Temporalio::Worker.new(
task_queue: 'my-task-queue',
workflows: [SayHelloWorkflow],
# There are various forms an activity can take, see "Activities" section for details
activities: [SayHelloActivity],
# During the beta period, this must be provided explicitly, see "Workers" section for details
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
activities: [SayHelloActivity]
)

# Run the worker until SIGINT. This can be done in many ways, see "Workers" section for details.
Expand Down Expand Up @@ -357,9 +355,7 @@ worker = Temporalio::Worker.new(
task_queue: 'my-task-queue',
workflows: [MyModule::MyWorkflow],
# There are various forms an activity can take, see "Activities" section for details
activities: [MyModule::MyActivity],
# During the beta period, this must be provided explicitly, see below for details
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
activities: [MyModule::MyActivity]
)

# Run the worker until block complete
Expand All @@ -372,9 +368,6 @@ Notes about the above code:

* A worker uses the same client that is used for other Temporal things.
* This just shows providing an activity class, but there are other forms, see the "Activities" section for details.
* The `workflow_executor` defaults to `Temporalio::Worker::WorkflowExecutor::Ractor.instance` which intentionally does
not work during this beta period. Therefore, during this beta period, opting in to
`Temporalio::Worker::WorkflowExecutor::ThreadPool.default` is required explicitly.
* The worker `run` method accepts an optional `Temporalio::Cancellation` object that can be used to cancel instead or in
addition to providing a block that waits for completion.
* The worker `run` method accepts a `shutdown_signals` array which will trap the signal and start shutdown when
Expand Down Expand Up @@ -994,6 +987,13 @@ it will raise the error raised in the activity.
The constructor of the environment has multiple keyword arguments that can be set to affect the activity context for the
activity.

### Ractors

It was an original goal to have workflows actually be Ractors for deterministic state isolation and have the library
support Ractors in general. However, due to the SDK's heavy use of the Google Protobuf library which
[is not Ractor-safe](https://github.com/protocolbuffers/protobuf/issues/19321), the Temporal Ruby SDK does not currently
work with Ractors.
Comment on lines +992 to +995
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passin' that buck hard

😆


### Platform Support

This SDK is backed by a Ruby C extension written in Rust leveraging the
Expand Down
11 changes: 4 additions & 7 deletions temporalio/lib/temporalio/converters/data_converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ module Temporalio
module Converters
# Data converter for converting/encoding payloads to/from Ruby values.
class DataConverter
# @return [PayloadConverter] Payload converter. This must be Ractor shareable.
# @return [PayloadConverter] Payload converter.
attr_reader :payload_converter

# @return [FailureConverter] Failure converter. This must be Ractor shareable.
# @return [FailureConverter] Failure converter.
attr_reader :failure_converter

# @return [PayloadCodec, nil] Optional codec for encoding/decoding payload bytes such as for encryption.
Expand All @@ -24,17 +24,14 @@ def self.default

# Create data converter.
#
# @param payload_converter [PayloadConverter] Payload converter to use. This must be Ractor shareable.
# @param failure_converter [FailureConverter] Failure converter to use. This must be Ractor shareable.
# @param payload_converter [PayloadConverter] Payload converter to use.
# @param failure_converter [FailureConverter] Failure converter to use.
# @param payload_codec [PayloadCodec, nil] Payload codec to use.
def initialize(
payload_converter: PayloadConverter.default,
failure_converter: FailureConverter.default,
payload_codec: nil
)
raise 'Payload converter not shareable' unless Ractor.shareable?(payload_converter)
raise 'Failure converter not shareable' unless Ractor.shareable?(failure_converter)

@payload_converter = payload_converter
@failure_converter = failure_converter
@payload_codec = payload_codec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Converters
class FailureConverter
# @return [FailureConverter] Default failure converter.
def self.default
@default ||= Ractor.make_shareable(FailureConverter.new)
@default ||= FailureConverter.new
end

# @return [Boolean] If +true+, the message and stack trace of the failure will be moved into the encoded attribute
Expand Down
14 changes: 6 additions & 8 deletions temporalio/lib/temporalio/converters/payload_converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ def self.default
# @param json_generate_options [Hash] Options for {::JSON.generate}.
# @return [PayloadConverter::Composite] Created payload converter.
def self.new_with_defaults(json_parse_options: { create_additions: true }, json_generate_options: {})
Ractor.make_shareable(
PayloadConverter::Composite.new(
PayloadConverter::BinaryNull.new,
PayloadConverter::BinaryPlain.new,
PayloadConverter::JSONProtobuf.new,
PayloadConverter::BinaryProtobuf.new,
PayloadConverter::JSONPlain.new(parse_options: json_parse_options, generate_options: json_generate_options)
)
PayloadConverter::Composite.new(
PayloadConverter::BinaryNull.new,
PayloadConverter::BinaryPlain.new,
PayloadConverter::JSONProtobuf.new,
PayloadConverter::BinaryProtobuf.new,
PayloadConverter::JSONPlain.new(parse_options: json_parse_options, generate_options: json_generate_options)
)
end

Expand Down
5 changes: 3 additions & 2 deletions temporalio/lib/temporalio/worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ def self.default_illegal_workflow_calls
# @param workflows [Array<Class<Workflow::Definition>>] Workflows for this worker.
# @param tuner [Tuner] Tuner that controls the amount of concurrent activities/workflows that run at a time.
# @param activity_executors [Hash<Symbol, Worker::ActivityExecutor>] Executors that activities can run within.
# @param workflow_executor [WorkflowExecutor] Workflow executor that workflow tasks run within.
# @param workflow_executor [WorkflowExecutor] Workflow executor that workflow tasks run within. This must be a
# {WorkflowExecutor::ThreadPool} currently.
# @param interceptors [Array<Interceptor::Activity, Interceptor::Workflow>] Interceptors specific to this worker.
# Note, interceptors set on the client that include the {Interceptor::Activity} or {Interceptor::Workflow} module
# are automatically included here, so no need to specify them again.
Expand Down Expand Up @@ -352,7 +353,7 @@ def initialize(
workflows: [],
tuner: Tuner.create_fixed,
activity_executors: ActivityExecutor.defaults,
workflow_executor: WorkflowExecutor::Ractor.instance,
workflow_executor: WorkflowExecutor::ThreadPool.default,
interceptors: [],
build_id: Worker.default_build_id,
identity: nil,
Expand Down
3 changes: 1 addition & 2 deletions temporalio/lib/temporalio/worker/workflow_executor.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# frozen_string_literal: true

require 'temporalio/worker/workflow_executor/ractor'
require 'temporalio/worker/workflow_executor/thread_pool'

module Temporalio
class Worker
# Workflow executor that executes workflow tasks. Unlike {ActivityExecutor}, this class is not meant for user
# implementation. Instead, either {WorkflowExecutor::ThreadPool} or {WorkflowExecutor::Ractor} should be used.
# implementation. The only implementation that is currently accepted is {WorkflowExecutor::ThreadPool}.
class WorkflowExecutor
# @!visibility private
def initialize
Expand Down
69 changes: 0 additions & 69 deletions temporalio/lib/temporalio/worker/workflow_executor/ractor.rb

This file was deleted.

This file was deleted.

4 changes: 1 addition & 3 deletions temporalio/test/converters/data_converter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ module Converters
class DataConverterTest < Test
def test_with_codec
converter = Temporalio::Converters::DataConverter.new(
failure_converter: Ractor.make_shareable(
Temporalio::Converters::FailureConverter.new(encode_common_attributes: true)
),
failure_converter: Temporalio::Converters::FailureConverter.new(encode_common_attributes: true),
payload_codec: Base64Codec.new
)

Expand Down
12 changes: 3 additions & 9 deletions temporalio/test/testing/workflow_environment_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ def test_time_skipping_auto
worker = Temporalio::Worker.new(
client: env.client,
task_queue: "tq-#{SecureRandom.uuid}",
workflows: [SlowWorkflow],
# TODO(cretz): Ractor support not currently working
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
workflows: [SlowWorkflow]
)
worker.run do
# Check that timestamp is around now
Expand All @@ -63,9 +61,7 @@ def test_time_skipping_manual
worker = Temporalio::Worker.new(
client: env.client,
task_queue: "tq-#{SecureRandom.uuid}",
workflows: [SlowWorkflow],
# TODO(cretz): Ractor support not currently working
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
workflows: [SlowWorkflow]
)
worker.run do
# Start workflow
Expand Down Expand Up @@ -121,9 +117,7 @@ def test_time_skipping_heartbeat_timeout
client: env.client,
task_queue: "tq-#{SecureRandom.uuid}",
workflows: [HeartbeatTimeoutWorkflow],
activities: [HeartbeatTimeoutActivity.new(env)],
# TODO(cretz): Ractor support not currently working
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
activities: [HeartbeatTimeoutActivity.new(env)]
)
worker.run do
# Run workflow and confirm it got heartbeat timeout
Expand Down
13 changes: 3 additions & 10 deletions temporalio/test/worker_workflow_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1114,9 +1114,7 @@ def test_payload_codec
# Workflow failure with failure encoding
new_options = env.client.options.with(
data_converter: Temporalio::Converters::DataConverter.new(
failure_converter: Ractor.make_shareable(
Temporalio::Converters::FailureConverter.new(encode_common_attributes: true)
),
failure_converter: Temporalio::Converters::FailureConverter.new(encode_common_attributes: true),
payload_codec: Base64Codec.new
)
)
Expand Down Expand Up @@ -1164,9 +1162,7 @@ def test_dynamic
worker = Temporalio::Worker.new(
client: env.client,
task_queue: "tq-#{SecureRandom.uuid}",
workflows: [DynamicWorkflow, NonDynamicWorkflow],
# TODO(cretz): Ractor support not currently working
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
workflows: [DynamicWorkflow, NonDynamicWorkflow]
)
worker.run do
# Non-dynamic
Expand Down Expand Up @@ -1670,9 +1666,7 @@ def do_update(arg)

def test_fail_workflow_payload_converter
new_options = env.client.options.with(
data_converter: Temporalio::Converters::DataConverter.new(
payload_converter: Ractor.make_shareable(FailWorkflowPayloadConverter.new)
)
data_converter: Temporalio::Converters::DataConverter.new(payload_converter: FailWorkflowPayloadConverter.new)
)
client = Temporalio::Client.new(**new_options.to_h)

Expand Down Expand Up @@ -1746,7 +1740,6 @@ def test_confirm_garbage_collect

# TODO(cretz): To test
# * Common
# * Ractor with global state
# * Eager workflow start
# * Unawaited futures that have exceptions, need to log warning like Java does
# * Enhanced stack trace?
Expand Down
2 changes: 0 additions & 2 deletions temporalio/test/workflow_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ def execute_workflow(
task_queue:,
activities:,
workflows: [workflow] + more_workflows,
# TODO(cretz): Ractor support not currently working
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default,
workflow_failure_exception_types:,
max_cached_workflows:,
logger: logger || client.options.logger,
Expand Down
Loading