From 46016d1d9bb052f370ff1784b654867b06e72f2e Mon Sep 17 00:00:00 2001 From: Heiko Nickerl Date: Sat, 29 Aug 2026 09:55:57 +0200 Subject: [PATCH 1/3] fix: allow disabling journal and idempotency retention Setting retention times to `timedelta(seconds=0)` was silently dropped; instead, the global defaults were used. Now, settings the retention times to 0s properly disables the rentention. --- python/restate/discovery.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/python/restate/discovery.py b/python/restate/discovery.py index b6a0d95..4fc45ea 100644 --- a/python/restate/discovery.py +++ b/python/restate/discovery.py @@ -346,14 +346,16 @@ def compute_discovery(endpoint: RestateEndpoint, discovered_as: typing.Literal[" description=handler.description, metadata=handler.metadata, inactivityTimeout=int(handler.inactivity_timeout.total_seconds() * 1000) - if handler.inactivity_timeout + if handler.inactivity_timeout is not None + else None, + abortTimeout=int(handler.abort_timeout.total_seconds() * 1000) + if handler.abort_timeout is not None else None, - abortTimeout=int(handler.abort_timeout.total_seconds() * 1000) if handler.abort_timeout else None, journalRetention=int(handler.journal_retention.total_seconds() * 1000) - if handler.journal_retention + if handler.journal_retention is not None else None, idempotencyRetention=int(handler.idempotency_retention.total_seconds() * 1000) - if handler.idempotency_retention + if handler.idempotency_retention is not None else None, workflowCompletionRetention=int(handler.workflow_retention.total_seconds() * 1000) if handler.workflow_retention @@ -394,14 +396,16 @@ def compute_discovery(endpoint: RestateEndpoint, discovered_as: typing.Literal[" description=description, metadata=metadata, inactivityTimeout=int(service.inactivity_timeout.total_seconds() * 1000) - if service.inactivity_timeout + if service.inactivity_timeout is not None + else None, + abortTimeout=int(service.abort_timeout.total_seconds() * 1000) + if service.abort_timeout is not None else None, - abortTimeout=int(service.abort_timeout.total_seconds() * 1000) if service.abort_timeout else None, journalRetention=int(service.journal_retention.total_seconds() * 1000) - if service.journal_retention + if service.journal_retention is not None else None, idempotencyRetention=int(service.idempotency_retention.total_seconds() * 1000) - if service.idempotency_retention + if service.idempotency_retention is not None else None, enableLazyState=service.enable_lazy_state if isinstance(service, (Workflow, VirtualObject)) else None, ingressPrivate=service.ingress_private, From 12911685a12992b3f68a0aaf5b78d1edf73ccf87 Mon Sep 17 00:00:00 2001 From: Heiko Nickerl Date: Wed, 2 Sep 2026 11:50:23 +0200 Subject: [PATCH 2/3] fix: fix settings workflow retention to 0s --- python/restate/discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/restate/discovery.py b/python/restate/discovery.py index 4fc45ea..0bd22dc 100644 --- a/python/restate/discovery.py +++ b/python/restate/discovery.py @@ -358,7 +358,7 @@ def compute_discovery(endpoint: RestateEndpoint, discovered_as: typing.Literal[" if handler.idempotency_retention is not None else None, workflowCompletionRetention=int(handler.workflow_retention.total_seconds() * 1000) - if handler.workflow_retention + if handler.workflow_retention is not None else None, enableLazyState=handler.enable_lazy_state, ingressPrivate=handler.ingress_private, From ff5b9360eb70d8e8c6024df9462e8343b54d586d Mon Sep 17 00:00:00 2001 From: Heiko Nickerl Date: Wed, 2 Sep 2026 11:53:29 +0200 Subject: [PATCH 3/3] test: ensure retention of 0s timedeltas --- tests/discovery.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/discovery.py diff --git a/tests/discovery.py b/tests/discovery.py new file mode 100644 index 0000000..8fe26fc --- /dev/null +++ b/tests/discovery.py @@ -0,0 +1,57 @@ +# +# Copyright (c) 2023-2026 - Restate Software, Inc., Restate GmbH +# +# This file is part of the Restate SDK for Python, +# which is released under the MIT license. +# +# You can find a copy of the license in file LICENSE in the root +# directory of this repository or package, or at +# https://github.com/restatedev/sdk-typescript/blob/main/LICENSE +# +from datetime import timedelta + +import restate +from restate.discovery import compute_discovery +from restate.endpoint import Endpoint + + +def test_zero_durations_are_preserved_in_manifest(): + service = restate.Service( + "ZeroDurationService", + inactivity_timeout=timedelta(0), + abort_timeout=timedelta(0), + journal_retention=timedelta(0), + idempotency_retention=timedelta(0), + ) + + @service.handler( + inactivity_timeout=timedelta(0), + abort_timeout=timedelta(0), + journal_retention=timedelta(0), + idempotency_retention=timedelta(0), + ) + async def handle(ctx: restate.Context): + pass + + workflow = restate.Workflow("ZeroRetentionWorkflow") + + @workflow.main(workflow_retention=timedelta(0)) + async def run(ctx: restate.WorkflowContext): + pass + + manifest = compute_discovery(Endpoint().bind(service, workflow), "bidi") + + service_manifest = manifest.services[0] + assert service_manifest.inactivityTimeout == 0 + assert service_manifest.abortTimeout == 0 + assert service_manifest.journalRetention == 0 + assert service_manifest.idempotencyRetention == 0 + + handler_manifest = service_manifest.handlers[0] + assert handler_manifest.inactivityTimeout == 0 + assert handler_manifest.abortTimeout == 0 + assert handler_manifest.journalRetention == 0 + assert handler_manifest.idempotencyRetention == 0 + + workflow_handler_manifest = manifest.services[1].handlers[0] + assert workflow_handler_manifest.workflowCompletionRetention == 0