Oren Eini

aka Ayende Rahien

Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,575
|
Comments: 51,189

Copyright ©️ Ayende Rahien 2004 — 2025

Privacy Policy · Terms
filter by tags archive
stack view grid view
  • architecture (606) rss
  • bugs (450) rss
  • challanges (123) rss
  • community (377) rss
  • databases (481) rss
  • design (893) rss
  • development (640) rss
  • hibernating-practices (71) rss
  • miscellaneous (592) rss
  • performance (397) rss
  • programming (1085) rss
  • raven (1442) rss
  • ravendb.net (526) rss
  • reviews (184) rss
  • 2025
    • May (7)
    • April (10)
    • March (10)
    • February (7)
    • January (12)
  • 2024
    • December (3)
    • November (2)
    • October (1)
    • September (3)
    • August (5)
    • July (10)
    • June (4)
    • May (6)
    • April (2)
    • March (8)
    • February (2)
    • January (14)
  • 2023
    • December (4)
    • October (4)
    • September (6)
    • August (12)
    • July (5)
    • June (15)
    • May (3)
    • April (11)
    • March (5)
    • February (5)
    • January (8)
  • 2022
    • December (5)
    • November (7)
    • October (7)
    • September (9)
    • August (10)
    • July (15)
    • June (12)
    • May (9)
    • April (14)
    • March (15)
    • February (13)
    • January (16)
  • 2021
    • December (23)
    • November (20)
    • October (16)
    • September (6)
    • August (16)
    • July (11)
    • June (16)
    • May (4)
    • April (10)
    • March (11)
    • February (15)
    • January (14)
  • 2020
    • December (10)
    • November (13)
    • October (15)
    • September (6)
    • August (9)
    • July (9)
    • June (17)
    • May (15)
    • April (14)
    • March (21)
    • February (16)
    • January (13)
  • 2019
    • December (17)
    • November (14)
    • October (16)
    • September (10)
    • August (8)
    • July (16)
    • June (11)
    • May (13)
    • April (18)
    • March (12)
    • February (19)
    • January (23)
  • 2018
    • December (15)
    • November (14)
    • October (19)
    • September (18)
    • August (23)
    • July (20)
    • June (20)
    • May (23)
    • April (15)
    • March (23)
    • February (19)
    • January (23)
  • 2017
    • December (21)
    • November (24)
    • October (22)
    • September (21)
    • August (23)
    • July (21)
    • June (24)
    • May (21)
    • April (21)
    • March (23)
    • February (20)
    • January (23)
  • 2016
    • December (17)
    • November (18)
    • October (22)
    • September (18)
    • August (23)
    • July (22)
    • June (17)
    • May (24)
    • April (16)
    • March (16)
    • February (21)
    • January (21)
  • 2015
    • December (5)
    • November (10)
    • October (9)
    • September (17)
    • August (20)
    • July (17)
    • June (4)
    • May (12)
    • April (9)
    • March (8)
    • February (25)
    • January (17)
  • 2014
    • December (22)
    • November (19)
    • October (21)
    • September (37)
    • August (24)
    • July (23)
    • June (13)
    • May (19)
    • April (24)
    • March (23)
    • February (21)
    • January (24)
  • 2013
    • December (23)
    • November (29)
    • October (27)
    • September (26)
    • August (24)
    • July (24)
    • June (23)
    • May (25)
    • April (26)
    • March (24)
    • February (24)
    • January (21)
  • 2012
    • December (19)
    • November (22)
    • October (27)
    • September (24)
    • August (30)
    • July (23)
    • June (25)
    • May (23)
    • April (25)
    • March (25)
    • February (28)
    • January (24)
  • 2011
    • December (17)
    • November (14)
    • October (24)
    • September (28)
    • August (27)
    • July (30)
    • June (19)
    • May (16)
    • April (30)
    • March (23)
    • February (11)
    • January (26)
  • 2010
    • December (29)
    • November (28)
    • October (35)
    • September (33)
    • August (44)
    • July (17)
    • June (20)
    • May (53)
    • April (29)
    • March (35)
    • February (33)
    • January (36)
  • 2009
    • December (37)
    • November (35)
    • October (53)
    • September (60)
    • August (66)
    • July (29)
    • June (24)
    • May (52)
    • April (63)
    • March (35)
    • February (53)
    • January (50)
  • 2008
    • December (58)
    • November (65)
    • October (46)
    • September (48)
    • August (96)
    • July (87)
    • June (45)
    • May (51)
    • April (52)
    • March (70)
    • February (43)
    • January (49)
  • 2007
    • December (100)
    • November (52)
    • October (109)
    • September (68)
    • August (80)
    • July (56)
    • June (150)
    • May (115)
    • April (73)
    • March (124)
    • February (102)
    • January (68)
  • 2006
    • December (95)
    • November (53)
    • October (120)
    • September (57)
    • August (88)
    • July (54)
    • June (103)
    • May (89)
    • April (84)
    • March (143)
    • February (78)
    • January (64)
  • 2005
    • December (70)
    • November (97)
    • October (91)
    • September (61)
    • August (74)
    • July (92)
    • June (100)
    • May (53)
    • April (42)
    • March (41)
    • February (84)
    • January (31)
  • 2004
    • December (49)
    • November (26)
    • October (26)
    • September (6)
    • April (10)
