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
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,19 @@ async completion.

See https://docs.temporal.io/standalone-activity for the cross-SDK feature overview.

### Fixed

#### `execute_update_with_start_workflow` no longer raises `RPCError NOT_FOUND` on validator rejection

When a `workflow_update_validator` rejected an update sent via
`Client#execute_update_with_start_workflow` (or `#start_update_with_start_workflow` with
`wait_for_stage: COMPLETED`), the client polled history for an outcome that was never written
and surfaced the failure as `Temporalio::Error::RPCError` with code `NOT_FOUND`. The caller now
correctly receives `Temporalio::Error::WorkflowUpdateFailedError`. (#454)

#### Start Delay for Standalone Activities

`Client#start_activity` and `Client#execute_activity` now accept a `start_delay:` kwarg. When set, the server creates the activity immediately,
but defers dispatch to a worker until the delay elapses. Retry attempts do not re-apply the delay.
`ScheduleToStart` and `ScheduleToClose` timeout clocks begin counting after the delay
elapses; `StartToClose` and `Heartbeat` are unaffected. Currently experimental.
elapses; `StartToClose` and `Heartbeat` are unaffected. Currently experimental.
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def start_update_with_start_workflow(input)
end

# If the user wants to wait until completed, we must poll until outcome if not already there
if input.wait_for_stage == Temporalio::Client::WorkflowUpdateWaitStage::COMPLETED && update_resp.outcome
if input.wait_for_stage == Temporalio::Client::WorkflowUpdateWaitStage::COMPLETED && !update_resp.outcome
update_resp.outcome = @client._impl.poll_workflow_update(
Temporalio::Client::Interceptor::PollWorkflowUpdateInput.new(
workflow_id: start_options.id,
Expand Down
34 changes: 34 additions & 0 deletions temporalio/test/worker_workflow_handler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,16 @@ def fail
raise Temporalio::Error::ApplicationError, 'Intentional failure'
end

workflow_update
def rejected_by_validator(value)
raise "Update handler should not run when validator rejects (got #{value})"
end

workflow_update_validator :rejected_by_validator
def validate_rejected_by_validator(value)
raise 'Validator rejection reason' if value.negative?
end

workflow_update
def start_waiting
Temporalio::Workflow.wait_condition { @finish_waiting }
Expand Down Expand Up @@ -784,6 +794,30 @@ def test_update_with_start_update_failure
end
end

def test_update_with_start_validator_rejection
worker = Temporalio::Worker.new(
client: env.client,
task_queue: "tq-#{SecureRandom.uuid}",
workflows: [UpdateWithStartWorkflow]
)
worker.run do
id = "wf-#{SecureRandom.uuid}"
start_workflow_operation = Temporalio::Client::WithStartWorkflowOperation.new(
UpdateWithStartWorkflow, 123,
id:, task_queue: worker.task_queue, id_conflict_policy: Temporalio::WorkflowIDConflictPolicy::FAIL
)
err = assert_raises(Temporalio::Error::WorkflowUpdateFailedError) do
env.client.execute_update_with_start_workflow(
UpdateWithStartWorkflow.rejected_by_validator, -1,
start_workflow_operation:
)
end
assert_instance_of Temporalio::Error::ApplicationError, err.cause
assert_equal 'Validator rejection reason', err.cause.message
assert_equal 123, start_workflow_operation.workflow_handle.query(UpdateWithStartWorkflow.counter)
end
end

def test_update_with_start_cancel
# Run worker
worker = Temporalio::Worker.new(
Expand Down
Loading