Skip to content

Add adaptive Hystrix implementation to support requests with near baseline latency#155

Open
kitanoyoru wants to merge 10 commits intocep21:masterfrom
kitanoyoru:feat/hystrix-adaptive
Open

Add adaptive Hystrix implementation to support requests with near baseline latency#155
kitanoyoru wants to merge 10 commits intocep21:masterfrom
kitanoyoru:feat/hystrix-adaptive

Conversation

@kitanoyoru
Copy link
Copy Markdown

@kitanoyoru kitanoyoru commented Apr 12, 2026

Closes #57

Hi, @cep21

This contribution adds closers/hystrix-adaptive: a ClosedToOpen implementation that wraps the standard Hystrix opener and defers opening the circuit when recent failures are mostly timeouts and additive latency headroom (extra) is still below MaxExtraLatency

How it works

Headroom is tracked on top of BaselineLatency. It increases on timeouts and on successes slower than baseline + extra (step size IncreaseExtra, capped by MaxExtraLatency). It decreases when successes finish faster than BaselineLatency (step size DecreaseExtra)

ShouldOpen first checks the inner Hystrix opener (volume + error %). If the inner wants to open, the adaptive layer may still keep the breaker closed when extra is in $(0, MaxExtraLatency)$ and the $\frac{\text{timeouts}}{timeouts + failures}\ge MinTimeoutRatioToDefer$ - so deadline pressure does not trip the breaker while headroom remains

When extra reaches MaxExtraLatency, that deferral stops: if the inner opener still wants to open, ShouldOpen returns true so the circuit can open after sustained overload

Closed / Opened reset adaptive state (including extra and adaptive rolling counters)

Demo

Source code can be found here

───────────────────────────────────────────────────────────────────────────────────────────────────────────────
System configuration
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ▸ Go go1.26.2  GOOS=darwin  GOARCH=arm64  compiler=gc
  ▸ CPUs=10  GOMAXPROCS=10  goroutines=1  pid=17723
  ▸ Working directory: /Users/kitanoyoru/dev/circuit
  ▸ Main module: github.com/cep21/circuit/v4  version: (devel)
  ▸ Memory: heap alloc 230 KiB  heap inuse 664 KiB  sys 7 MiB  GC=0


───────────────────────────────────────────────────────────────────────────────────────────────────────────────
[1/5] Default Hystrix circuit
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ▸ Hystrix opener
  ▸ rolling ≥3 requests, error% ≥ 50
  ▸ per-request deadline 5ms
      1/3     5.765ms  timeout  breaker CLOSED
      2/3      5.68ms  timeout  breaker CLOSED
      3/3     5.712ms  timeout  breaker OPEN
  ⇒ breaker OPEN

───────────────────────────────────────────────────────────────────────────────────────────────────────────────
[2/5] Adaptive Hystrix circuit — timeout-heavy: defer OPEN (headroom below MaxExtraLatency)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ▸ ConfigureOpener (embedded, effective)
     RequestVolumeThreshold=3  ErrorThresholdPercentage=50  NumBuckets=10  RollingDuration=10s
  ▸ ConfigureAdaptive (effective)
     BaselineLatency=10ms  MaxExtraLatency=200ms
     IncreaseExtra=10ms  DecreaseExtra=10ms  MinTimeoutRatioToDefer=0.85
  — request latency 15ms
      1/3    16.075ms  ok       breaker CLOSED  hr=10ms
      2/3    16.054ms  ok       breaker CLOSED  hr=20ms
      3/3    15.352ms  ok       breaker CLOSED  hr=30ms
  ⇒ breaker CLOSED

───────────────────────────────────────────────────────────────────────────────────────────────────────────────
[3/5] Adaptive Hystrix circuit — headroom min / max / rate (successes only)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ▸ ConfigureOpener (embedded, effective)
     RequestVolumeThreshold=3  ErrorThresholdPercentage=50  NumBuckets=10  RollingDuration=10s
  ▸ ConfigureAdaptive (effective)
     BaselineLatency=50ms  MaxExtraLatency=80ms
     IncreaseExtra=10ms  DecreaseExtra=10ms  MinTimeoutRatioToDefer=0.85
  — Phase A · request latency 75ms → headroom ↑
      1/3    76.053ms  ok       breaker CLOSED  hr=10ms
      2/3    75.564ms  ok       breaker CLOSED  hr=20ms
      3/3    76.062ms  ok       breaker CLOSED  hr=30ms
  — Phase B · request latency 25ms → headroom ↓
      1/3    25.622ms  ok       breaker CLOSED  hr=20ms
      2/3    25.344ms  ok       breaker CLOSED  hr=10ms
      3/3    25.116ms  ok       breaker CLOSED  hr=0
  ⇒ breaker CLOSED · headroom 0 (was 30ms after phase A)

───────────────────────────────────────────────────────────────────────────────────────────────────────────────
[4/5] Adaptive Hystrix circuit opens because of unexpected errors
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ▸ ConfigureOpener (embedded, effective)
     RequestVolumeThreshold=3  ErrorThresholdPercentage=50  NumBuckets=10  RollingDuration=10s
  ▸ ConfigureAdaptive (effective)
     BaselineLatency=100ms  MaxExtraLatency=200ms
     IncreaseExtra=10ms  DecreaseExtra=10ms  MinTimeoutRatioToDefer=0.85
  — Need ≥3 requests in window before trip (then error% ≥ 50)
      1/3        16µs  error    breaker CLOSED  hr=0
      2/3         3µs  error    breaker CLOSED  hr=0
      3/3         5µs  error    breaker OPEN  hr=0
  ⇒ breaker OPEN

───────────────────────────────────────────────────────────────────────────────────────────────────────────────
[5/5] Adaptive Hystrix circuit opens because of too much long running requests (extra at MaxExtraLatency)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ▸ ConfigureOpener (embedded, effective)
     RequestVolumeThreshold=3  ErrorThresholdPercentage=50  NumBuckets=10  RollingDuration=10s
  ▸ ConfigureAdaptive (effective)
     BaselineLatency=100ms  MaxExtraLatency=30ms
     IncreaseExtra=10ms  DecreaseExtra=10ms  MinTimeoutRatioToDefer=0.85
  — Phase A · request latency 40ms
      1/3    41.059ms  ok       breaker CLOSED  hr=0
      2/3    41.062ms  ok       breaker CLOSED  hr=0
      3/3    41.056ms  ok       breaker CLOSED  hr=0
  — Phase B · request latency 150ms
      1/3   150.906ms  ok       breaker CLOSED  hr=10ms
      2/3   151.063ms  ok       breaker CLOSED  hr=20ms
      3/3   151.066ms  ok       breaker OPEN  hr=0
  ⇒ breaker OPEN

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow adaptive limits

1 participant