Skip to content

Commit 6daa85a

Browse files
author
Magne Helleborg
authored
Improved control over aggregate operations timing (#229)
* Remove duplicate tracing on event handling * Increased request deduplication for Aggregate cluster actors, to support very long aggregates being rehydrated. * Added the ability to set a default aggregate perform timeout, which will be used if the user does not provide a CancellationToken. Not enabled by default
1 parent bb39360 commit 6daa85a

6 files changed

Lines changed: 55 additions & 5 deletions

File tree

Source/Aggregates/Actors/AggregateClusterKindFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ public static ClusterKind CreateKind(IServiceProvider serviceProvider)
3030
var idleUnloadTimeout = serviceProvider.GetRequiredService<AggregateUnloadTimeout>()();
3131

3232
return new ClusterKind(aggregateRootType.Id.Value.ToString(),
33-
Props.FromProducer(() => new AggregateActor<TAggregate>(providerForTenant, logger, idleUnloadTimeout)).WithClusterRequestDeduplication());
33+
Props.FromProducer(() => new AggregateActor<TAggregate>(providerForTenant, logger, idleUnloadTimeout)).WithClusterRequestDeduplication(TimeSpan.FromMinutes(5)));
3434
}
3535
}

Source/Aggregates/AggregateRootOperations.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414

1515
namespace Dolittle.SDK.Aggregates;
1616

