Initial commit

This commit is contained in:
Yaser
2026-08-13 19:50:53 +03:30
commit 38084458fe
879 changed files with 95198 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
# frozen_string_literal: true
# TODO: Add Bench::Error and better errors processing.
# TODO: Add Bench::Report and extract all metrics to it.
module Bench
module Matching
class AMQP
# include Helpers
include ::Bench::Helpers
def initialize(config)
@config = config
@rmq_http_client = RabbitMQHTTP.default_client
@injector = Injectors.initialize_injector(@config[:orders])
@currencies = Currency.where(id: @config[:currencies].split(',').map(&:squish).reject(&:blank?))
# TODO: Print errors in the end of benchmark and include them into report.
@errors = []
end
def run_fee!
# TODO: Check if Matching daemon is running before start (use queue_info[:consumers]).
Kernel.puts "Creating members ..."
if Member.count == 0
@members = Factories.create_list(:member, @config[:traders])
else
@members = Member.all
end
Kernel.puts "Depositing funds ..."
@members.map(&method(:become_billionaire))
Kernel.puts "Generating orders by injector and saving them in db..."
# TODO: Add orders generation progress bar.
@config[:round].times do
@injector.generate!(@members)
@orders_number = @injector.size
Kernel.puts "Publishing messages to RabbitMQ..."
@matching_started_at = @publish_started_at = Time.now
# TODO: Add orders publishing progress bar.
publish_messages
end
@publish_finished_at = Time.now
Kernel.puts "Messages are published to RabbitMQ."
Kernel.puts "Waiting for order processing by matching daemon..."
wait_for_matching
@matching_finished_at = Time.now
end
def run!
# TODO: Check if Matching daemon is running before start (use queue_info[:consumers]).
Kernel.puts "Creating members ..."
if Member.count == 0
@members = Factories.create_list(:member, @config[:traders])
else
@members = Member.all
end
Kernel.puts "Depositing funds ..."
@members.map(&method(:become_billionaire))
Kernel.puts "Generating orders by injector and saving them in db..."
# TODO: Add orders generation progress bar.
@injector.generate!(@members)
@orders_number = @injector.size
Kernel.puts "Publishing messages to RabbitMQ..."
@matching_started_at = @publish_started_at = Time.now
# TODO: Add orders publishing progress bar.
publish_messages
@publish_finished_at = Time.now
Kernel.puts "Messages are published to RabbitMQ."
Kernel.puts "Waiting for order processing by matching daemon..."
wait_for_matching
@matching_finished_at = Time.now
end
def publish_messages
Array.new(@config[:threads]) do
Thread.new do
loop do
order = @injector.pop
break unless order
p Kernel.puts "user number #{order.member_id} and order number is #{order.id} maker fee = #{order.maker_fee} submiited order level is #{order.member.group} and trades = #{order.member.trades.last_month.inject(0){ |sum, x| sum + x.rls }}"
# ::AMQP::Queue.enqueue(:matching, action: 'submit', order: order.to_matching_attributes)
::AMQP::Queue.enqueue(:order_processor,
{ action: 'submit', order: order.to_matching_attributes },
{ persistent: false })
rescue StandardError => e
Kernel.puts e
@errors << e
end
end
end.map(&:join)
end
# TODO: Find better solution for getting message number in queue.
# E.g there is rabbitmqctl list_queues.
def wait_for_matching
last_log_time = Time.at(0)
queue_status_file = File.open(queue_status_file_path('matching'), 'a')
loop do
queue_status = matching_queue_status
break if queue_status[:messages].zero? &&
queue_status[:idle_since].present? &&
Time.parse("#{queue_status[:idle_since]} UTC") >= @publish_started_at
if last_log_time + 5 < Time.now
queue_status_file.puts(YAML.dump([queue_status.merge(timestamp: Time.now.iso8601).deep_stringify_keys]))
last_log_time = Time.now
end
sleep 0.5
end
end
# TODO: Add more useful metrics to result.
def result
@result ||=
begin
publish_ops = @orders_number / (@publish_finished_at - @publish_started_at)
matching_ops = @orders_number / (@matching_finished_at - @publish_started_at)
# TODO: Deal with calling iso8601(6) everywhere.
{ config: @config,
submit_publish: {
started_at: @publish_started_at.iso8601(6),
finished_at: @publish_started_at.iso8601(6),
operations: @orders_number,
ops: publish_ops
},
matching: {
finished_at: @matching_finished_at.iso8601(6),
operations: @orders_number,
ops: matching_ops,
started_at: @matching_started_at.iso8601(6)
}
}
end
end
def save_report
report_path = Rails.root.join(@config[:report_path])
FileUtils.mkpath(report_path)
report_name = "#{self.class.parent.name.demodulize.downcase}-"\
"#{self.class.name.humanize.demodulize}-#{@config[:orders][:injector]}-"\
"#{@config[:orders][:number]}-#{@publish_started_at.iso8601}.yml"
File.open(report_path.join(report_name), 'w') do |f|
f.puts YAML.dump(result.deep_stringify_keys)
end
end
private
# TODO: Use get queue by name.
# TODO: Use Faraday instead of RabbitMQ::HTTP::Client.
def matching_queue_status
@rmq_http_client.list_queues.find { |q| q[:name] == ::AMQP::Config.binding_queue(:matching).first }
end
def queue_status_file_path(name)
log_path = Rails.root.join(@config[:log_path])
FileUtils.mkpath(log_path)
log_path.join("#{name}-#{@publish_started_at.iso8601}.yml")
end
end
end
end