I've trying to build a web API throttling mechanism in ASP.NET Core that "penalizes" excess usage in an exponentially-growing denial window e.g. first 3 tries are allowed, 4th is blocked if within 60 seconds, 5th will blocked if within 120s, 6th blocked for 240s, etc.
The latest .Net 7's API rate-limiting is great, but rate-limiting isn't throttling and I couldn't find a way to augment it enough, either by partitioning or otherwise, as it would lack that sliding window feature.
I tried different options using LazyCache, directly with IMemoryCache, and my favourite, IDistributedCache - and looked at what others did, such as suggested by @JsAndDotNet here. However, all these derivatives suffer from the same security flaw when the cache is full (flaw for throttling purposes, that is): Adding a cache entry when the cache is full doesn't return any error (as discussed here). In fact, it continues as if that entry was added successfully to the cache. You can catch that asynchronically through a callback - but at that point, access has been already granted.
An attacker could simply flood the cache memory until its full, virtually bypassing the throttling mechanism on all subsequent requests. When the cache memory is full, compaction is triggered - which is another bad thing for throttling security, because it get rids of potentially relevant entries in the cache.
My question is how do I avoid that "cache full" vulnerability and catch it synchronically so I can block all transactions until memory is available again?