17+
/// <summary>
18+
/// If no CancellationToken is used, this delegate will be called to use the default timeout.
19+
/// </summary>
20+
public delegate CancellationToken DefaultAggregatePerformTimeout();
21+
22+
1723
/// <summary>
1824
/// Represents an implementation of <see cref="IAggregateRootOperations{T}"/>.
1925
/// </summary>
@@ -23,6 +29,7 @@ public class AggregateRootOperations<TAggregate> : IAggregateRootOperations<TAgg
2329
{
2430
readonly EventSourceId _eventSourceId;
2531
readonly IRootContext _context;
32+
readonly DefaultAggregatePerformTimeout _defaultTimeout;
2633
readonly ClusterIdentity _clusterIdentity;
2734

2835
/// <summary>
@@ -31,9 +38,11 @@ public class AggregateRootOperations<TAggregate> : IAggregateRootOperations<TAgg
3138
/// <param name="eventSourceId">The <see cref="EventSourceId"/> of the aggregate root instance.</param>
3239
/// <param name="tenantId">The <see cref="TenantId"/> of the current tenant.</param>
3340
/// <param name="context">The <see cref="IRootContext" />Root context used to communicate with actors</param>
34-
public AggregateRootOperations(EventSourceId eventSourceId, TenantId tenantId, IRootContext context)
41+
/// <param name="defaultTimeout">The &lt;see cref="DefaultAggregatePerformTimeout" /&gt; Used if no cancellation token is passed.</param>
42+
public AggregateRootOperations(EventSourceId eventSourceId, TenantId tenantId, IRootContext context, DefaultAggregatePerformTimeout defaultTimeout)
3543
{
3644
_context = context;
45+
_defaultTimeout = defaultTimeout;
3746
_eventSourceId = eventSourceId;
3847
_clusterIdentity = ClusterIdentityMapper.GetClusterIdentity<TAggregate>(tenantId, eventSourceId);
3948
}
@@ -51,6 +60,10 @@ public Task Perform(Action<TAggregate> method, CancellationToken cancellationTok
5160
/// <inheritdoc/>
5261
public async Task Perform(Func<TAggregate, Task> method, CancellationToken cancellationToken = default)
5362
{
63+
if(cancellationToken == default)
64+
{
65+
cancellationToken = _defaultTimeout();
66+
}
5467
using var activity = Tracing.ActivitySource.StartActivity($"{typeof(TAggregate).Name}.Perform")
5568
?.Tag(_eventSourceId);
5669

Source/Events.Handling/EventHandler.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ public EventHandler(
7676
public async Task Handle(object @event, EventType eventType, EventContext context, IServiceProvider serviceProvider, CancellationToken cancellation)
7777
{
7878
var time = Stopwatch.StartNew();
79-
using var activity = @event is not HandleEventRequest ? context.CommittedExecutionContext.StartChildActivity($"{_activityName}{@event.GetType().Name}")
80-
?.Tag(eventType) : null;
79+
using var activity = context.CommittedExecutionContext.StartChildActivity($"{_activityName}{@event.GetType().Name}");
8180

8281
try
8382
{

Source/SDK/Builders/DolittleClientConfiguration.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,13 @@ public class DolittleClientConfiguration : IConfigurationBuilder
4242
/// <summary>
4343
/// How long should the aggregates be kept in memory when not in use.
4444
/// </summary>
45-
public TimeSpan AggregateIdleTimeout { get; private set; } = TimeSpan.FromSeconds(20);
45+
public TimeSpan AggregateIdleTimeout { get; private set; } = TimeSpan.FromSeconds(30);
46+
47+
/// <summary>
48+
/// The default timeout for performing an aggregate operation (potentially including hydration & commits).
49+
/// If no CancellationToken is provided, this timeout will be used.
50+
/// </summary>
51+
public TimeSpan? DefaultAggregatePerformTimeout { get; private set; }
4652

4753
/// <summary>
4854
/// Gets the event serializer provider.
@@ -195,6 +201,13 @@ public IConfigurationBuilder WithAggregateIdleTimout(TimeSpan timeout)
195201
AggregateIdleTimeout = timeout;
196202
return this;
197203
}
204+
205+
/// <inheritdoc />
206+
public IConfigurationBuilder WithDefaultAggregatePerformTimeout(TimeSpan timeout)
207+
{
208+
DefaultAggregatePerformTimeout = timeout;
209+
return this;
210+
}
198211

199212
/// <inheritdoc />
200213
public IConfigurationBuilder WithServiceProvider(IServiceProvider serviceProvider)

Source/SDK/Builders/IConfigurationBuilder.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ public interface IConfigurationBuilder
6868
/// <returns></returns>
6969
public IConfigurationBuilder WithAggregateIdleTimout(TimeSpan timeout);
7070

71+
/// <summary>
72+
/// Sets the default timeout for performing an aggregate operation (potentially including hydration & commits).
73+
/// If no CancellationToken is provided, this timeout will be used.
74+
/// Not set by default
75+
/// </summary>
76+
/// <param name="timeout"></param>
77+
/// <returns></returns>
78+
public IConfigurationBuilder WithDefaultAggregatePerformTimeout(TimeSpan timeout);
79+
7180
/// <summary>
7281
/// Configures the root <see cref="IServiceProvider"/> for the <see cref="IDolittleClient"/>.
7382
/// </summary>

Source/SDK/Proto/ServiceCollectionExtensions.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Collections.Immutable;
7+
using Dolittle.SDK.Aggregates;
78
using Dolittle.SDK.Aggregates.Actors;
89
using Dolittle.SDK.Builders;
910
using Microsoft.Extensions.DependencyInjection;
@@ -40,6 +41,21 @@ public static IServiceCollection AddProtoInfra(this IServiceCollection services,
4041
var timeout = dolittleClientConfiguration.AggregateIdleTimeout;
4142

4243
return () => timeout;
44+
})
45+
.AddSingleton<DefaultAggregatePerformTimeout>(sp =>
46+
{
47+
var dolittleClientConfiguration = sp.GetRequiredService<DolittleClientConfiguration>();
48+
var timeout = dolittleClientConfiguration.DefaultAggregatePerformTimeout;
49+
if (timeout is not null)
50+
{
51+
var seconds = (int)Math.Ceiling(timeout.Value.TotalSeconds);
52+
if (seconds > 0)
53+
{
54+
return () => CancellationTokens.FromSeconds(seconds);
55+
}
56+
}
57+
58+
return () => default;
4359
});
4460
}
4561

0 commit comments

Comments
 (0)