Skip to content

Commit 124ba53

Browse files
Make RateLimiterOptions follow Options pattern (#72530)
* Update ConcurrencyLimiterOptions.cs * Update FixedWindowRateLimiterOptions.cs * Update FixedWindowRateLimiterOptions.cs * Update FixedWindowRateLimiterOptions.cs * Update SlidingWindowRateLimiterOptions.cs * Update TokenBucketRateLimiterOptions.cs * Update ConcurrencyLimiter.cs * Update FixedWindowRateLimiter.cs * Update SlidingWindowRateLimiter.cs * Update TokenBucketRateLimiter.cs * Update ChainedLimiterTests.cs * Update ConcurrencyLimiterOptions.cs * Update FixedWindowRateLimiterOptions.cs * Update SlidingWindowRateLimiterOptions.cs * Update TokenBucketRateLimiterOptions.cs * Update RateLimitPartition.cs * Update System.Threading.RateLimiting.cs * Update ChainedLimiterTests.cs * Update ConcurrencyLimiter.cs * Update FixedWindowRateLimiter.cs * Update SlidingWindowRateLimiter.cs * Update TokenBucketRateLimiter.cs * Update ConcurrencyLimiterOptions.cs * Update FixedWindowRateLimiterOptions.cs * Update SlidingWindowRateLimiterOptions.cs * Update ChainedLimiterTests.cs * Update ChainedLimiterTests.cs * Fix tests * Update ConcurrencyLimiter.cs * Update FixedWindowRateLimiter.cs * Update SlidingWindowRateLimiter.cs * Update TokenBucketRateLimiter.cs * Update FixedWindowRateLimiter.cs * Update SlidingWindowRateLimiter.cs * Update TokenBucketRateLimiter.cs * Update FixedWindowRateLimiterTests.cs * Update SlidingWindowRateLimiterTests.cs * Update TokenBucketRateLimiterTests.cs * Update TokenBucketRateLimiter.cs * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs Co-authored-by: Brennan <[email protected]> * Update FixedWindowRateLimiter.cs * Update SlidingWindowRateLimiter.cs * Fixup * Whitespace * ArgumentNullException * React to test change * Fix tests * Another test fix * Feedback * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Update src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/TokenBucketRateLimiterOptions.cs Co-authored-by: Brennan <[email protected]> * Fix checks, add test Co-authored-by: Brennan <[email protected]>
1 parent 2a2edc8 commit 124ba53

17 files changed

+2043
-571
lines changed

src/libraries/System.Threading.RateLimiting/ref/System.Threading.RateLimiting.cs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ protected override void Dispose(bool disposing) { }
1818
}
1919
public sealed partial class ConcurrencyLimiterOptions
2020
{
21-
public ConcurrencyLimiterOptions(int permitLimit, System.Threading.RateLimiting.QueueProcessingOrder queueProcessingOrder, int queueLimit) { }
22-
public int PermitLimit { get { throw null; } }
23-
public int QueueLimit { get { throw null; } }
24-
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } }
21+
public ConcurrencyLimiterOptions() { }
22+
public int PermitLimit { get { throw null; } set { throw null; } }
23+
public int QueueLimit { get { throw null; } set { throw null; } }
24+
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } set { throw null; } }
2525
}
2626
public sealed partial class FixedWindowRateLimiter : System.Threading.RateLimiting.ReplenishingRateLimiter
2727
{
@@ -38,12 +38,12 @@ protected override void Dispose(bool disposing) { }
3838
}
3939
public sealed partial class FixedWindowRateLimiterOptions
4040
{
41-
public FixedWindowRateLimiterOptions(int permitLimit, System.Threading.RateLimiting.QueueProcessingOrder queueProcessingOrder, int queueLimit, System.TimeSpan window, bool autoReplenishment = true) { }
42-
public bool AutoReplenishment { get { throw null; } }
43-
public int PermitLimit { get { throw null; } }
44-
public int QueueLimit { get { throw null; } }
45-
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } }
46-
public System.TimeSpan Window { get { throw null; } }
41+
public FixedWindowRateLimiterOptions() { }
42+
public bool AutoReplenishment { get { throw null; } set { throw null; } }
43+
public int PermitLimit { get { throw null; } set { throw null; } }
44+
public int QueueLimit { get { throw null; } set { throw null; } }
45+
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } set { throw null; } }
46+
public System.TimeSpan Window { get { throw null; } set { throw null; } }
4747
}
4848
public static partial class MetadataName
4949
{
@@ -151,13 +151,13 @@ protected override void Dispose(bool disposing) { }
151151
}
152152
public sealed partial class SlidingWindowRateLimiterOptions
153153
{
154-
public SlidingWindowRateLimiterOptions(int permitLimit, System.Threading.RateLimiting.QueueProcessingOrder queueProcessingOrder, int queueLimit, System.TimeSpan window, int segmentsPerWindow, bool autoReplenishment = true) { }
155-
public bool AutoReplenishment { get { throw null; } }
156-
public int PermitLimit { get { throw null; } }
157-
public int QueueLimit { get { throw null; } }
158-
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } }
159-
public int SegmentsPerWindow { get { throw null; } }
160-
public System.TimeSpan Window { get { throw null; } }
154+
public SlidingWindowRateLimiterOptions() { }
155+
public bool AutoReplenishment { get { throw null; } set { throw null; } }
156+
public int PermitLimit { get { throw null; } set { throw null; } }
157+
public int QueueLimit { get { throw null; } set { throw null; } }
158+
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } set { throw null; } }
159+
public int SegmentsPerWindow { get { throw null; } set { throw null; } }
160+
public System.TimeSpan Window { get { throw null; } set { throw null; } }
161161
}
162162
public sealed partial class TokenBucketRateLimiter : System.Threading.RateLimiting.ReplenishingRateLimiter
163163
{
@@ -174,12 +174,12 @@ protected override void Dispose(bool disposing) { }
174174
}
175175
public sealed partial class TokenBucketRateLimiterOptions
176176
{
177-
public TokenBucketRateLimiterOptions(int tokenLimit, System.Threading.RateLimiting.QueueProcessingOrder queueProcessingOrder, int queueLimit, System.TimeSpan replenishmentPeriod, int tokensPerPeriod, bool autoReplenishment = true) { }
178-
public bool AutoReplenishment { get { throw null; } }
179-
public int QueueLimit { get { throw null; } }
180-
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } }
181-
public System.TimeSpan ReplenishmentPeriod { get { throw null; } }
182-
public int TokenLimit { get { throw null; } }
183-
public int TokensPerPeriod { get { throw null; } }
177+
public TokenBucketRateLimiterOptions() { }
178+
public bool AutoReplenishment { get { throw null; } set { throw null; } }
179+
public int QueueLimit { get { throw null; } set { throw null; } }
180+
public System.Threading.RateLimiting.QueueProcessingOrder QueueProcessingOrder { get { throw null; } set { throw null; } }
181+
public System.TimeSpan ReplenishmentPeriod { get { throw null; } set { throw null; } }
182+
public int TokenLimit { get { throw null; } set { throw null; } }
183+
public int TokensPerPeriod { get { throw null; } set { throw null; } }
184184
}
185185
}

