From 810bcf2e6cd1709a65df80e88f9e0b14cf63cbc9 Mon Sep 17 00:00:00 2001 From: jmaeagle99 <44687433+jmaeagle99@users.noreply.github.com> Date: Wed, 21 Jan 2026 08:16:01 -0800 Subject: [PATCH 1/3] Add validate_namespace to allow SDKs to get namespace limit information --- .../temporal/sdk/core/core_interface.proto | 15 ++++++ crates/common/src/lib.rs | 11 +++++ crates/sdk-core-c-bridge/src/worker.rs | 2 +- crates/sdk-core/src/worker/mod.rs | 49 +++++++++++++------ .../tests/integ_tests/worker_tests.rs | 2 +- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/crates/common/protos/local/temporal/sdk/core/core_interface.proto b/crates/common/protos/local/temporal/sdk/core/core_interface.proto index bfe7a0a37..2173e55d3 100644 --- a/crates/common/protos/local/temporal/sdk/core/core_interface.proto +++ b/crates/common/protos/local/temporal/sdk/core/core_interface.proto @@ -51,3 +51,18 @@ message NexusSlotInfo { string service = 1; string operation = 2; } + +// Info about a namespace +message NamespaceInfo { + // Namespace configured limits + Limits limits = 1; + + message Limits { + // Maximum size in bytes for payload fields in workflow history events + // (e.g., workflow/activity inputs and results, failure details, signal payloads). + // When exceeded, the server will reject the operation with an error. + int64 blob_size_limit_error = 1; + // Maximum total memo size in bytes per workflow execution. + int64 memo_size_limit_error = 2; + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 11e7f1970..107d0d649 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -18,6 +18,7 @@ use crate::{ protos::coresdk::{ ActivityHeartbeat, ActivityTaskCompletion, activity_task::ActivityTask, + NamespaceInfo, nexus::{NexusTask, NexusTaskCompletion}, workflow_activation::WorkflowActivation, workflow_completion::WorkflowActivationCompletion, @@ -35,8 +36,14 @@ pub trait Worker: Send + Sync { /// Validate that the worker can properly connect to server, plus any other validation that /// needs to be done asynchronously. Lang SDKs should call this function once before calling /// any others. + /// TODO: Move all SDKs over to validate_namespace and then remove async fn validate(&self) -> Result<(), WorkerValidationError>; + /// Validate that the worker can properly connect to server, plus any other validation that + /// needs to be done asynchronously. Lang SDKs should call this function once before calling + /// any others. + async fn validate_namespace(&self) -> Result; + /// Ask the worker for some work, returning a [WorkflowActivation]. It is then the language /// SDK's responsibility to call the appropriate workflow code with the provided inputs. Blocks /// indefinitely until such work is available or [Worker::shutdown] is called. @@ -160,6 +167,10 @@ where (**self).validate().await } + async fn validate_namespace(&self) -> Result { + (**self).validate_namespace().await + } + async fn poll_workflow_activation(&self) -> Result { (**self).poll_workflow_activation().await } diff --git a/crates/sdk-core-c-bridge/src/worker.rs b/crates/sdk-core-c-bridge/src/worker.rs index 78f50e7b7..3b39b554e 100644 --- a/crates/sdk-core-c-bridge/src/worker.rs +++ b/crates/sdk-core-c-bridge/src/worker.rs @@ -628,7 +628,7 @@ pub extern "C" fn temporal_core_worker_validate( let user_data = UserDataHandle(user_data); let core_worker = worker.worker.as_ref().unwrap().clone(); worker.runtime.core.tokio_handle().spawn(async move { - let fail = match core_worker.validate().await { + let fail = match core_worker.validate_namespace().await { Ok(_) => std::ptr::null(), Err(err) => worker .runtime diff --git a/crates/sdk-core/src/worker/mod.rs b/crates/sdk-core/src/worker/mod.rs index a1b1318b9..f96864ae9 100644 --- a/crates/sdk-core/src/worker/mod.rs +++ b/crates/sdk-core/src/worker/mod.rs @@ -77,6 +77,8 @@ use temporalio_common::{ ActivityTaskCompletion, activity_result::activity_execution_result, activity_task::ActivityTask, + namespace_info, + NamespaceInfo, nexus::{NexusTask, NexusTaskCompletion, nexus_task_completion}, workflow_activation::{WorkflowActivation, remove_from_cache::EvictionReason}, workflow_completion::WorkflowActivationCompletion, @@ -165,8 +167,37 @@ pub(crate) struct WorkerTelemetry { #[async_trait::async_trait] impl WorkerTrait for Worker { async fn validate(&self) -> Result<(), WorkerValidationError> { - self.verify_namespace_exists().await?; - Ok(()) + self.validate_namespace().await?; + return Ok(()) + } + + async fn validate_namespace(&self) -> Result { + match self.client.describe_namespace().await { + Ok(info) => { + let limits = info.namespace_info.and_then(|ns_info| { + ns_info.limits.map(|api_limits| { + namespace_info::Limits { + blob_size_limit_error: api_limits.blob_size_limit_error, + memo_size_limit_error: api_limits.memo_size_limit_error, + } + }) + }); + return Ok(NamespaceInfo { limits }); + }, + Err(e) => { + if e.code() == tonic::Code::Unimplemented { + // Ignore if unimplemented since we wouldn't want to fail against an old server, for + // example. + return Ok(NamespaceInfo { + ..Default::default() + }); + } + return Err(WorkerValidationError::NamespaceDescribeError { + source: e, + namespace: self.config.namespace.clone(), + }); + } + } } async fn poll_workflow_activation(&self) -> Result { @@ -1086,20 +1117,6 @@ impl Worker { dbg_panic!("trying to notify local result when workflows not enabled for this worker"); } } - - async fn verify_namespace_exists(&self) -> Result<(), WorkerValidationError> { - if let Err(e) = self.client.describe_namespace().await { - // Ignore if unimplemented since we wouldn't want to fail against an old server, for - // example. - if e.code() != tonic::Code::Unimplemented { - return Err(WorkerValidationError::NamespaceDescribeError { - source: e, - namespace: self.config.namespace.clone(), - }); - } - } - Ok(()) - } } struct ClientWorkerRegistrator { diff --git a/crates/sdk-core/tests/integ_tests/worker_tests.rs b/crates/sdk-core/tests/integ_tests/worker_tests.rs index 2b50549b4..8b28398bb 100644 --- a/crates/sdk-core/tests/integ_tests/worker_tests.rs +++ b/crates/sdk-core/tests/integ_tests/worker_tests.rs @@ -97,7 +97,7 @@ async fn worker_validation_fails_on_nonexistent_namespace() { ) .unwrap(); - let res = worker.validate().await; + let res = worker.validate_namespace().await; assert_matches!( res, Err(WorkerValidationError::NamespaceDescribeError { .. }) From ace9f138e5279792a1d6e27c80021632399d67ca Mon Sep 17 00:00:00 2001 From: jmaeagle99 <44687433+jmaeagle99@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:43:01 -0800 Subject: [PATCH 2/3] Format --- crates/common/src/lib.rs | 3 +-- crates/sdk-core/src/worker/mod.rs | 15 ++++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 107d0d649..0c426be36 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -16,9 +16,8 @@ use crate::{ WorkerValidationError, }, protos::coresdk::{ - ActivityHeartbeat, ActivityTaskCompletion, + ActivityHeartbeat, ActivityTaskCompletion, NamespaceInfo, activity_task::ActivityTask, - NamespaceInfo, nexus::{NexusTask, NexusTaskCompletion}, workflow_activation::WorkflowActivation, workflow_completion::WorkflowActivationCompletion, diff --git a/crates/sdk-core/src/worker/mod.rs b/crates/sdk-core/src/worker/mod.rs index f96864ae9..9d260cbca 100644 --- a/crates/sdk-core/src/worker/mod.rs +++ b/crates/sdk-core/src/worker/mod.rs @@ -74,11 +74,10 @@ use temporalio_common::{ protos::{ TaskToken, coresdk::{ - ActivityTaskCompletion, + ActivityTaskCompletion, NamespaceInfo, activity_result::activity_execution_result, activity_task::ActivityTask, namespace_info, - NamespaceInfo, nexus::{NexusTask, NexusTaskCompletion, nexus_task_completion}, workflow_activation::{WorkflowActivation, remove_from_cache::EvictionReason}, workflow_completion::WorkflowActivationCompletion, @@ -168,22 +167,20 @@ pub(crate) struct WorkerTelemetry { impl WorkerTrait for Worker { async fn validate(&self) -> Result<(), WorkerValidationError> { self.validate_namespace().await?; - return Ok(()) + return Ok(()); } async fn validate_namespace(&self) -> Result { match self.client.describe_namespace().await { Ok(info) => { let limits = info.namespace_info.and_then(|ns_info| { - ns_info.limits.map(|api_limits| { - namespace_info::Limits { - blob_size_limit_error: api_limits.blob_size_limit_error, - memo_size_limit_error: api_limits.memo_size_limit_error, - } + ns_info.limits.map(|api_limits| namespace_info::Limits { + blob_size_limit_error: api_limits.blob_size_limit_error, + memo_size_limit_error: api_limits.memo_size_limit_error, }) }); return Ok(NamespaceInfo { limits }); - }, + } Err(e) => { if e.code() == tonic::Code::Unimplemented { // Ignore if unimplemented since we wouldn't want to fail against an old server, for From 83a2a7033d1ed6e1129dafed28bf97fc2baaf775 Mon Sep 17 00:00:00 2001 From: jmaeagle99 <44687433+jmaeagle99@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:02:00 -0800 Subject: [PATCH 3/3] Update validate instead of making separate methods --- crates/common/src/lib.rs | 14 ++------------ crates/sdk-core-c-bridge/src/worker.rs | 2 +- crates/sdk-core/src/worker/mod.rs | 7 +------ crates/sdk-core/tests/integ_tests/worker_tests.rs | 2 +- 4 files changed, 5 insertions(+), 20 deletions(-) diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 0c426be36..bc2099559 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -35,13 +35,7 @@ pub trait Worker: Send + Sync { /// Validate that the worker can properly connect to server, plus any other validation that /// needs to be done asynchronously. Lang SDKs should call this function once before calling /// any others. - /// TODO: Move all SDKs over to validate_namespace and then remove - async fn validate(&self) -> Result<(), WorkerValidationError>; - - /// Validate that the worker can properly connect to server, plus any other validation that - /// needs to be done asynchronously. Lang SDKs should call this function once before calling - /// any others. - async fn validate_namespace(&self) -> Result; + async fn validate(&self) -> Result; /// Ask the worker for some work, returning a [WorkflowActivation]. It is then the language /// SDK's responsibility to call the appropriate workflow code with the provided inputs. Blocks @@ -162,14 +156,10 @@ impl Worker for Arc where W: Worker + ?Sized, { - async fn validate(&self) -> Result<(), WorkerValidationError> { + async fn validate(&self) -> Result { (**self).validate().await } - async fn validate_namespace(&self) -> Result { - (**self).validate_namespace().await - } - async fn poll_workflow_activation(&self) -> Result { (**self).poll_workflow_activation().await } diff --git a/crates/sdk-core-c-bridge/src/worker.rs b/crates/sdk-core-c-bridge/src/worker.rs index 3b39b554e..78f50e7b7 100644 --- a/crates/sdk-core-c-bridge/src/worker.rs +++ b/crates/sdk-core-c-bridge/src/worker.rs @@ -628,7 +628,7 @@ pub extern "C" fn temporal_core_worker_validate( let user_data = UserDataHandle(user_data); let core_worker = worker.worker.as_ref().unwrap().clone(); worker.runtime.core.tokio_handle().spawn(async move { - let fail = match core_worker.validate_namespace().await { + let fail = match core_worker.validate().await { Ok(_) => std::ptr::null(), Err(err) => worker .runtime diff --git a/crates/sdk-core/src/worker/mod.rs b/crates/sdk-core/src/worker/mod.rs index 9d260cbca..cf4571812 100644 --- a/crates/sdk-core/src/worker/mod.rs +++ b/crates/sdk-core/src/worker/mod.rs @@ -165,12 +165,7 @@ pub(crate) struct WorkerTelemetry { #[async_trait::async_trait] impl WorkerTrait for Worker { - async fn validate(&self) -> Result<(), WorkerValidationError> { - self.validate_namespace().await?; - return Ok(()); - } - - async fn validate_namespace(&self) -> Result { + async fn validate(&self) -> Result { match self.client.describe_namespace().await { Ok(info) => { let limits = info.namespace_info.and_then(|ns_info| { diff --git a/crates/sdk-core/tests/integ_tests/worker_tests.rs b/crates/sdk-core/tests/integ_tests/worker_tests.rs index 8b28398bb..2b50549b4 100644 --- a/crates/sdk-core/tests/integ_tests/worker_tests.rs +++ b/crates/sdk-core/tests/integ_tests/worker_tests.rs @@ -97,7 +97,7 @@ async fn worker_validation_fails_on_nonexistent_namespace() { ) .unwrap(); - let res = worker.validate_namespace().await; + let res = worker.validate().await; assert_matches!( res, Err(WorkerValidationError::NamespaceDescribeError { .. })