///|
pub(all) struct KeyedFixedWindow {
limit : Int
window_ms : Int
mut windows : @hashmap.HashMap[String, FixedWindow]
mut last_seen : @hashmap.HashMap[String, Int]
} derive(Debug)
///|
pub fn KeyedFixedWindow::new(limit : Int, window_ms : Int) -> KeyedFixedWindow {
KeyedFixedWindow::{
limit: positive(limit, 1),
window_ms: positive(window_ms, 1),
windows: @hashmap.HashMap([]),
last_seen: @hashmap.HashMap([]),
}
}
///|
fn KeyedFixedWindow::window_for(
self : KeyedFixedWindow,
key : String,
now_ms : Int,
) -> FixedWindow {
match self.windows.get(key) {
Some(window) => window
None => {
let window = FixedWindow::new(self.limit, self.window_ms, start_ms=now_ms)
self.windows.set(key, window)
window
}
}
}
///|
pub fn KeyedFixedWindow::allow_at(
self : KeyedFixedWindow,
key : String,
now_ms : Int,
cost? : Int = 1,
) -> Decision {
let window = self.window_for(key, now_ms)
self.last_seen.set(key, now_ms)
window.allow_at(now_ms, cost~)
}
///|
pub fn KeyedFixedWindow::len(self : KeyedFixedWindow) -> Int {
self.windows.length()
}
///|
pub fn KeyedFixedWindow::prune_idle(
self : KeyedFixedWindow,
now_ms : Int,
max_idle_ms : Int,
) -> Int {
prune_string_maps(self.last_seen, self.windows, now_ms, max_idle_ms)
}
///|
pub(all) struct KeyedSlidingLog {
limit : Int
window_ms : Int
mut logs : @hashmap.HashMap[String, SlidingLog]
mut last_seen : @hashmap.HashMap[String, Int]
} derive(Debug)
///|
pub fn KeyedSlidingLog::new(limit : Int, window_ms : Int) -> KeyedSlidingLog {
KeyedSlidingLog::{
limit: positive(limit, 1),
window_ms: positive(window_ms, 1),
logs: @hashmap.HashMap([]),
last_seen: @hashmap.HashMap([]),
}
}
///|
fn KeyedSlidingLog::log_for(
self : KeyedSlidingLog,
key : String,
_now_ms : Int,
) -> SlidingLog {
match self.logs.get(key) {
Some(log) => log
None => {
let log = SlidingLog::new(self.limit, self.window_ms)
self.logs.set(key, log)
log
}
}
}
///|
pub fn KeyedSlidingLog::allow_at(
self : KeyedSlidingLog,
key : String,
now_ms : Int,
cost? : Int = 1,
) -> Decision {
let log = self.log_for(key, now_ms)
self.last_seen.set(key, now_ms)
log.allow_at(now_ms, cost~)
}
///|
pub fn KeyedSlidingLog::len(self : KeyedSlidingLog) -> Int {
self.logs.length()
}
///|
pub fn KeyedSlidingLog::prune_idle(
self : KeyedSlidingLog,
now_ms : Int,
max_idle_ms : Int,
) -> Int {
prune_string_maps(self.last_seen, self.logs, now_ms, max_idle_ms)
}
///|
pub(all) struct KeyedGcra {
limit : Int
period_ms : Int
burst_capacity : Int
mut states : @hashmap.HashMap[String, Gcra]
mut last_seen : @hashmap.HashMap[String, Int]
} derive(Debug)
///|
pub fn KeyedGcra::new(
limit : Int,
period_ms : Int,
burst_capacity? : Int = 1,
) -> KeyedGcra {
KeyedGcra::{
limit: positive(limit, 1),
period_ms: positive(period_ms, 1),
burst_capacity: positive(burst_capacity, 1),
states: @hashmap.HashMap([]),
last_seen: @hashmap.HashMap([]),
}
}
///|
fn KeyedGcra::state_for(self : KeyedGcra, key : String, now_ms : Int) -> Gcra {
match self.states.get(key) {
Some(state) => state
None => {
let state = Gcra::new(
self.limit,
self.period_ms,
burst_capacity=self.burst_capacity,
start_ms=now_ms,
)
self.states.set(key, state)
state
}
}
}
///|
pub fn KeyedGcra::allow_at(
self : KeyedGcra,
key : String,
now_ms : Int,
cost? : Int = 1,
) -> Decision {
let state = self.state_for(key, now_ms)
self.last_seen.set(key, now_ms)
state.allow_at(now_ms, cost~)
}
///|
pub fn KeyedGcra::len(self : KeyedGcra) -> Int {
self.states.length()
}
///|
pub fn KeyedGcra::prune_idle(
self : KeyedGcra,
now_ms : Int,
max_idle_ms : Int,
) -> Int {
prune_string_maps(self.last_seen, self.states, now_ms, max_idle_ms)
}
///|
pub(all) struct KeyedConcurrencyLimiter {
capacity : Int
mut states : @hashmap.HashMap[String, ConcurrencyLimiter]
} derive(Debug)
///|
pub fn KeyedConcurrencyLimiter::new(capacity : Int) -> KeyedConcurrencyLimiter {
KeyedConcurrencyLimiter::{
capacity: positive(capacity, 1),
states: @hashmap.HashMap([]),
}
}
///|
fn KeyedConcurrencyLimiter::state_for(
self : KeyedConcurrencyLimiter,
key : String,
) -> ConcurrencyLimiter {
match self.states.get(key) {
Some(state) => state
None => {
let state = ConcurrencyLimiter::new(self.capacity)
self.states.set(key, state)
state
}
}
}
///|
pub fn KeyedConcurrencyLimiter::acquire(
self : KeyedConcurrencyLimiter,
key : String,
) -> Decision {
self.state_for(key).acquire()
}
///|
pub fn KeyedConcurrencyLimiter::release(
self : KeyedConcurrencyLimiter,
key : String,
) -> Bool {
match self.states.get(key) {
Some(state) => state.release()
None => false
}
}
///|
pub fn KeyedConcurrencyLimiter::in_use(
self : KeyedConcurrencyLimiter,
key : String,
) -> Int {
match self.states.get(key) {
Some(state) => state.in_use()
None => 0
}
}
///|
pub fn KeyedConcurrencyLimiter::len(self : KeyedConcurrencyLimiter) -> Int {
self.states.length()
}
///|
fn[V] prune_string_maps(
last_seen : @hashmap.HashMap[String, Int],
values : @hashmap.HashMap[String, V],
now_ms : Int,
max_idle_ms : Int,
) -> Int {
let deadline = now_ms - positive(max_idle_ms, 1)
let keys = last_seen.keys().to_array()
let mut removed = 0
for key in keys {
match last_seen.get(key) {
Some(last) =>
if last < deadline {
last_seen.remove(key)
values.remove(key)
removed = removed + 1
}
None => ()
}
}
removed
}