src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,26 @@ public sealed class ConcurrencyLimiter : RateLimiter
3838
/// <param name="options">Options to specify the behavior of the <see cref="ConcurrencyLimiter"/>.</param>
3939
public ConcurrencyLimiter(ConcurrencyLimiterOptions options)
4040
{
41-
_options = options ?? throw new ArgumentNullException(nameof(options));
41+
if (options is null)
42+
{
43+
throw new ArgumentNullException(nameof(options));
44+
}
45+
if (options.PermitLimit <= 0)
46+
{
47+
throw new ArgumentException($"{nameof(options.PermitLimit)} must be set to a value greater than 0.", nameof(options));
48+
}
49+
if (options.QueueLimit < 0)
50+
{
51+
throw new ArgumentException($"{nameof(options.QueueLimit)} must be set to a value greater than or equal to 0.", nameof(options));
52+
}
53+
54+
_options = new ConcurrencyLimiterOptions
55+
{
56+
PermitLimit = options.PermitLimit,
57+
QueueProcessingOrder = options.QueueProcessingOrder,
58+
QueueLimit = options.QueueLimit
59+
};
60+
4261
_permitCount = _options.PermitLimit;
4362
}
4463

src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiterOptions.cs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,24 @@ namespace System.Threading.RateLimiting
88
/// </summary>
99
public sealed class ConcurrencyLimiterOptions
1010
{
11-
/// <summary>
12-
/// Initializes the <see cref="ConcurrencyLimiterOptions"/>.
13-
/// </summary>
14-
/// <param name="permitLimit">Maximum number of permits that can be leased concurrently.</param>
15-
/// <param name="queueProcessingOrder">Determines the behaviour of <see cref="RateLimiter.WaitAndAcquireAsync"/> when not enough resources can be leased.</param>
16-
/// <param name="queueLimit">Maximum number of permits that can be queued concurrently.</param>
17-
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="permitLimit"/> or <paramref name="queueLimit"/> are less than 0.</exception>
18-
public ConcurrencyLimiterOptions(int permitLimit, QueueProcessingOrder queueProcessingOrder, int queueLimit)
19-
{
20-
if (permitLimit < 0)
21-
{
22-
throw new ArgumentOutOfRangeException(nameof(permitLimit));
23-
}
24-
if (queueLimit < 0)
25-
{
26-
throw new ArgumentOutOfRangeException(nameof(queueLimit));
27-
}
28-
PermitLimit = permitLimit;
29-
QueueProcessingOrder = queueProcessingOrder;
30-
QueueLimit = queueLimit;
31-
}
32-
3311
/// <summary>
3412
/// Maximum number of permits that can be leased concurrently.
13+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="ConcurrencyLimiter"/>.
3514
/// </summary>
36-
public int PermitLimit { get; }
15+
public int PermitLimit { get; set; }
3716

3817
/// <summary>
3918
/// Determines the behaviour of <see cref="RateLimiter.WaitAndAcquireAsync"/> when not enough resources can be leased.
4019
/// </summary>
4120
/// <value>
4221
/// <see cref="QueueProcessingOrder.OldestFirst"/> by default.
4322
/// </value>
44-
public QueueProcessingOrder QueueProcessingOrder { get; } = QueueProcessingOrder.OldestFirst;
23+
public QueueProcessingOrder QueueProcessingOrder { get; set; } = QueueProcessingOrder.OldestFirst;
4524

4625
/// <summary>
4726
/// Maximum number of permits that can be queued concurrently.
27+
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="ConcurrencyLimiter"/>.
4828
/// </summary>
49-
public int QueueLimit { get; }
29+
public int QueueLimit { get; set; }
5030
}
5131
}

