Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Actor State TTL #1164

Merged
merged 20 commits into from
Jan 8, 2024
Merged
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
4 changes: 2 additions & 2 deletions examples/Actor/ActorClient/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,7 +46,7 @@ public static async Task Main(string[] args)
var proxy = ActorProxy.Create<IDemoActor>(actorId, "DemoActor");

Console.WriteLine("Making call using actor proxy to save data.");
await proxy.SaveData(data);
await proxy.SaveData(data, TimeSpan.FromMinutes(10));
Console.WriteLine("Making call using actor proxy to get data.");
var receivedData = await proxy.GetData();
Console.WriteLine($"Received data is {receivedData}.");
Expand Down
10 changes: 5 additions & 5 deletions examples/Actor/DemoActor/DemoActor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,12 +41,12 @@ public DemoActor(ActorHost host, BankService bank)
this.bank = bank;
}

public async Task SaveData(MyData data)
public async Task SaveData(MyData data, TimeSpan ttl)
{
Console.WriteLine($"This is Actor id {this.Id} with data {data}.");

// Set State using StateManager, state is saved after the method execution.
await this.StateManager.SetStateAsync<MyData>(StateName, data);
await this.StateManager.SetStateAsync<MyData>(StateName, data, ttl);
}

public Task<MyData> GetData()
Expand Down Expand Up @@ -109,7 +109,7 @@ public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSp
// This method is invoked when an actor reminder is fired.
var actorState = await this.StateManager.GetStateAsync<MyData>(StateName);
actorState.PropertyB = $"Reminder triggered at '{DateTime.Now:yyyy-MM-ddTHH:mm:ss}'";
await this.StateManager.SetStateAsync<MyData>(StateName, actorState);
await this.StateManager.SetStateAsync<MyData>(StateName, actorState, ttl: TimeSpan.FromMinutes(5));
}

class TimerParams
Expand Down Expand Up @@ -173,7 +173,7 @@ public async Task TimerCallback(byte[] data)
{
var state = await this.StateManager.GetStateAsync<MyData>(StateName);
state.PropertyA = $"Timer triggered at '{DateTime.Now:yyyyy-MM-ddTHH:mm:s}'";
await this.StateManager.SetStateAsync<MyData>(StateName, state);
await this.StateManager.SetStateAsync<MyData>(StateName, state, ttl: TimeSpan.FromMinutes(5));
var timerParams = JsonSerializer.Deserialize<TimerParams>(data);
Console.WriteLine("Timer parameter1: " + timerParams.IntParam);
Console.WriteLine("Timer parameter2: " + timerParams.StringParam);
Expand Down
5 changes: 3 additions & 2 deletions examples/Actor/IDemoActor/IDemoActor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,8 +27,9 @@ public interface IDemoActor : IActor
/// Method to save data.
/// </summary>
/// <param name="data">DAta to save.</param>
/// <param name="ttl">TTL of state key.</param>
/// <returns>A task that represents the asynchronous save operation.</returns>
Task SaveData(MyData data);
Task SaveData(MyData data, TimeSpan ttl);

/// <summary>
/// Method to get data.
Expand Down
50 changes: 50 additions & 0 deletions src/Dapr.Actors/Communication/ActorStateResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// ------------------------------------------------------------------------
// Copyright 2023 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Actors.Communication
{
using System;

/// <summary>
/// Represents a response from fetching an actor state key.
/// </summary>
public class ActorStateResponse<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="ActorStateResponse{T}"/> class.
/// </summary>
/// <param name="value">The response value.</param>
/// <param name="ttlExpireTime">The time to live expiration time.</param>
public ActorStateResponse(T value, DateTimeOffset? ttlExpireTime)
{
this.Value = value;
this.TTLExpireTime = ttlExpireTime;
}

/// <summary>
/// Gets the response value as a string.
/// </summary>
/// <value>
/// The response value as a string.
/// </value>
public T Value { get; }

/// <summary>
/// Gets the time to live expiration time.
/// </summary>
/// <value>
/// The time to live expiration time.
/// </value>
public DateTimeOffset? TTLExpireTime { get; }
}
}
3 changes: 2 additions & 1 deletion src/Dapr.Actors/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@ internal static class Constants
public const string RequestHeaderName = "X-DaprRequestHeader";
public const string ErrorResponseHeaderName = "X-DaprErrorResponseHeader";
public const string ReentrancyRequestHeaderName = "Dapr-Reentrancy-Id";
public const string TTLResponseHeaderName = "Metadata.ttlExpireTime";
public const string Dapr = "dapr";
public const string Config = "config";
public const string State = "state";
Expand Down
15 changes: 13 additions & 2 deletions src/Dapr.Actors/DaprHttpInteractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public DaprHttpInteractor(
this.httpClient.Timeout = requestTimeout ?? this.httpClient.Timeout;
}

