Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api endpoint with thread to stress test apiroom #60

Merged
merged 3 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions app/controllers/stress_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class StressController < ApplicationController
skip_before_action :verify_authenticity_token
http_basic_authenticate_with name: Rails.application.credentials.stress[:user],
password: Rails.application.credentials.stress[:password]

def create
command = "rake stress:apiroom --"\
"#{generate_task_param('file', file_name)}"\
"#{generate_task_param('rps', params[:rps])}"\
"#{generate_task_param('time', params[:time])}"\
"#{generate_task_param('fake', params[:fake])}"\

Thread.new { system(command) }
render plain: "Task created, files will be named #{file_name}"
end

private

def file_name
"result_#{Time.now.to_i}"
end

def generate_task_param(key, param)
" --#{key} #{param}" if param.present?
end
end
2 changes: 1 addition & 1 deletion config/credentials.yml.enc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
XIfh1lvKUdlxK550NYgSWq8AyN91XelxwVkiFlQ8SBoejOQzKebJ1xHRli4/wQbkF6UygfNITdO8fDSxBFd2iy1Krksh0R4ePyBny6unPFeVYiy6kfWqI0lF0IGCLu/Z7XrdC1LjVNA9pbRgym7SCJReNsIdmQEX0ZL7kNqTdw+I7+AnCUwHxMk7ynLWcVQXtiwJpfCGAIEuQ9mRreOT6Tcd+sLo5dr7zx501zBrTuqGfjIZ2XJjdU8XOuXENUDzzY4Xdf5fqWXoSjaRsiQVO0SZv/KGJ5hoFBn7hekY9JK5eWl0RlbrBPza3/8BwUnVkw1oDMl80s76Ph5MtgBjQ0nAXOT+mn4y/VewNvA0OwhQjKlIgEVuSqB2LMagoORJT8zPIqef8VsGqJRSi3/bnnONiphG31g++U63--lzr5h+1EA/r97wMT--XprQxiV7MQ7MTYBzz5EicQ==
VHEGmOAai40QieN0vuKV8bMsudNN6zLg1EfNAJpzZFx9Z7r/YRbMzQO0Gt9lANPpdtq80M5ozqRd7ijQVMy8YKRvljpRQuBt8XCd2R+9TKoDKMfl46KOwGuz6qJk4gllbnlGN+rAU9Mvt/k5i1hfYxe0yMjs8LsgnxsJOYzj+4TpPKahsCJu12zDdKBlFz2/r8TF7HaomhiZuqUSxpGMBAl0CD8CaCdQq77U/hxn7Labf8op2xCdlNRMeJIj6cKl56Uw/q612ONrniJzu+34RQ41OzBkVnkCUx9YxxbGVnqH0lSVlJz8UkIg1AiiI3s9k9NaX67G9Ru5O091Wj4zKZgYCHaBY1THRfOunyM1/nxaCxCrmqsmD4KiXQI67Sa/2dgJvy4Q8Gb6mxvU5kN6kbuY4miOWN8/4RZ03eraLksQtJJVRjVFmjeWm6N5DINYrwaS3zzA94yWC5uUGpOvz2faKGEeJi9BmrhO--vW2CVZxqYeQDtCtY--cH0W96g70Jr9pvOCL4AaEg==
2 changes: 1 addition & 1 deletion config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ development:
# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost
# host: localhost

# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
Expand Down
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Rails.application.routes.draw do
match "/404", to: "errors#not_found", via: :all
match "/500", to: "errors#internal_server_error", via: :all

resources :stress, only: %i[create]

scope "(:locale)", locale: /en|es/ do
resources :validators, only: %i[new show create], param: :content_hash
devise_for :users
Expand Down
9 changes: 7 additions & 2 deletions lib/tasks/generate.rake
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ namespace :generate do

reports = args[:total]
puts "Generating #{reports} reports..."
Report.delete_by(student:, course:)
date = Generate.date
reports.times do |i|
content = Base64.encode64(Generate.create_pdf)
FactoryBot.create(:report,
course:,
student:,
content:,
date: Time.zone.today + i.day,
date: date + i.day,
content_hash: Digest::SHA256.hexdigest(content)
)
end
Expand All @@ -29,6 +29,11 @@ class Generate
TEST_NAME = "STRESS".freeze
ACTION_CONTROLLER = ActionController::Base.new

