agentic examples

The Jitter Shootout

The Jitter Shootout: none vs equal (+/-25%, the default) vs full (uniform over [0, delay], new this round) - same forty workers, same synchronized failure, three retry-arrival histograms. Pick your herd size with your eyes open.

Reliability & Recovery Round 6 Mike Perham exit 0

source on github

bundle exec ruby examples/jitter_shootout.rb

a real captured run

JITTER SHOOTOUT: 40 workers, synchronized failure, 150ms base backoff (seed 20260707)

  none (jitter: false):
     325- 350ms  ###                                      3
     350- 375ms  #####################################    37
    peak 37 workers per bucket, spread 25ms

  equal +/-25% (the default):
     100- 125ms  ##                                       2
     125- 150ms  ##################                       18
     150- 175ms  ##########                               10
     175- 200ms  ##########                               10
    peak 18 workers per bucket, spread 76ms

  full [0, delay] (jitter: :full):
       0-  25ms  ###                                      3
      25-  50ms  ##############                           14
      50-  75ms  ######                                   6
      75- 100ms  #####                                    5
     100- 125ms  ####                                     4
     125- 150ms  #######                                  7
     150- 175ms  #                                        1
    peak 14 workers per bucket, spread 150ms

scoreboard (peak herd, smaller is safer):
  none (jitter: false)             37 of 40
  equal +/-25% (the default)       18 of 40
  full [0, delay] (jitter: :full)  14 of 40

full jitter trades punctuality for survival: retries land anywhere
in [0, delay], so some come back early and few come back TOGETHER.
when the upstream is already hurting, together is the only wrong time.

source

# frozen_string_literal: true

# The Jitter Shootout: none vs equal (+/-25%, the default) vs full
# (uniform over [0, delay], new this round) - same forty workers, same
# synchronized failure, three retry-arrival histograms. Pick your
# herd size with your eyes open.
#
#   bundle exec ruby examples/jitter_shootout.rb [seed]
#
# Runs offline and deterministically.

require class="s">"bundler/setup"
require class="s">"agentic"

Agentic.logger.level = class="y">:fatal

WORKERS = 40
BACKOFF = 0.15
BUCKET_MS = 25

def run_mode(jitter, seed)
  srand(seed)
  started = Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC)
  arrivals = []
  attempts = Hash.new(0)

  orchestrator = Agentic:class="y">:PlanOrchestrator.new(
    concurrency_limit: WORKERS,
    retry_policy: {
      max_retries: 2,
      retryable_errors: [class="s">"RuntimeError"],
      backoff_strategy: class="y">:constant,
      backoff_constant: BACKOFF,
      backoff_jitter: jitter
    }
  )

  WORKERS.times do |i|
    orchestrator.add_task(Agentic:class="y">:Task.new(
      description: class="s">"worker-#{i}",
      agent_spec: {class="s">"name" => class="s">"worker", class="s">"instructions" => class="s">"call"}
    ), agent: ->(t) {
      attempts[t.description] += 1
      raise class="s">"hiccup" if attempts[t.description] == 1

      arrivals << Process.clock_gettime(Process:class="y">:CLOCK_MONOTONIC) - started
      class="y">:ok
    })
  end
  orchestrator.execute_plan
  arrivals
end

def report(label, arrivals)
  buckets = arrivals.group_by { |t| (t * 1000 / BUCKET_MS).floor }
  peak = buckets.values.map(&class="y">:size).max
  spread = ((arrivals.max - arrivals.min) * 1000).round

  puts class="s">"  #{label}:"
  buckets.sort.each do |bucket, hits|
    puts format(class="s">"    %4d-%4dms  %-40s %d", bucket * BUCKET_MS, (bucket + 1) * BUCKET_MS,
      class="s">"#" * hits.size, hits.size)
  end
  puts format(class="s">"    peak %d workers per bucket, spread %dms", peak, spread)
  puts
  peak
end

seed = (ARGV.first || 20260707).to_i
puts class="s">"JITTER SHOOTOUT: #{WORKERS} workers, synchronized failure, " \
  class="s">"#{(BACKOFF * 1000).round}ms base backoff (seed #{seed})"
puts

peaks = {
  class="s">"none (jitter: false)" => run_mode(false, seed),
  class="s">"equal +/-25% (the default)" => run_mode(true, seed),
  class="s">"full [0, delay] (jitter: class="y">:full)" => run_mode(class="y">:full, seed)
}.map { |label, arrivals| [label, report(label, arrivals)] }

puts class="s">"scoreboard (peak herd, smaller is safer):"
peaks.each { |label, peak| puts format(class="s">"  %-32s %2d of %d", label, peak, WORKERS) }
puts
puts class="s">"full jitter trades punctuality for survival: retries land anywhere"
puts class="s">"in [0, delay], so some come back early and few come back TOGETHER."
puts class="s">"when the upstream is already hurting, together is the only wrong time."