23166

How to build a rate-limiting API with Observables?

I would like to create a simple Calculator service that has a single method to add numbers. This Add method should be async and has to limit the number of concurrent calls being made at a given time. For instance, no more than 5 concurrent calls per second. If the rate limit is exceeded, the call should throw an exception.

The class should be like:

public class RateLimitingCalculator { public async Task<int> Add(int a, int b) { //... } }

Any ideas? I would like implement it with Reactive Extensions, but if it's better to use another strategy, I would stick to it! Thanks!!

Answer1:

I don't think using Rx makes sense here, unless you can rewrite your method into something like public IObservable<int> Add(IObservable<Tuple<int, int>> values), as suggested by Enigmativity in a comment.

What I would do is to separate the concern of rate limiting into a separate class. That way, your code could look something like this:

public class RateLimitingCalculator { private RateLimiter rateLimiter = new RateLimiter(5, TimeSpan.FromSeconds(1)); public async Task<int> Add(int a, int b) { rateLimiter.ThrowIfRateExceeded(); //... } }

The implementation of RateLimiter depends on your exact requirements, but a very simple, not-thread-safe version could look like this:

class RateLimiter { private readonly int rate; private readonly TimeSpan perTime; private DateTime secondStart = DateTime.MinValue; private int count = 0; public RateLimiter(int rate, TimeSpan perTime) { this.rate = rate; this.perTime = perTime; } public void ThrowIfRateExceeded() { var now = DateTime.UtcNow; if (now - secondStart > perTime) { secondStart = now; count = 1; return; } if (count >= rate) throw new RateLimitExceededException(); count++; } }

Recommend

  • hibernate entity is not mapped since version 4.3.2
  • getting bad request while using passport in login form
  • Avoid merging master into development branch
  • Spring Integration debounce/deduplicate
  • Mapping ManyToMany with composite Primary key and Annotation:
  • Why not Factory pattern for sorting? [closed]
  • How to extract text from a PDF and decode characters?
  • multidatatrigger with multibinding in ControlTemplate.Triggers
  • Cloud Code function running twice
  • Do query loads all the data in memory
  • converting text file into xml using php?
  • Unable to get column index with table.getColumn method using custom table Model
  • Thread safety of a fluent like class using clone() and non final fields
  • custom UITableViewCell with image for highlighting
  • Converting a WriteableBitmap image ToArray in UWP
  • javascript inside java/jsp code
  • Rearranging Cells in UITableView Bug & Saving Changes
  • Akka Routing: Reply's send to router ends up as dead letters
  • Cannot Parse HTML Data Using Android / JSOUP
  • Android Studio and gradle
  • Load html files in TinyMce
  • How get height of the a view with gone visibility and height defined as wrap_content in xml?
  • JTable with a ScrollPane misbehaving
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Java static initializers and reflection
  • Change div Background jquery
  • How does Linux kernel interrupt the application?
  • IndexOutOfRangeException on multidimensional array despite using GetLength check
  • unknown Exception android
  • Busy indicator not showing up in wpf window [duplicate]
  • Sorting a 2D array using the second column C++
  • costura.fody for a dll that references another dll
  • Observable and ngFor in Angular 2
  • How can i traverse a binary tree from right to left in java?
  • failed to connect to specific WiFi in android programmatically
  • UserPrincipal.Current returns apppool on IIS
  • Unable to use reactive element in my shiny app
  • Why do underscore prefixed variables exist?
  • java string with new operator and a literal
  • How can I use threading to 'tick' a timer to be accessed by other threads?