Repository navigation
[DO NOT MERGE] Spike managing saga timeouts - #6025
Draft
SzymonPobiega wants to merge 1 commit into
Draft
SzymonPobiega wants to merge 1 commit into
SzymonPobiega wants to merge 1 commit into
Conversation
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The primary instance keeps a projection of saga timeouts reported by endpoints and lets an operator reschedule, trigger, or cancel a pending timeout. This spike implements RavenDB persistence only.
The design is in docs/saga-timeout-management-design.md, added by this pull request. The endpoint side is Particular/NServiceBus#7996, and the ServicePulse pages are Particular/ServicePulse#3166.
The design rests on one rule: the endpoint owns the timeout, and ServiceControl keeps a projection of it. ServiceControl never changes a timeout directly. An operator action is a request sent to the endpoint, which applies it to the saga and reports the result like any other change.
Changes
namespace NServiceBus, soNServiceBus.EnclosedMessageTypesresolves by full name, as forEndpointUsageReport. One handler applies them throughISagaTimeoutDataStore.(Version, Rank). Requested and rescheduled have rank 0; triggered and canceled have rank 1, so a cancel wins a tie with a reschedule at the same version. Each history entry has a deduplication key, so a duplicate report changes nothing. A rejection for an unknown timeout creates anUnknownrecord that lists exclude.SagaCompleteddeletes the saga's timeouts and stores a completed-saga marker, so late reports for that saga are ignored.SagaTimeouts/{TimeoutId}document with the current state and up to 100 history entries. Payloads are attachments namedpayload-v{Version}, andSagaTimeoutsIndexserves the list queries. Finished timeouts and markers expire afterErrorRetentionPeriod.GET api/timeoutswithstatus, filters, paging, and sortingGET api/timeouts/countsGET api/timeouts/{id}GET api/sagas/{sagaId}/timeoutsPOST api/timeouts/{id}/reschedule,/trigger, and/cancelDELETE api/timeouts/{id}error:timeouts:view, in the Read role, anderror:timeouts:manage, in the Operate role.expected_versionand records a pending operation. It returns 202, 409 for a stale version or another pending operation, or 422 when the timeout is not pending or has no management address. The request then goes to the endpoint's satellite queue from a handler, in the handler's transport transaction, and is written to the message action audit log.Constraints and deviations from the design
NotSupportedException. A report on a SQL-backed instance fails and goes to the error queue. Their test projects exclude the new shared persistence tests.SagaCompletedfor the same saga are processed at the same time, a timeout can be left behind. The completion finds the documents to delete through the index. Forget removes such a timeout.Verification
Unknowntimeouts, operator request outcomes, and forget.ServiceControl.Persistence.Tests.RavenDB: 371 passed.ServiceControl.UnitTests: 461 passed, including satellite request headers and deserializing a report the way the endpoint sends it.ServiceControl.Infrastructure.Tests: 115 passed.NServiceBus.SagaTimeoutRequestedwith no assembly name and no message id header, then reads it through the API.🤖 Generated with Claude Code