Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
9 changes: 6 additions & 3 deletions dapr/aio/clients/grpc/interceptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ class _ClientCallDetailsAsync(

class DaprClientTimeoutInterceptorAsync(UnaryUnaryClientInterceptor):
def intercept_unary_unary(self, continuation, client_call_details, request):
# If a specific timeout is not set, create a new ClientCallDetails with the default timeout
if client_call_details.timeout is None:
# Only apply a deadline when DAPR_API_TIMEOUT_SECONDS is explicitly configured.
# Without an explicit setting there is no SDK-level default deadline: Dapr's own
# resiliency policies and component timeouts act as the authoritative bounds for
# long-running operations such as LLM calls and workflow activities.
if settings.DAPR_API_TIMEOUT_SECONDS is not None and client_call_details.timeout is None:
new_client_call_details = _ClientCallDetailsAsync(
Comment thread
cicoyle marked this conversation as resolved.
client_call_details.method,
settings.DAPR_API_TIMEOUT_SECONDS,
float(settings.DAPR_API_TIMEOUT_SECONDS),
client_call_details.metadata,
client_call_details.credentials,
client_call_details.wait_for_ready,
Expand Down
9 changes: 6 additions & 3 deletions dapr/clients/grpc/interceptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ class _ClientCallDetails(

class DaprClientTimeoutInterceptor(UnaryUnaryClientInterceptor):
def intercept_unary_unary(self, continuation, client_call_details, request):
# If a specific timeout is not set, create a new ClientCallDetails with the default timeout
if client_call_details.timeout is None:
# Only apply a deadline when DAPR_API_TIMEOUT_SECONDS is explicitly configured.
# Without an explicit setting there is no SDK-level default deadline: Dapr's own
# resiliency policies and component timeouts act as the authoritative bounds for
# long-running operations such as LLM calls and workflow activities.
if settings.DAPR_API_TIMEOUT_SECONDS is not None and client_call_details.timeout is None:
new_client_call_details = _ClientCallDetails(
Comment thread
cicoyle marked this conversation as resolved.
client_call_details.method,
settings.DAPR_API_TIMEOUT_SECONDS,
float(settings.DAPR_API_TIMEOUT_SECONDS),
client_call_details.metadata,
client_call_details.credentials,
client_call_details.wait_for_ready,
Expand Down
4 changes: 3 additions & 1 deletion dapr/conf/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
DAPR_HEALTH_TIMEOUT = 60 # seconds

DAPR_API_MAX_RETRIES = 0
DAPR_API_TIMEOUT_SECONDS = 60
DAPR_API_TIMEOUT_SECONDS = (
None # No default deadline — set DAPR_API_TIMEOUT_SECONDS env var to impose one
)

DAPR_API_METHOD_INVOCATION_PROTOCOL = 'http'

Expand Down
17 changes: 17 additions & 0 deletions tests/clients/test_timeout_interceptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,20 @@ def test_intercept_unary_unary_without_timeout(self):
)
called_client_call_details = continuation.call_args[0][0]
self.assertEqual(7, called_client_call_details.timeout)

@patch.object(settings, 'DAPR_API_TIMEOUT_SECONDS', None)
def test_intercept_unary_unary_no_global_timeout_no_per_call_timeout(self):
Comment thread
cicoyle marked this conversation as resolved.
continuation = Mock()
request = Mock()
client_call_details = Mock()
client_call_details.method = 'method'
client_call_details.timeout = None
client_call_details.metadata = 'metadata'
client_call_details.credentials = 'credentials'
client_call_details.wait_for_ready = 'wait_for_ready'
client_call_details.compression = 'compression'

DaprClientTimeoutInterceptor().intercept_unary_unary(
continuation, client_call_details, request
)
continuation.assert_called_once_with(client_call_details, request)
16 changes: 16 additions & 0 deletions tests/clients/test_timeout_interceptor_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,19 @@ def test_intercept_unary_unary_without_timeout(self):
)
called_client_call_details = continuation.call_args[0][0]
self.assertEqual(7, called_client_call_details.timeout)

@patch.object(settings, 'DAPR_API_TIMEOUT_SECONDS', None)
def test_intercept_unary_unary_no_global_timeout_no_per_call_timeout(self):
continuation = Mock()
request = Mock()
client_call_details = Mock()
client_call_details.method = 'method'
client_call_details.timeout = None
client_call_details.metadata = 'metadata'
client_call_details.credentials = 'credentials'
client_call_details.wait_for_ready = 'wait_for_ready'

DaprClientTimeoutInterceptorAsync().intercept_unary_unary(
continuation, client_call_details, request
)
continuation.assert_called_once_with(client_call_details, request)
Loading