src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiter.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,32 @@ public sealed class FixedWindowRateLimiter : ReplenishingRateLimiter
4444
/// <param name="options">Options to specify the behavior of the <see cref="FixedWindowRateLimiter"/>.</param>
4545
public FixedWindowRateLimiter(FixedWindowRateLimiterOptions options)
4646
{
47-
_options = options ?? throw new ArgumentNullException(nameof(options));
47+
if (options is null)
48+
{
49+
throw new ArgumentNullException(nameof(options));
50+
}
51+
if (options.PermitLimit <= 0)
52+
{
53+
throw new ArgumentException($"{nameof(options.PermitLimit)} must be set to a value greater than 0.", nameof(options));
54+
}
55+
if (options.QueueLimit < 0)
56+
{
57+
throw new ArgumentException($"{nameof(options.QueueLimit)} must be set to a value greater than or equal to 0.", nameof(options));
58+
}
59+
if (options.Window < TimeSpan.Zero)
60+
{
61+
throw new ArgumentException($"{nameof(options.Window)} must be set to a value greater than or equal to TimeSpan.Zero.", nameof(options));
62+
}
63+
64+
_options = new FixedWindowRateLimiterOptions
65+
{
66+
PermitLimit = options.PermitLimit,
67+
QueueProcessingOrder = options.QueueProcessingOrder,
68+
QueueLimit = options.QueueLimit,
69+
Window = options.Window,
70+
AutoReplenishment = options.AutoReplenishment
71+
};
72+
4873
_requestCount = options.PermitLimit;
4974

5075
_idleSince = _lastReplenishmentTick = Stopwatch.GetTimestamp();

src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/FixedWindowRateLimiterOptions.cs

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,69 +8,39 @@ namespace System.Threading.RateLimiting
88
/// </summary>
99
public sealed class FixedWindowRateLimiterOptions
1010
{
11-
/// <summary>
12-
/// Initializes the <see cref="FixedWindowRateLimiterOptions"/>.
13-
/// </summary>
14-
/// <param name="permitLimit">Maximum number of requests that can be served in the window.</param>
15-
/// <param name="queueProcessingOrder"></param>
16-
/// <param name="queueLimit">Maximum number of unprocessed request counters waiting via <see cref="RateLimiter.WaitAndAcquireAsync(int, CancellationToken)"/>.</param>
17-
/// <param name="window">
18-
/// Specifies how often request counters can be replenished. Replenishing is triggered either by an internal timer if <paramref name="autoReplenishment"/> is true, or by calling <see cref="FixedWindowRateLimiter.TryReplenish"/>.
19-
/// </param>
20-
/// <param name="autoReplenishment">
21-
/// Specifies whether request replenishment will be handled by the <see cref="FixedWindowRateLimiter"/> or by another party via <see cref="FixedWindowRateLimiter.TryReplenish"/>.
22-
/// </param>
23-
/// <exception cref="ArgumentOutOfRangeException">When <paramref name="permitLimit"/> or <paramref name="queueLimit"/> are less than 0. </exception>
24-
public FixedWindowRateLimiterOptions(
25-
int permitLimit,
26-
QueueProcessingOrder queueProcessingOrder,
27-
int queueLimit,
28-
TimeSpan window,
29-
bool autoReplenishment = true)
30-
{
31-
if (permitLimit < 0)
32-
{
33-
throw new ArgumentOutOfRangeException(nameof(permitLimit));
34-
}
35-
if (queueLimit < 0)
36-
{
37-
throw new ArgumentOutOfRangeException(nameof(queueLimit));
38-
}
39-
40-
PermitLimit = permitLimit;
41-
QueueProcessingOrder = queueProcessingOrder;
42-
QueueLimit = queueLimit;
43-
Window = window;
44-
AutoReplenishment = autoReplenishment;
45-
}
46-
4711
/// <summary>
4812
/// Specifies the time window that takes in the requests.
13+
/// Must be set to a value >= <see cref="TimeSpan.Zero" /> by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
4914
/// </summary>
50-
public TimeSpan Window { get; }
15+
public TimeSpan Window { get; set; } = TimeSpan.Zero;
5116

5217
/// <summary>
5318
/// Specified whether the <see cref="FixedWindowRateLimiter"/> is automatically refresh counters or if someone else
5419
/// will be calling <see cref="FixedWindowRateLimiter.TryReplenish"/> to refresh counters.
5520
/// </summary>
56-
public bool AutoReplenishment { get; }
21+
/// <value>
22+
/// <see langword="true" /> by default.
23+
/// </value>
24+
public bool AutoReplenishment { get; set; } = true;
5725

5826
/// <summary>
5927
/// Maximum number of permit counters that can be allowed in a window.
28+
/// Must be set to a value > 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
6029
/// </summary>
61-
public int PermitLimit { get; }
30+
public int PermitLimit { get; set; }
6231

6332
/// <summary>
6433
/// Determines the behaviour of <see cref="RateLimiter.WaitAndAcquireAsync"/> when not enough resources can be leased.
6534
/// </summary>
6635
/// <value>
6736
/// <see cref="QueueProcessingOrder.OldestFirst"/> by default.
6837
/// </value>
69-
public QueueProcessingOrder QueueProcessingOrder { get; }
38+
public QueueProcessingOrder QueueProcessingOrder { get; set; } = QueueProcessingOrder.OldestFirst;
7039

7140
/// <summary>
7241
/// Maximum cumulative permit count of queued acquisition requests.
42+
/// Must be set to a value >= 0 by the time these options are passed to the constructor of <see cref="FixedWindowRateLimiter"/>.
7343
/// </summary>
74-
public int QueueLimit { get; }
44+
public int QueueLimit { get; set; }
7545
}
7646
}

