Skip to content

Added support to actor timers get/list methods - #1949

Merged
WhitWaldo merged 9 commits into
dapr:masterfrom
m3nax:feat/actor-timers-get-list
Oct 22, 2026
Merged

WhitWaldo merged 9 commits into
dapr:masterfrom
m3nax:feat/actor-timers-get-list

Conversation

@m3nax

@m3nax m3nax commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Description

Add support for actor list/get timers runtime methods dapr/dapr#10461

Issue reference

We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.

Please reference the issue this PR will close: #1948

Checklist

Please make sure you've completed the relevant tasks for this PR, out of the following list:

  • Code compiles correctly
  • Created/updated tests
  • Extended the documentation

m3nax added 3 commits October 21, 2026 17:05
Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com>
Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com>
Signed-off-by: Manuel Menegazzo <65919883+m3nax@users.noreply.github.com>
@m3nax
m3nax marked this pull request as ready for review October 21, 2026 17:02
@m3nax
m3nax requested review from a team as code owners October 21, 2026 17:02
@WhitWaldo
WhitWaldo requested a balanced review from Copilot October 21, 2026 19:27
@WhitWaldo WhitWaldo added this to the v1.19 milestone Oct 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

TTL timestamp parsing breaks sidecar queries, and completed local one-shot timers remain queryable.

2 open issues
What changed in this PR

Adds actor timer get/list support across Actors.Next schedulers for the Dapr 1.19 runtime APIs, addressing #1948.

Changes:

  • Adds timer query contracts, sidecar mappings, and local scheduler implementations.
  • Adds unit and real-sidecar coverage.
  • Introduces an HTTP timer example with Aspire hosting.
File Description
test/​Dapr.IntegrationTest.Actors.Next/​RealSidecarActorTests.cs Tests real-sidecar timer queries.
test/​Dapr.Actors.Next.Testing.Test/​ActorTestRuntimeTests.cs Tests virtual timer queries and expiry.
test/​Dapr.Actors.Next.StateMachine.Test/​StateMachineEdgeTests.cs Updates the scheduler stub.
test/​Dapr.Actors.Next.Core.Test/​TimerConcurrencyTests.cs Tests local timer queries.
test/​Dapr.Actors.Next.Core.Test/​SidecarAdapterTests.cs Tests request mapping and authentication.
src/​Dapr.Actors.Next.Testing/​VirtualActorTimeProvider.cs Implements virtual timer queries.
src/​Dapr.Actors.Next.Core/​Timers/​IActorTimerScheduler.cs Defines get/list methods.
src/​Dapr.Actors.Next.Core/​Timers/​DaprSidecarActorTimerScheduler.cs Implements sidecar timer queries.
src/​Dapr.Actors.Next.Core/​Timers/​DaprSidecarActorReminderScheduler.cs Reuses shared payload decoding.
src/​Dapr.Actors.Next.Core/​Timers/​CoreActorTimerScheduler.cs Stores queryable timer metadata.
src/​Dapr.Actors.Next.Core/​Timers/​ActorTimerInfo.cs Adds timer result records.
src/​Dapr.Actors.Next.Core/​Timers/​ActorSchedulePayloadDecoder.cs Extracts shared payload decoding.
examples/​Actor.Next/​README.md Links the timer example.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.Tests/​Timers.Next.Example08.Tests.csproj Defines the example test project.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.Tests/​TimerDemoActorTests.cs Tests the example with virtual time.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.http Provides sample HTTP requests.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.csproj Defines the example application.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.AppHost/​Timers.Next.Example08.AppHost.csproj Defines Aspire hosting dependencies.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.AppHost/​Properties/​launchSettings.json Configures dashboard launch settings.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.AppHost/​Directory.Build.props Imports target framework settings.
examples/​Actor.Next/​08-Timers/​Timers.Next.Example08.AppHost/​AppHost.cs Configures application and sidecar hosting.
examples/​Actor.Next/​08-Timers/​TimerDemoActor.cs Demonstrates timer management.
examples/​Actor.Next/​08-Timers/​README.md Documents setup and usage.
examples/​Actor.Next/​08-Timers/​Properties/​launchSettings.json Configures application launch settings.
examples/​Actor.Next/​08-Timers/​Program.cs Exposes HTTP endpoints and demo UI.
examples/​Actor.Next/​08-Timers/​appsettings.json Configures HTTP and gRPC endpoints.
all.sln Includes the example projects.

Review effort: Balanced

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Payload decoding can corrupt timer data, microsecond periods can break inspection, and one-shot metadata differs across schedulers.

1 open issue
3 previously missed issues

In code that hasn't changed since last review

Medium severity Normalize infinite timer periods to null in stored timer info

src/​Dapr.Actors.Next.Core/​Timers/​CoreActorTimerScheduler.cs:188

Scheduling with period: Timeout.InfiniteTimeSpan creates a one-shot timer, but Get/List expose its period as -1 ms here. The sidecar scheduler omits negative periods and returns null instead, so consumers see different metadata for the same registration in memory and in production. Normalize negative periods to null in the stored timer info.

Medium severity Support Go microsecond suffix in timer duration parsing

src/​Dapr.Actors.Next.Core/​Timers/​DaprSidecarActorTimerScheduler.cs:300

The runtime returns a 250-microsecond timer period as @every 250µs, but ActorScheduleDurationParser recognizes only ASCII us. As a result, GetAsync throws for that timer, and one such timer makes the entire ListAsync call throw. Accept Go's µs suffix in both the parser's regular expression and unit conversion, and cover it in get/list response tests.

Medium severity Normalize virtual scheduler infinite periods to null

src/​Dapr.Actors.Next.Testing/​VirtualActorTimeProvider.cs:839

