├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md └── simulate.rb /.gitignore: -------------------------------------------------------------------------------- 1 | tmp/* 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'gruff' 4 | gem 'rmagick', '~> 4.0.0' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | gruff (0.6.0) 5 | rmagick (>= 2.13.4) 6 | rmagick (4.0.0) 7 | 8 | PLATFORMS 9 | ruby 10 | 11 | DEPENDENCIES 12 | gruff 13 | rmagick (~> 4.0.0) 14 | 15 | BUNDLED WITH 16 | 2.0.2 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simulate Memory needs for serving web requests 2 | 3 | ![](https://www.dropbox.com/s/eqoujwsf8mrc1s1/Screenshot%202019-10-28%2014.07.29.png?raw=1) 4 | 5 | ## Install 6 | 7 | ``` 8 | $ bundle install 9 | ``` 10 | 11 | ## Run 12 | 13 | ``` 14 | $ ruby simulate.rb 15 | ``` 16 | 17 | When you do this a graphic will be generated to the `tmp` directory with the simulation results and if you're on a mac or linux machine the image will pop up. 18 | 19 | ## Options 20 | 21 | Simulate multiple threads: 22 | 23 | ``` 24 | $ THREAD_COUNT=5 ruby simulate.rb 25 | ``` 26 | 27 | Look at the environment variables at the top of `simulate.rb` for more options. 28 | -------------------------------------------------------------------------------- /simulate.rb: -------------------------------------------------------------------------------- 1 | require 'gruff' 2 | require 'pathname' 3 | require 'fileutils' 4 | 5 | thread_count = ENV.fetch('THREAD_COUNT') { 1 }.to_i 6 | maximum_memory = !!ENV["DISABLE_MAX_MEMORY"].nil? 7 | request_count_min = ENV.fetch('REQUEST_COUNT_MIN') { 1 }.to_i 8 | request_count_max = ENV.fetch('REQUEST_COUNT_MAX') { 10 }.to_i 9 | 10 | puts "Simulating..." 11 | puts "Thread count: #{thread_count}" 12 | puts "Showing Maximum memory: #{maximum_memory}" 13 | puts "Request count min: #{request_count_min}" 14 | puts "Request count max: #{request_count_max}" 15 | 16 | 17 | entry_hash = Hash.new {|h,k| h[k] = [] } 18 | 19 | def simulate_request(request_array: , height_multiply: rand(1..4), duration: rand(10..100), before_start: rand(1..100), after_start: rand(1..100)) 20 | before_start.times.each do 21 | request_array << 0 22 | end 23 | 24 | (1...duration).each do |x| 25 | request_array << x * height_multiply 26 | end 27 | 28 | after_start.times.each do 29 | request_array << 0 30 | end 31 | end 32 | 33 | def find_max_count_from_hash(entry_hash) 34 | max_count = 0 35 | entry_hash.each do |k,v| 36 | count = v.length 37 | max_count = count if count > max_count 38 | end 39 | return max_count 40 | end 41 | 42 | def pad_entry_hashes_to_be_same_length(entry_hash) 43 | max_count = find_max_count_from_hash(entry_hash) 44 | 45 | entry_hash.each do |k, v| 46 | count = v.length 47 | if count < max_count 48 | v.fill(0, count...max_count) 49 | end 50 | end 51 | end 52 | 53 | thread_count.times.each do |thread_number| 54 | rand(request_count_min..request_count_max).times.each do 55 | simulate_request(request_array: entry_hash["Thread #{thread_number+1}"]) 56 | end 57 | end 58 | 59 | pad_entry_hashes_to_be_same_length(entry_hash) 60 | 61 | if maximum_memory 62 | values = entry_hash.values 63 | mega_array = values.pop.dup 64 | values.each do |v_array| 65 | mega_array = mega_array.zip(v_array).map(&:sum) 66 | end 67 | 68 | max_val = 0 69 | mega_array.each_with_index do |val, i| 70 | if max_val > val 71 | mega_array[i] = max_val 72 | else 73 | max_val = val 74 | end 75 | end 76 | 77 | entry_hash["Max Total"] = mega_array 78 | end 79 | 80 | time_name = Time.now.strftime('%Y-%m-%d-%H-%M-%s-%N').to_s 81 | 82 | output_dir = Pathname.new("tmp") 83 | FileUtils.mkdir_p(output_dir) 84 | 85 | g = Gruff::Line.new(1000, 1000) 86 | g.hide_dots = true 87 | g.title = "Simulating Memory Requirements\nof Requests Over Time" 88 | g.x_axis_label = 'Time (unitless)' 89 | g.y_axis_label = 'Memory use (unitless)' 90 | entry_hash.each do |k, v| 91 | g.data k, v 92 | end 93 | file_name = output_dir.join("#{time_name}-chart.png") 94 | g.write(file_name) 95 | 96 | puts file_name 97 | `open #{file_name}` 98 | --------------------------------------------------------------------------------