public async Task<string> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default)
public async Task<ActorStateResponse<string>> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We want to keep ActorStateResponse as an internal type. How do we keep complication happy with this constraint?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@philliphoff any ideas here?

Copy link
Collaborator

@philliphoff philliphoff Oct 24, 2023

Choose a reason for hiding this comment

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

ActorStateResponse is already internal, no, or are you referring to the fact that this method is public? IDaprHttpInteractor and DaprHttpInteractor are already internal types, so nothing of them can be seen outside the SDK. The public on the method only applies to other types within the SDK. (Method visibility is always masked by visibility of the underlying type.)

{
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName);

Expand All @@ -72,7 +72,18 @@ HttpRequestMessage RequestFunc()

using var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
var stringResponse = await response.Content.ReadAsStringAsync();
return stringResponse;

DateTimeOffset? ttlExpireTime = null;
if (response.Headers.TryGetValues(Constants.TTLResponseHeaderName, out IEnumerable<string> headerValues))
{
var ttlExpireTimeString = headerValues.First();
if (!string.IsNullOrEmpty(ttlExpireTimeString))
{
ttlExpireTime = DateTime.Parse(ttlExpireTimeString, CultureInfo.InvariantCulture);
}
}

return new ActorStateResponse<string>(stringResponse, ttlExpireTime);
}

public Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default)
Expand Down
4 changes: 2 additions & 2 deletions src/Dapr.Actors/IDaprInteractor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,7 +52,7 @@ internal interface IDaprInteractor
/// <param name="keyName">Name of key to get value for.</param>
/// <param name="cancellationToken">Cancels the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
Task<string> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default);
Task<ActorStateResponse<string>> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default);

/// <summary>
/// Invokes Actor method.
Expand Down
17 changes: 15 additions & 2 deletions src/Dapr.Actors/Runtime/ActorStateChange.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,14 +27,16 @@ public sealed class ActorStateChange
/// <param name="type">The type of value associated with given actor state name.</param>
/// <param name="value">The value associated with given actor state name.</param>
/// <param name="changeKind">The kind of state change for given actor state name.</param>
public ActorStateChange(string stateName, Type type, object value, StateChangeKind changeKind)
/// <param name="ttlExpireTime">The time to live for the state.</param>
public ActorStateChange(string stateName, Type type, object value, StateChangeKind changeKind, DateTimeOffset? ttlExpireTime)
{
ArgumentVerifier.ThrowIfNull(stateName, nameof(stateName));

this.StateName = stateName;
this.Type = type;
this.Value = value;
this.ChangeKind = changeKind;
this.TTLExpireTime = ttlExpireTime;
}

/// <summary>
Expand Down Expand Up @@ -68,5 +70,16 @@ public ActorStateChange(string stateName, Type type, object value, StateChangeKi
/// The kind of state change for given actor state name.
/// </value>
public StateChangeKind ChangeKind { get; }

/// <summary>
/// Gets the time to live for the state.
/// </summary>
/// <value>
/// The time to live for the state.
/// </value>
/// <remarks>
/// If null, the state will not expire.
/// </remarks>
public DateTimeOffset? TTLExpireTime { get; }
}
}
Loading
Loading