src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimitPartition.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,15 @@ public static RateLimitPartition<TKey> GetTokenBucketLimiter<TKey>(
7171
// We don't want individual TokenBucketRateLimiters to have timers. We will instead have our own internal Timer handling all of them
7272
if (options.AutoReplenishment is true)
7373
{
74-
options = new TokenBucketRateLimiterOptions(options.TokenLimit, options.QueueProcessingOrder, options.QueueLimit,
75-
options.ReplenishmentPeriod, options.TokensPerPeriod, autoReplenishment: false);
74+
options = new TokenBucketRateLimiterOptions
75+
{
76+
TokenLimit = options.TokenLimit,
77+
QueueProcessingOrder = options.QueueProcessingOrder,
78+
QueueLimit = options.QueueLimit,
79+
ReplenishmentPeriod = options.ReplenishmentPeriod,
80+
TokensPerPeriod = options.TokensPerPeriod,
81+
AutoReplenishment = false
82+
};
7683
}
7784
return new TokenBucketRateLimiter(options);
7885
});
@@ -98,8 +105,15 @@ public static RateLimitPartition<TKey> GetSlidingWindowLimiter<TKey>(
98105
// We don't want individual SlidingWindowRateLimiters to have timers. We will instead have our own internal Timer handling all of them
99106
if (options.AutoReplenishment is true)
100107
{
101-
options = new SlidingWindowRateLimiterOptions(options.PermitLimit, options.QueueProcessingOrder, options.QueueLimit,
102-
options.Window, options.SegmentsPerWindow, autoReplenishment: false);
108+
options = new SlidingWindowRateLimiterOptions
109+
{
110+
PermitLimit = options.PermitLimit,
111+
QueueProcessingOrder = options.QueueProcessingOrder,
112+
QueueLimit = options.QueueLimit,
113+
Window = options.Window,
114+
SegmentsPerWindow = options.SegmentsPerWindow,
115+
AutoReplenishment = false
116+
};
103117
}
104118
return new SlidingWindowRateLimiter(options);
105119
});
@@ -125,8 +139,14 @@ public static RateLimitPartition<TKey> GetFixedWindowLimiter<TKey>(
125139
// We don't want individual FixedWindowRateLimiters to have timers. We will instead have our own internal Timer handling all of them
126140
if (options.AutoReplenishment is true)
127141
{
128-
options = new FixedWindowRateLimiterOptions(options.PermitLimit, options.QueueProcessingOrder, options.QueueLimit,
129-
options.Window, autoReplenishment: false);
142+
options = new FixedWindowRateLimiterOptions
143+
{
144+
PermitLimit = options.PermitLimit,
145+
QueueProcessingOrder = options.QueueProcessingOrder,
146+
QueueLimit = options.QueueLimit,
147+
Window = options.Window,
148+
AutoReplenishment = false
149+
};
130150
}
131151
return new FixedWindowRateLimiter(options);
132152
});

