-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
[API Proposal]: Rename RateLimiter.WaitAsync #71775
Comments
Tagging subscribers to this area: @mangod9 Issue DetailsBackground and motivation
We think changing the name to API Proposalnamespace System.Threading.RateLimiting;
public abstract class RateLimiter : IAsyncDisposable, IDisposable
{
- public ValueTask<RateLimitLease> WaitAsync(int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAsyncCore(int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
}
public abstract class PartitionedRateLimiter<TResource> : IAsyncDisposable, IDisposable
{
- public ValueTask<RateLimitLease> WaitAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
} API UsageRateLimiter limiter = GetLimiter();
var lease = limiter.Acquire(1);
if (!lease.IsAcquired)
{
lease = await limiter.AcquireAsync(1);
} Alternative DesignsNo response RisksNo response
|
namespace System.Threading.RateLimiting;
public abstract class RateLimiter : IAsyncDisposable, IDisposable
{
- public ValueTask<RateLimitLease> WaitAsync(int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> WaitAndAcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAsyncCore(int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
}
public abstract class PartitionedRateLimiter<TResource> : IAsyncDisposable, IDisposable
{
- public ValueTask<RateLimitLease> WaitAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> WaitAndAcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
} |
We've had some more conversation about this on the ASP.NET side, and we just can't swallow the wordy The basic idea is to rename API Proposalnamespace System.Threading.RateLimiting;
public abstract class RateLimiter : IAsyncDisposable, IDisposable
{
- public RateLimitLease Acquire( int permitCount = 1);
+ public RateLimitLease TryAcquire(int permitCount = 1);
- protected abstract RateLimitLease AcquireCore(TResource resource, int permitCount);
+ protected abstract RateLimitLease TryAcquireCore(TResource resource, int permitCount);
- public ValueTask<RateLimitLease> WaitAndAcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
}
public abstract class PartitionedRateLimiter<TResource> : IAsyncDisposable, IDisposable
{
- public RateLimitLease Acquire(TResource resource, int permitCount = 1);
+ public RateLimitLease TryAcquire(TResource resource, int permitCount = 1);
- protected abstract RateLimitLease AcquireCore(TResource resource, int permitCount);
+ protected abstract RateLimitLease TryAcquireCore(TResource resource, int permitCount);
- public ValueTask<RateLimitLease> WaitAndAcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
} API UsageRateLimiter limiter = GetLimiter();
var lease = limiter.TryAcquire(1);
if (!lease.IsAcquired)
{
lease = await limiter.AcquireAsync(1);
// ...
} |
Why not make it actually follow the Try pattern and return a bool with the lease as the out parameter? If it's unsuccessful, do you actually need the resulting lease object? The bool value would be duplicated in the object, but it would always be true in this case.
Who can't? Why? It is the accurate description, and having a TryAquire and Aquire just changes the problem. Having Try not follow the pattern of returning bool is also hard to swallow in that it's confusing for consumers and weakens the pattern overall, and it suggests that if Aquire returns successfully then the lease has been successfully acquired, since it lacks the corresponding Try, e.g. TryDequeue returns false on failure while Dequeue throws on failure. |
Yes, the lease may contain metadata, e.g. an estimate for how long you should wait until you call One potential rename could be to keep |
QueueAcquireAsync is so much better than |
I don't like The name How about |
That's not horrible, but talk about a mouthful :) Or... AttemptAcquire and AcquireAsync? Alternatively, did we consider actually just making Acquire behave like AcquireAsync, except via synchronous rather than asynchronous blocking? It could have an int-based overload for a timeout, e.g. Acquire(0) would be identical in behavior to what Acquire() is today. In this regard it would end up being like the Wait/WaitAsync pairs we have on some sync primitives. |
I like it.
I really like this idea. The problem I see with it is that then we'd want to still provide a non-acquired RateLimitLease with whatever metadata is expected (e.g. RETRY_AFTER and REASON_PHRASE) rather than throwing like we would in the async version if you trip the CancellationToken. We could probably do this, but then that creates an unfortunate scenario where the blocking API provides better behavior than the non-blocking API at least in terms of providing metadata after a timeout. We could add a timeout in addition to the CancellationToken to the async API but that would be really weird. We could instead return a non-acquired lease rather than throwing when the CancellationToken trips in AcquireAsync, but that would also really weird. I don't want to rule out the timout change, and I think we should discuss that further, but the AttemptAcquire and AcquireAsync rename seems like an easy win. I don't want that to be lost in pursuit of something better that may not happen, so I'm going to propose that rename for tomorrow's API review. API Proposalnamespace System.Threading.RateLimiting;
public abstract class RateLimiter : IAsyncDisposable, IDisposable
{
- public RateLimitLease Acquire(int permitCount = 1);
+ public RateLimitLease AttemptAcquire(int permitCount = 1);
- protected abstract RateLimitLease AcquireCore(int permitCount);
+ protected abstract RateLimitLease AttemptAcquireCore(int permitCount);
- public ValueTask<RateLimitLease> WaitAndAcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
}
public abstract class PartitionedRateLimiter<TResource> : IAsyncDisposable, IDisposable
{
- public RateLimitLease Acquire(TResource resource, int permitCount = 1);
+ public RateLimitLease AttemptAcquire(TResource resource, int permitCount = 1);
- protected abstract RateLimitLease AcquireCore(TResource resource, int permitCount);
+ protected abstract RateLimitLease AttemptAcquireCore(TResource resource, int permitCount);
- public ValueTask<RateLimitLease> WaitAndAcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
} API UsageRateLimiter limiter = GetLimiter();
var lease = limiter.TryAcquire(1);
if (!lease.IsAcquired)
{
lease = await limiter.AcquireAsync(1);
// ...
} |
namespace System.Threading.RateLimiting;
public abstract class RateLimiter : IAsyncDisposable, IDisposable
{
- public RateLimitLease Acquire(int permitCount = 1);
+ public RateLimitLease AttemptAcquire(int permitCount = 1);
- protected abstract RateLimitLease AcquireCore(int permitCount);
+ protected abstract RateLimitLease AttemptAcquireCore(int permitCount);
- public ValueTask<RateLimitLease> WaitAndAcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(int permitCount, CancellationToken cancellationToken);
}
public abstract class PartitionedRateLimiter<TResource> : IAsyncDisposable, IDisposable
{
- public RateLimitLease Acquire(TResource resource, int permitCount = 1);
+ public RateLimitLease AttemptAcquire(TResource resource, int permitCount = 1);
- protected abstract RateLimitLease AcquireCore(TResource resource, int permitCount);
+ protected abstract RateLimitLease AttemptAcquireCore(TResource resource, int permitCount);
- public ValueTask<RateLimitLease> WaitAndAcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
+ public ValueTask<RateLimitLease> AcquireAsync(TResource resourceID, int permitCount = 1, CancellationToken cancellationToken = default);
- protected abstract ValueTask<RateLimitLease> WaitAndAcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
+ protected abstract ValueTask<RateLimitLease> AcquireAsyncCore(TResource resourceID, int permitCount, CancellationToken cancellationToken);
} |
Background and motivation
WaitAsync
doesn't really convey that it acquires a lease. It more looks like it will wait until more permits are available. Sort of likeChannelReader.WaitToReadAsync
.We think changing the name to
AcquireAsync
is clearer that it might need to wait for more permits to become available and then acquire a lease. Similar toChannelReader.ReadAsync
.API Proposal
API Usage
Alternative Designs
No response
Risks
No response
The text was updated successfully, but these errors were encountered: