Composed Limits
Composed Limits: a real provider enforces BOTH a billed quota and a connection ceiling. quota.and(pool) - new this round - holds the two laws in one limiter, and the run proves each law bound the traffic in its own dimension: admissions per window AND concurrent in-flight.
Reliability & Recovery
Round 7
Samuel Williams
exit 0
bundle exec ruby examples/composed_limits.rb
a real captured run
COMPOSED LIMITS: 12 calls x 50ms, quota 6/200ms AND pool of 2 window 1 ( 0-200ms): ###### 6 admitted (quota allows 6) window 2 (200-400ms): ###### 6 admitted (quota allows 6) pool high-water: 2 of 2 - the socket law held quota high-water: 6 concurrent (window law measures admissions, not flight) which law binds? the pool could clear ~8 per window (2 lanes x 4 service times) but the quota admits only 6 - so the QUOTA is the binding constraint, and the chart shows it: exactly 6 per window. raise the quota and the pool becomes the wall. composition enforces both laws and the windows tell you which one is throttling you. ordering note: quota.and(pool) spends quota BEFORE waiting for a socket - correct, because sockets are scarce and quota refills on a clock. the reverse order would hold a connection hostage while waiting for the meter.
source
# frozen_string_literal: true # Composed Limits: a real provider enforces BOTH a billed quota and a # connection ceiling. quota.and(pool) - new this round - holds the two # laws in one limiter, and the run proves each law bound the traffic # in its own dimension: admissions per window AND concurrent in-flight. # # bundle exec ruby examples/composed_limits.rb # # Runs offline; 12 slow calls against quota 6/200ms and pool of 2. require class="s">"bundler/setup" require class="s">"agentic" require class="s">"async" REQUESTS = 12 CALL_TIME = 0.05 quota = Agentic:class="y">:RateLimit.new(6, per: 0.2) # billing law: 6 per 200ms pool = Agentic:class="y">:RateLimit.new(2) # socket law: 2 in flight limit = quota.and(pool) # both, in that order admissions = [] started = Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) Sync do REQUESTS.times.map { Async do limit.acquire do admissions << Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) - started sleep(CALL_TIME) end end }.each(&class="y">:wait) end puts class="s">"COMPOSED LIMITS: #{REQUESTS} calls x #{(CALL_TIME * 1000).round}ms, " \ class="s">"quota 6/200ms AND pool of 2" puts buckets = admissions.group_by { |t| (t / 0.2).floor } buckets.sort.each do |window, hits| puts format(class="s">" window %d (%3d-%3dms): %-8s %d admitted (quota allows 6)", window + 1, window * 200, (window + 1) * 200, class="s">"#" * hits.size, hits.size) end puts puts format(class="s">" pool high-water: %d of %d - the socket law held", pool.high_water, pool.ceiling) puts format(class="s">" quota high-water: %d concurrent (window law measures admissions, not flight)", quota.high_water) puts puts class="s">"which law binds? the pool could clear ~8 per window (2 lanes x 4" puts class="s">"service times) but the quota admits only 6 - so the QUOTA is the" puts class="s">"binding constraint, and the chart shows it: exactly 6 per window." puts class="s">"raise the quota and the pool becomes the wall. composition enforces" puts class="s">"both laws and the windows tell you which one is throttling you." puts puts class="s">"ordering note: quota.and(pool) spends quota BEFORE waiting for a" puts class="s">"socket - correct, because sockets are scarce and quota refills on a" puts class="s">"clock. the reverse order would hold a connection hostage while" puts class="s">"waiting for the meter."