Skip to content
Open
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
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(
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(
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):
Copy link
Member

Choose a reason for hiding this comment

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

Don't we need a test that covers DAPR_API_TIMEOUT_SECONDS being None and timeout having a value?

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