The virtual scheduler also exposes Timeout.InfiniteTimeSpan as a -1 ms Period for one-shot timers. The sidecar returns null for this registration, so tests using virtual time observe different Get/List metadata from production. Normalize negative original periods to null when creating timer info, without changing reminder metadata.

2 resolved since last review

Review effort: Balanced

@m3nax
m3nax marked this pull request as draft October 21, 2026 20:43
m3nax added 2 commits October 21, 2026 22:45
Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>
Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Valid sidecar timing formats can cause timer lookup and listing to fail during parsing.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Support RFC 3339 absolute due times in timer mappings

src/​Dapr.Actors.Next.Core/​Timers/​DaprSidecarActorTimerScheduler.cs:289

Dapr accepts an absolute RFC 3339 dueTime and preserves that input in get/list responses. A timer registered with a timestamp, for example through HTTP, therefore reaches this duration-only parser and throws FormatException. The same mapping at line 299 makes one such timer prevent listing all timers for the actor. Define how absolute due times are represented in ActorTimerInfo, handle them in both mappings, and add get/list tests for timestamp-based registrations.

Medium severity Handle Go microsecond units in timer duration parsing

src/​Dapr.Actors.Next.Core/​Timers/​DaprSidecarActorTimerScheduler.cs:300

A timer scheduled with a 0.5 ms period is returned by Dapr as @every 500µs. ActorScheduleDurationParser accepts ASCII us, but not Go's µs, so GetAsync throws FormatException; one such timer also makes the entire ListAsync call fail. Normalize the unit in both mappings, or support it in the shared parser, and add get/list regression coverage.

1 resolved since last review

Review effort: Balanced

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Valid timer durations can break queries, and one-shot period metadata does not consistently honor the public contract.

No open issues 🎉

4 previously missed issues

In code that hasn't changed since last review

Medium severity Support Go-style duration formats in HTTP timer parsing

src/​Dapr.Actors.Next.Core/​Timers/​ActorScheduleDurationParser.cs:58

HTTP timers can use Go due times such as +1h, .5s, 1.s, and 1h.5m, which the runtime echoes unchanged. Parse cannot fully consume these forms and falls back to TimeSpan.Parse, which rejects their unit suffixes. As a result, GetAsync throws and one such registration makes ListAsync fail for the entire actor. Support the leading sign and leading/trailing decimal-point forms in the shared parser, with regression tests for these inputs.

Medium severity Normalize nonpositive timer periods to null metadata

src/​Dapr.Actors.Next.Core/​Timers/​CoreActorTimerScheduler.cs:188

Passing TimeSpan.Zero or Timeout.InfiniteTimeSpan as the period creates a one-shot timer, but this stores a non-null Period in its get/list metadata. That contradicts ActorTimerInfo.Period's null-for-one-shot contract and misclassifies the timer for callers using Period.HasValue. Normalize nonpositive periods to null when constructing the metadata, and cover both values in the core scheduler tests.

Medium severity Map sidecar zero-period timers to null periods

src/​Dapr.Actors.Next.Core/​Timers/​DaprSidecarActorTimerScheduler.cs:316

An explicitly registered zero period is returned by the sidecar as @every 0s for a one-shot timer. This mapping exposes TimeSpan.Zero instead of the null promised by ActorTimerInfo.Period, so callers using Period.HasValue misclassify it as periodic. Normalize nonpositive parsed periods to null and add a sidecar mapping test for @every 0s.

Medium severity Normalize virtual scheduler one-shot periods to null

src/​Dapr.Actors.Next.Testing/​VirtualActorTimeProvider.cs:839

The virtual scheduler treats zero and Timeout.InfiniteTimeSpan periods as one-shot, but this mapping returns those values as a non-null Period. This violates ActorTimerInfo.Period's null-for-one-shot contract and misclassifies timers for callers using Period.HasValue in ActorTestRuntime. Normalize nonpositive periods to null here and cover both inputs in the virtual scheduler tests.

Review effort: Balanced

…d and decimal durations

Signed-off-by: Manuel Menegazzo <manuel.menegazzo@outlook.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Timer visibility differs between in-memory and sidecar execution, and the integration test's version gate permits an unsupported default runtime.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Keep one-shot timers queryable until callback dispatch completes

src/​Dapr.Actors.Next.Core/​Timers/​ScheduledActorCallback.cs:195

One-shot timers become unqueryable before their callbacks finish. Here, onCompleted removes the registration before runtime.DispatchAsync; the direct VirtualActorTimeProvider scheduler similarly sets Canceled = true before dispatch, so its new get/list methods exclude the timer. In the runtime implementation linked by this PR, removal happens after CallReminder returns. Consequently, a callback inspecting its own timer can find it through the sidecar but not through either in-memory scheduler. Keep an admitted one-shot queryable until dispatch finishes, including failure, without admitting it twice. Preserve cancellation and replacement identity checks, and add callback-level get/list tests for both implementations.

Review effort: Balanced

@WhitWaldo

Copy link
Copy Markdown
Contributor

@m3nax If you can confirm this is safe to take out of draft, I'd be happy to merge it into the 1.19 release (but note that you have about 6 hours until it'll have to wait for an SDK patch release).

@m3nax
m3nax marked this pull request as ready for review October 22, 2026 06:02
@m3nax

m3nax commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

@WhitWaldo Some checks seem to be failing, but I don’t think they’re related to my changes. Could you re-run them manually? If they pass, I think everything’s ready.

@WhitWaldo WhitWaldo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me - thank you for putting together this contribution!

@WhitWaldo
WhitWaldo merged commit 88adf78 into dapr:master Oct 22, 2026
1224 of 1227 checks passed
@m3nax
m3nax deleted the feat/actor-timers-get-list branch October 22, 2026 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add support for actor list/get timers

3 participants