Comparison page for RavenDB and MongoDB
  previous post next post  
Feb 26 2020

Throttling API calls in a distributed environment with RavenDB

time to read 1 min | 40 words

I wanted to draw your attention to the Throttling Outgoing HTTP Requests in a Distributed Environment Using RavenDB and .NET Core article by Kamran.

Here is showing there an interesting real world use case for using distributed counters with RavenDB>

Tweet Share Share 3 comments
Tags:
  • raven
  • ravendb.net
  • community

  previous post next post  

Comments

Jason
26 Feb 2020
19:06 PM
Jason

That example seems weird, it didn't achieve what its goal.

  • He put MaximumRequestLimit and SlidingTimeWindowInSeconds inside model, my guess that treat as setting, which is already weird place to begin with. He never use it at end. He use hard coded variables. REQUEST_LIMIT and SLIDING_TIME_WINDOW_IN_SECONDS.
  • The counter is not aware of time. This is not 30 request every 30 seconds limit. It can as far as goes to 30 request every 900 seconds. e.g. every request have 29~30 seconds apart. That document will constantly sliding whenever the count increment.

One way to implement would be time ranged document. ```json5 { id: 'throttling/counter/{purpose}/2020-02-27T02:35:30Z' } // document has counter ~~~ The ID of document should be 15 seconds apart. e.g. * throttling/counter/{purpose}/2020-02-27T02:35:30Z * throttling/counter/{purpose}/2020-02-27T02:35:45Z * throttling/counter/{purpose}/2020-02-27T02:36:00Z * throttling/counter/{purpose}/2020-02-27T02:36:15Z

Whenever request happen, get current range and last range, add together it's current allocated request count. If total exceed 30, then stop. The document can also have expire time, such as expire after 2 hours, so database can clean up the junks.

e.g. If current time is 2020-02-27T02:36:29Z, I will grab 2020-02-27T02:36:15Z and 2020-02-27T02:36:00Ztime frame document. If first time range had 10 count, second had 5 count, its safe. If first range had 30 and second range had 0, means it is currently on throttling.

The time range can vary, 15 seconds is more accurate than 30 seconds, time range too small means too many documents to maintain.

On the other hand, throttling normally happen at API level, if client level, it can implement in memory. Such as Interlocked.Increment. But that's not the main problem. I know the article is an example of use expiration, counter, locker, but it is not the solution for its initial goal.

Jason
26 Feb 2020
19:35 PM
Jason

The example I give didn't explain the issue well. The better example would be small pock to cause constantly slide, then sudden spike will hit limit.

  1. On 0 mark, requested 28 requests. Expire on 30 mark. (count is 28)
  2. On 29 mark, did another request, which slide to expire on 59 mark. (count is now 29)
  3. On 58 mark, I want to do another 29 requests, now the count is 29, which only allow me to execute one request. The other 28 request have to wait 30 more seconds.
Kamran
29 Feb 2020
21:03 PM
Kamran

Hi @Jason,

Thanks for the feedback, let me see if I can help clarify a few things.

He put MaximumRequestLimit and SlidingTimeWindowInSeconds inside model, my guess that treat as setting, which is already weird place to begin with. He never use it at end. He use hard coded variables. REQUEST_LIMIT and SLIDING_TIME_WINDOW_IN_SECONDS.

The example model with MaximumRequestLimit and SlidingTimeWindowInSeconds is meant to be an illustration, the demo code doesn't actually use that to store the settings. I wanted to just call out that you could load settings from the database instead of hardcoding, if that's what you wished to do. You may want to store them separately, in a MyServiceConfig document, for example.

The counter is not aware of time. This is not 30 request every 30 seconds limit. It can as far as goes to 30 request every 900 seconds. e.g. every request have 29~30 seconds apart. That document will constantly sliding whenever the count increment.

I am not sure I understand. In the demo, the counter will increment to 30 and will stop making requests until the window expires (which at a minimum will be 30 seconds and at a maximum, 90 seconds due to the RavenDB setting for 'delete frequency interval'). The window doesn't reset when the counter is incremented, they are two separate mechanisms, it only resets when RavenDB deletes the expired document.

If the first request took 30 seconds, that's OK, because we're throttling to 30 requests per 30s interval, we won't hit the external API rate limit if that's the case. We just don't want to go over it, we're rate limiting ourselves. We are not trying to "evenly distribute" requests, I think that's a different story altogether.

To give a solid example, I use this in production to throttle outgoing requests to a web service that will throw an exception if you exceed 200 requests within 30 minutes. This throttling mechanism ensures I don't exceed it (and in the rare case it allows an additional request, it's a message queue, so the message will be put back onto the queue and be throttled next time).

On the other hand, throttling normally happen at API level, if client level, it can implement in memory. Such as Interlocked.Increment.

The premise of the article is that you can't do it in-memory when you have multiple processes running on a cluster, so other solutions using Interlocked.Increment don't quite work. You need some kind of backing store to coordinate the throttling and so I present one way to do it using RavenDB alone.

The better example would be small pock to cause constantly slide, then sudden spike will hit limit.

I don't think this is quite correct. Remember as long as the counter is at or above maximum, the code must wait until the document expires and RavenDB deletes it, to resume again. Incrementing the counter has no bearing on the expiration time (it is set at document creation time). Only when the document is deleted will the counter reset back to 0.

I encourage you to run the sample application and check it out, I think it does what you expect and I've been running essentially the same code in production for some weeks now to help proactively manage requests to an external API without much issue. I could be misunderstanding what you mean, though, so let me know if it still is confusing.

Cheers, Kamran

Comment preview

Comments have been closed on this topic.

Markdown formatting

ESC to close

Markdown turns plain text formatting into fancy HTML formatting.

Phrase Emphasis

*italic*   **bold**
_italic_   __bold__

Links

Inline:

An [example](http://url.com/ "Title")

Reference-style labels (titles are optional):

An [example][id]. Then, anywhere
else in the doc, define the link:
  [id]: http://example.com/  "Title"

Images

Inline (titles are optional):

![alt text](/path/img.jpg "Title")

Reference-style:

![alt text][id]
[id]: /url/to/img.jpg "Title"

Headers

Setext-style:

Header 1
========
Header 2
--------

atx-style (closing #'s are optional):

# Header 1 #
## Header 2 ##
###### Header 6

Lists

Ordered, without paragraphs:

1.  Foo
2.  Bar

Unordered, with paragraphs:

*   A list item.
    With multiple paragraphs.
*   Bar

You can nest them:

*   Abacus
    * answer
*   Bubbles
    1.  bunk
    2.  bupkis
        * BELITTLER
    3. burper
*   Cunning

Blockquotes

> Email-style angle brackets
> are used for blockquotes.
> > And, they can be nested.
> #### Headers in blockquotes
> 
> * You can quote a list.
> * Etc.

Horizontal Rules

Three or more dashes or asterisks:

---
* * *
- - - - 

Manual Line Breaks

End a line with two or more spaces:

Roses are red,   
Violets are blue.

Fenced Code Blocks

Code blocks delimited by 3 or more backticks or tildas:

```
This is a preformatted
code block
```

Header IDs

Set the id of headings with {#<id>} at end of heading line:

## My Heading {#myheading}

Tables

Fruit    |Color
---------|----------
Apples   |Red
Pears	 |Green
Bananas  |Yellow

Definition Lists

Term 1
: Definition 1
Term 2
: Definition 2

Footnotes

Body text with a footnote [^1]
[^1]: Footnote text here

Abbreviations

MDD <- will have title
*[MDD]: MarkdownDeep

 

FUTURE POSTS

  1. NOT Sharding RavenDB Vector Search - 6 hours from now
  2. Optimizing the cost of clearing a set - 3 days from now
  3. Scaling HNSW in RavenDB: Optimizing for inadequate hardware - 5 days from now

There are posts all the way to May 14, 2025

RECENT SERIES

  1. RavenDB News (2):
    02 May 2025 - May 2025
  2. Recording (15):
    30 Apr 2025 - Practical AI Integration with RavenDB
  3. Production Postmortem (52):
    07 Apr 2025 - The race condition in the interlock
  4. RavenDB (13):
    02 Apr 2025 - .NET Aspire integration
  5. RavenDB 7.1 (6):
    18 Mar 2025 - One IO Ring to rule them all
View all series

RECENT COMMENTS

  • ถูกใจ ร้านนี้สุดๆ! อุปกรณ์ จาก inkspa pantip ทำให้ ผลงาน สวย จ้าง เสื้อ ทีม ได้ งาน ลงตัว ราคา คุ้ม ชวน ใคร อยากได้ ดีไซน...
    By ink-spa พันทิป on The null check that didn't check for nulls
  • But in case you have nullability checks enabled (i.e. `<Nullable>enable</Nullable>`), then you'll have a compiler warning on ...
    By Samyon Ristov on The null check that didn't check for nulls
  • Grok wasn't *wrong*. It only said that `_items` can't be null for the condition to evaluate to `true`, but didn't say anythi...
    By Johannes Egger on The null check that didn't check for nulls
  • When I started enabling NRT, I remember I was initially confused when all variables (for reference types) declared with `var`...
    By riccardo on The null check that didn't check for nulls
  • That is surprising - I think of var as a shorthand that does not affect the final result of the compilation. I wouldn't expec...
    By Chris B on The null check that didn't check for nulls

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats
}