Skip to content

Instantly share code, notes, and snippets.

@bschaeffer
Last active July 30, 2020 19:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bschaeffer/0b8e8d38aa60435700847313d32459c7 to your computer and use it in GitHub Desktop.
Save bschaeffer/0b8e8d38aa60435700847313d32459c7 to your computer and use it in GitHub Desktop.
Load testing example (producer->consumer pattern)
class LoadTest
def initialize(time:, rate:, pool:)
@time = time
@rate = rate
@pool = pool
@requests = []
@started = false
@shutdown = false
@lock = Mutex.new
@cond = ConditionVariable.new
@queue = Queue.new
end
def start!(producer, consumer)
return if @started
@started = true
@producer = Thread.new { run_producer(producer) }
@consumers = @pool.times.map { |i| Thread.new(i) { |x| run_consumer(x, consumer) } }
end
def rate=(rate)
@lock.synchronize { @rate = rate }
end
def time=(time)
@lock.synchronize { @time = time }
end
def shutdown!
@shutdown = true
@pool.times { @queue << :shutdown }
end
def run_producer(producer)
loop do
if @shutdown
puts "[producer] shutting down"
break
end
@lock.synchronize do
@requests.reject! { |v| v < Time.now - @time }
if @requests.size < @rate
@queue << producer.call
@cond.wait(@lock)
else
sleep(0.1)
end
end
end
end
def run_consumer(tname, consumer)
loop do
job = @queue.pop
if job == :shutdown
puts "[consumer:#{tname}] shutting down"
break
end
consumer.call(job)
@lock.synchronize do
now = Time.now
puts "[consumer:#{tname}] #{Time.now}"
@requests << now
@cond.signal
end
end
end
end
test = LoadTest.new(time: 1, rate: 21, pool: 5)
data = Customer.limit(100).pluck(:id)
producer = -> { data.sample }
consumer = ->(id) { puts "Got customer: #{id}" }
test.start!(producer, consumer)
test.rate = 30 # increase the rate
test.shutdown!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment