Skip to content

Commit 752d861

Browse files
committed
Grain filters + attributes
1 parent 961d814 commit 752d861

File tree

53 files changed

+1297
-242
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1297
-242
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Orleans.Hosting;
2+
3+
namespace ManagedCode.Orleans.RateLimiting.Client.Extensions;
4+
5+
public static class ClientBuilderExtensions
6+
{
7+
public static IClientBuilder AddOrleansRateLimiting(this IClientBuilder clientBuilder)
8+
{
9+
return clientBuilder;
10+
}
11+
}

ManagedCode.Orleans.RateLimiting.Client/ManagedCode.Orleans.RateLimiting.Client.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@
1818

1919
<ItemGroup>
2020

21-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
22-
<PackageReference Include="Microsoft.Orleans.Client" Version="7.1.1"/>
23-
<PackageReference Include="System.Threading.RateLimiting" Version="7.0.0"/>
21+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
22+
<PackageReference Include="Microsoft.Orleans.Client" Version="7.1.1" />
23+
<PackageReference Include="System.Threading.RateLimiting" Version="7.0.0" />
2424

2525
</ItemGroup>
2626

2727
<ItemGroup>
28-
<ProjectReference Include="..\ManagedCode.Orleans.RateLimiting.Core\ManagedCode.Orleans.RateLimiting.Core.csproj"/>
28+
<ProjectReference Include="..\ManagedCode.Orleans.RateLimiting.Core\ManagedCode.Orleans.RateLimiting.Core.csproj" />
2929
</ItemGroup>
3030

3131
<ItemGroup>
32-
<Folder Include="Middlewares\"/>
32+
<Folder Include="Middlewares\" />
3333
</ItemGroup>
3434