def self.date
report = Report.where(student:, course:).order(:date).last
report.exist? ? report.date + 1.day : Time.zone.at(0)
end

def self.parse_args
options = { total: 1_000 }
opts = OptionParser.new
Expand Down
35 changes: 20 additions & 15 deletions lib/tasks/stress.rake
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,36 @@ FakeReport = Struct.new(:id, :content_hash)

namespace :stress do
desc "Stress apiroom with lots of data"
task :apiroom, %i[rps time file] => :environment do
task :apiroom, %i[rps time fake file] => :environment do
args = Stress.parse_args

rps = args[:rps].to_i
puts "Using rps: #{rps}"
time = args[:time].to_i
puts "Using time: #{time}"

if args[:file]
file = Stress.ensure_file
puts "Using filename: #{file}"
end
file = Stress.ensure_file(args[:file])
puts "Using file: #{file}(.html/.json)"

puts "Stressing apiroom..."
simple_test = GasLoadTestCustom.new({ client: rps * time, time: })
if args[:fake]
simple_test.run(output: args[:file], file_name: file) do
puts "With fake data"
simple_test.run(output: true, file_name: "#{file}.html") do
Stress.send_report(FakeReport.new({
content_hash: Digest::SHA256.hexdigest(SecureRandom.base64(24)),
id: SecureRandom.uuid
}))
end
else
puts "With real data"
reports = Stress.find_reports(rps * time).to_a
if reports.size < rps * time
puts "Insuficient reports: #{reports.size} created, #{rps * time} needed"
exit
end
Stress.remove_transaction_id
report_transaction = {}
simple_test.run(output: args[:file], file_name: file) do |i|
simple_test.run(output: true, file_name: "#{file}.html") do |i|
report = reports[i]
response = Stress.send_report(report)
report_transaction[report.id] = response["transactionId"]
Expand All @@ -43,8 +42,9 @@ namespace :stress do
end
end

puts JSON.pretty_generate(Stress.data_results(simple_test)) unless args[:file]
Stress.fix_result_table(file) if args[:file]
Stress.fix_result_table("#{file}.html")

Stress.save_json(simple_test, "#{file}.json")
exit
end
end
Expand Down Expand Up @@ -72,15 +72,15 @@ class Stress
end

def self.parse_args
options = { rps: 100, time: 10, file: false, fake: false }
options = { rps: 100, time: 10, file: "result", fake: false }
opts = OptionParser.new
opts.banner = "Usage: stress:apiroom [options]"
opts.on("-r ARG", "--rps ARG", Integer, "Request per second") { |v| options[:rps] = v }
opts.on("-t ARG", "--time ARG", Integer, "Total time to stress API") do |v|
options[:time] = v
end
opts.on("-f", "--file [FLAG]", TrueClass, "Store in file or not") do |v|
options[:file] = v.nil? ? true : v
opts.on("-f", "--file FILE", String, "File name without extension to store data") do |v|
options[:file] = v
end
opts.on("--fake [FLAG]", TrueClass, "Use fake data or not") do |v|
options[:fake] = v.nil? ? true : v
Expand All @@ -94,6 +94,11 @@ class Stress
options
end

def self.save_json(simple_test, file_name)
json = JSON.pretty_generate(Stress.data_results(simple_test))
File.open(file_name, "w") { |file| file.puts json }
end

def self.send_report(report)
body = { data: { dataToStore: report.content_hash, reportID: report.id.to_s }, keys: {} }
HTTParty.post(
Expand All @@ -105,11 +110,11 @@ class Stress
)
end

def self.ensure_file
def self.ensure_file(file)
path = "#{Rails.root}/results"
Dir.mkdir(path) unless File.exist?(path)

path << "/result_#{Time.now.to_i}.html"
path << "/#{file}"
end

def self.data_results(test)
Expand Down