src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/SlidingWindowRateLimiter.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,33 @@ public sealed class SlidingWindowRateLimiter : ReplenishingRateLimiter
4747
/// <param name="options">Options to specify the behavior of the <see cref="SlidingWindowRateLimiter"/>.</param>
4848
public SlidingWindowRateLimiter(SlidingWindowRateLimiterOptions options)
4949
{
50-
_options = options ?? throw new ArgumentNullException(nameof(options));
50+
if (options is null)
51+
{
52+
throw new ArgumentNullException(nameof(options));
53+
}
54+
if (options.PermitLimit <= 0 || options.SegmentsPerWindow <= 0)
55+
{
56+
throw new ArgumentException($"Both {nameof(options.PermitLimit)} and {nameof(options.SegmentsPerWindow)} must be set to values greater than 0.", nameof(options));
57+
}
58+
if (options.QueueLimit < 0)
59+
{
60+
throw new ArgumentException($"{nameof(options.QueueLimit)} must be set to a value greater than or equal to 0.", nameof(options));
61+
}
62+
if (options.Window < TimeSpan.Zero)
63+
{
64+
throw new ArgumentException($"{nameof(options.Window)} must be set to a value greater than or equal to TimeSpan.Zero.", nameof(options));
65+
}
66+
67+
_options = new SlidingWindowRateLimiterOptions
68+
{
69+
PermitLimit = options.PermitLimit,
70+
QueueProcessingOrder = options.QueueProcessingOrder,
71+
QueueLimit = options.QueueLimit,
72+
Window = options.Window,
73+
SegmentsPerWindow = options.SegmentsPerWindow,
74+
AutoReplenishment = options.AutoReplenishment
75+
};
76+
5177
_requestCount = options.PermitLimit;
5278

5379
// _requestsPerSegment holds the no. of acquired requests in each window segment

0 commit comments

Comments
 (0)