3535
</Project>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Threading.RateLimiting;
3+
4+
namespace ManagedCode.Orleans.RateLimiting.Core.Attributes;
5+
6+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
7+
public class ConcurrencyLimiterAttribute : Attribute, ILimiterAttribute<ConcurrencyLimiterOptions>
8+
{
9+
public string? Key { get; }
10+
public KeyType KeyType { get; }
11+
public ConcurrencyLimiterOptions? Options { get; }
12+
13+
/// <summary>
14+
/// ConcurrencyLimiterAttribute
15+
/// </summary>
16+
/// <param name="keyType">What key to use to identify RateLimiting, can be overridden by setting key property</param>
17+
/// <param name="key">Custom string as key for RateLimiting</param>
18+
/// <param name="permitLimit">
19+
/// Maximum number of permits that can be leased concurrently.
20+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="ConcurrencyLimiter"/>.
21+
/// </param>
22+
/// <param name="queueLimit">
23+
/// Maximum number of permits that can be queued concurrently.
24+
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="ConcurrencyLimiter"/>.
25+
/// </param>
26+
/// <param name="queueProcessingOrder">Determines the behaviour of <see cref="RateLimiter.AcquireAsync"/> when not enough resources can be leased.</param>
27+
public ConcurrencyLimiterAttribute(KeyType keyType = KeyType.GrainId, string? key = null,
28+
int? permitLimit = null,
29+
int? queueLimit = null,
30+
QueueProcessingOrder queueProcessingOrder = QueueProcessingOrder.OldestFirst)
31+
{
32+
Key = key;
33+
KeyType = keyType;
34+
35+
//override keyType if key is set
36+
if (!string.IsNullOrEmpty(key))
37+
{
38+
KeyType = KeyType.Key;
39+
}
40+
41+
if (permitLimit.HasValue || queueLimit.HasValue)
42+
{
43+
Options = new ConcurrencyLimiterOptions()
44+
{
45+
PermitLimit = permitLimit ?? 1,
46+
QueueLimit = queueLimit ?? 1,
47+
QueueProcessingOrder = queueProcessingOrder
48+
};
49+
}
50+
}
51+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Threading.RateLimiting;
3+
4+
namespace ManagedCode.Orleans.RateLimiting.Core.Attributes;
5+
6+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
7+
public class FixedWindowRateLimiterAttribute : Attribute, ILimiterAttribute<FixedWindowRateLimiterOptions>
8+
{
9+
public string? Key { get; }
10+
public KeyType KeyType { get; }
11+
public FixedWindowRateLimiterOptions? Options { get; }
12+
13+
/// <summary>
14+
/// FixedWindowRateLimiterAttribute
15+
/// </summary>
16+
/// <param name="keyType">What key to use to identify RateLimiting, can be overridden by setting key property</param>
17+
/// <param name="key">Custom string as key for RateLimiting</param>
18+
/// <param name="window">
19+
/// Specifies the time window that takes in the requests.
20+
/// Must be set to a value greater than <see cref="TimeSpan.Zero" /> by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
21+
/// </param>
22+
/// <param name="permitLimit">
23+
/// Maximum number of permits that can be leased concurrently.
24+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
25+
/// </param>
26+
/// <param name="queueLimit">
27+
/// Maximum number of permits that can be queued concurrently.
28+
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
29+
/// </param>
30+
/// <param name="autoReplenishment">
31+
/// Maximum number of permit counters that can be allowed in a window.
32+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
33+
/// </param>
34+
/// <param name="queueProcessingOrder">Determines the behaviour of <see cref="RateLimiter.AcquireAsync"/> when not enough resources can be leased.</param>
35+
public FixedWindowRateLimiterAttribute(KeyType keyType = KeyType.GrainId, string? key = null,
36+
TimeSpan? window = null,
37+
int? permitLimit = null,
38+
int? queueLimit = null,
39+
bool autoReplenishment = true,
40+
QueueProcessingOrder queueProcessingOrder = QueueProcessingOrder.OldestFirst)
41+
{
42+
Key = key;
43+
KeyType = keyType;
44+
45+
//override keyType if key is set
46+
if (!string.IsNullOrEmpty(key))
47+
{
48+
KeyType = KeyType.Key;
49+
}
50+
51+
if (permitLimit.HasValue || queueLimit.HasValue || window.HasValue)
52+
{
53+
Options = new FixedWindowRateLimiterOptions()
54+
{
55+
Window = window ?? TimeSpan.FromSeconds(1),
56+
PermitLimit = permitLimit ?? 1,
57+
QueueLimit = queueLimit ?? 1,
58+
AutoReplenishment = autoReplenishment,
59+
QueueProcessingOrder = queueProcessingOrder
60+
};
61+
}
62+
}
63+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ManagedCode.Orleans.RateLimiting.Core.Attributes;
2+
3+
public interface ILimiterAttribute<T>
4+
{
5+
public string? Key { get; }
6+
public KeyType KeyType { get; }
7+
public T? Options { get; }
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ManagedCode.Orleans.RateLimiting.Core.Attributes;
2+
3+
public enum KeyType
4+
{
5+
Key,
6+
GrainId,
7+
GrainType,
8+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Threading.RateLimiting;
3+
4+
namespace ManagedCode.Orleans.RateLimiting.Core.Attributes;
5+
6+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
7+
public class SlidingWindowRateLimiterAttribute : Attribute, ILimiterAttribute<SlidingWindowRateLimiterOptions>
8+
{
9+
public string? Key { get; }
10+
public KeyType KeyType { get; }
11+
public SlidingWindowRateLimiterOptions? Options { get; }
12+
13+
/// <summary>
14+
/// FixedWindowRateLimiterAttribute
15+
/// </summary>
16+
/// <param name="keyType">What key to use to identify RateLimiting, can be overridden by setting key property</param>
17+
/// <param name="key">Custom string as key for RateLimiting</param>
18+
/// <param name="window">
19+
/// Specifies the time window that takes in the requests.
20+
/// Must be set to a value greater than <see cref="TimeSpan.Zero" /> by the time these options are passed to the constructor of <see cref="SlidingWindowRateLimiter"/>.
21+
/// </param>
22+
/// <param name="permitLimit">
23+
/// Maximum number of permits that can be leased concurrently.
24+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="SlidingWindowRateLimiter"/>.
25+
/// </param>
26+
/// <param name="queueLimit">
27+
/// Maximum number of permits that can be queued concurrently.
28+
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="SlidingWindowRateLimiter"/>.
29+
/// </param>
30+
/// <param name="segmentsPerWindow">
31+
/// Specifies the maximum number of segments a window is divided into.
32+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="SlidingWindowRateLimiter"/>.
33+
/// </param>
34+
/// <param name="autoReplenishment">
35+
/// Maximum number of permit counters that can be allowed in a window.
36+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="SlidingWindowRateLimiter"/>.
37+
/// </param>
38+
/// <param name="queueProcessingOrder">Determines the behaviour of <see cref="RateLimiter.AcquireAsync"/> when not enough resources can be leased.</param>
39+
public SlidingWindowRateLimiterAttribute(KeyType keyType = KeyType.GrainId, string? key = null,
40+
TimeSpan? window = null,
41+
int? permitLimit = null,
42+
int? queueLimit = null,
43+
int? segmentsPerWindow = null,
44+
bool autoReplenishment = true,
45+
QueueProcessingOrder queueProcessingOrder = QueueProcessingOrder.OldestFirst)
46+
{
47+
Key = key;
48+
KeyType = keyType;
49+
50+
//override keyType if key is set
51+
if (!string.IsNullOrEmpty(key))
52+
{
53+
KeyType = KeyType.Key;
54+
}
55+
56+
if (permitLimit.HasValue || queueLimit.HasValue || window.HasValue || segmentsPerWindow.HasValue)
57+
{
58+
Options = new SlidingWindowRateLimiterOptions()
59+
{
60+
Window = window ?? TimeSpan.FromSeconds(1),
61+
PermitLimit = permitLimit ?? 1,
62+
QueueLimit = queueLimit ?? 1,
63+
SegmentsPerWindow = segmentsPerWindow ?? 1,
64+
AutoReplenishment = autoReplenishment,
65+
QueueProcessingOrder = queueProcessingOrder
66+
};
67+
}
68+
}
69+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Threading.RateLimiting;
3+
4+
namespace ManagedCode.Orleans.RateLimiting.Core.Attributes;
5+
6+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
7+
public class TokenBucketRateLimiterAttribute : Attribute, ILimiterAttribute<TokenBucketRateLimiterOptions>
8+
{
9+
public string? Key { get; }
10+
public KeyType KeyType { get; }
11+
public TokenBucketRateLimiterOptions? Options { get; }
12+
13+
/// <summary>
14+
/// FixedWindowRateLimiterAttribute
15+
/// </summary>
16+
/// <param name="keyType">What key to use to identify RateLimiting, can be overridden by setting key property</param>
17+
/// <param name="key">Custom string as key for RateLimiting</param>
18+
/// <param name="replenishmentPeriod">
19+
/// Specifies the minimum period between replenishments.
20+
/// Must be set to a value greater than <see cref="TimeSpan.Zero" /> by the time these options are passed to the constructor of <see cref="TokenBucketRateLimiter"/>.
21+
/// </param>
22+
/// <param name="tokensPerPeriod">
23+
/// Specifies the maximum number of tokens to restore each replenishment.
24+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="TokenBucketRateLimiter"/>.
25+
/// </param>
26+
/// <param name="queueLimit">
27+
/// Maximum number of permits that can be queued concurrently.
28+
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="TokenBucketRateLimiter"/>.
29+
/// </param>
30+
/// <param name="tokenLimit">
31+
/// Specified whether the <see cref="TokenBucketRateLimiter"/> is automatically replenishing tokens or if someone else
32+
/// will be calling <see cref="TokenBucketRateLimiter.TryReplenish"/> to replenish tokens.
33+
/// </param>
34+
/// <param name="autoReplenishment">
35+
/// Maximum number of permit counters that can be allowed in a window.
36+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="TokenBucketRateLimiter"/>.
37+
/// </param>
38+
/// <param name="queueProcessingOrder">Determines the behaviour of <see cref="RateLimiter.AcquireAsync"/> when not enough resources can be leased.</param>
39+
public TokenBucketRateLimiterAttribute(KeyType keyType = KeyType.GrainId, string? key = null,
40+
TimeSpan? replenishmentPeriod = null,
41+
int? tokensPerPeriod = null,
42+
int? queueLimit = null,
43+
int? tokenLimit = null,
44+
bool autoReplenishment = true,
45+
QueueProcessingOrder queueProcessingOrder = QueueProcessingOrder.OldestFirst)
46+
{
47+
Key = key;
48+
KeyType = keyType;
49+
50+
//override keyType if key is set
51+
if (!string.IsNullOrEmpty(key))
52+
{
53+
KeyType = KeyType.Key;
54+
}
55+
56+
if (tokensPerPeriod.HasValue || queueLimit.HasValue || replenishmentPeriod.HasValue || tokenLimit.HasValue)
57+
{
58+
Options = new TokenBucketRateLimiterOptions()
59+
{
60+
ReplenishmentPeriod = replenishmentPeriod ?? TimeSpan.FromSeconds(1),
61+
TokensPerPeriod = tokensPerPeriod ?? 1,
62+
QueueLimit = queueLimit ?? 1,
63+
TokenLimit = tokenLimit ?? 1,
64+
AutoReplenishment = autoReplenishment,
65+
QueueProcessingOrder = queueProcessingOrder
66+
};
67+
}
68+
}
69+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
3+
namespace ManagedCode.Orleans.RateLimiting.Core.Exceptions;
4+
5+
public class RateLimitExceededException : Exception
6+
{
7+
public RateLimitExceededException()
8+
{
9+
Reason = "Rate limit exceeded";
10+
RetryAfter = TimeSpan.Zero;
11+
}
12+
13+
public RateLimitExceededException(string reason)
14+
{
15+
Reason = reason;
16+
RetryAfter = TimeSpan.Zero;
17+
}
18+
19+
public RateLimitExceededException(TimeSpan retry)
20+
{
21+
Reason = "Time limit exceeded";
22+
RetryAfter = retry;
23+
}
24+
25+
public RateLimitExceededException(string reason, TimeSpan retry)
26+
{
27+
Reason = reason;
28+
RetryAfter = retry;
29+
}
30+
31+
public string Reason { get; set; }
32+
public TimeSpan RetryAfter { get; set; }
33+
}

ManagedCode.Orleans.RateLimiting.Core/Extensions/GrainFactoryExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using ManagedCode.Orleans.RateLimiting.Core.Attributes;
13
using ManagedCode.Orleans.RateLimiting.Core.Interfaces;
24
using ManagedCode.Orleans.RateLimiting.Core.Models.Holders;
35
using Orleans;
@@ -6,6 +8,21 @@ namespace ManagedCode.Orleans.RateLimiting.Core.Extensions;
68

79
public static class GrainFactoryExtensions
810
{
11+
public static ILimiterHolder GetRateLimiter<T>(this IGrainFactory factory, string key) where T : IRateLimiterGrain
12+
{
13+
ILimiterHolder limiter = typeof(T) switch
14+
{
15+
IFixedWindowRateLimiterGrain => factory.GetFixedWindowRateLimiter(key),
16+
IConcurrencyLimiterGrain => factory.GetConcurrencyLimiter(key),
17+
ISlidingWindowRateLimiterGrain => factory.GetSlidingWindowRateLimiter(key),
18+
ITokenBucketRateLimiterGrain => factory.GetTokenBucketRateLimiter(key),
19+
20+
_ => null //throw new ArgumentException("Unknown rate limiter grain type")
21+
};
22+
23+
return limiter;
24+
}
25+
926
public static FixedWindowRateLimiterHolder GetFixedWindowRateLimiter(this IGrainFactory factory, string key)
1027
{
1128
return new FixedWindowRateLimiterHolder(factory.GetGrain<IFixedWindowRateLimiterGrain>(key), factory);

0 commit comments

Comments
 (0)