mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
Simplifications and performance improvements (#200)
* Add pool_concurrency_test manual spec Add MT connection count without Mutex * Drop checkout_some, simpler pool_prepared statement * Make pool statement a struct * Drop StringKeyCache mutex The StringKeyCache is now only used inside a connection. It's assumed that connections are not used concurrently with multiple queries. * Drop do_close in pool statements * Add specs and update comment * Fix typo
This commit is contained in:
parent
06df272740
commit
c106775ea9
10 changed files with 139 additions and 149 deletions
|
@ -57,14 +57,6 @@ describe DB::Database do
|
|||
end
|
||||
end
|
||||
|
||||
it "should close pool statements when closing db" do
|
||||
stmt = uninitialized DB::PoolStatement
|
||||
with_dummy do |db|
|
||||
stmt = db.build("query1")
|
||||
end
|
||||
stmt.closed?.should be_true
|
||||
end
|
||||
|
||||
it "should not reconnect if connection is lost and retry_attempts=0" do
|
||||
DummyDriver::DummyConnection.clear_connections
|
||||
DB.open "dummy://localhost:1027?initial_pool_size=1&max_pool_size=1&retry_attempts=0" do |db|
|
||||
|
@ -187,6 +179,25 @@ describe DB::Database do
|
|||
end
|
||||
end
|
||||
|
||||
it "should not checkout multiple connections if there is a statement error" do
|
||||
with_dummy "dummy://localhost:1027?initial_pool_size=1&max_pool_size=10&retry_attempts=10" do |db|
|
||||
expect_raises DB::Error do
|
||||
db.exec("syntax error")
|
||||
end
|
||||
DummyDriver::DummyConnection.connections.size.should eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
it "should attempt all retries if connection is lost" do
|
||||
with_dummy "dummy://localhost:1027?initial_pool_size=1&max_pool_size=1&retry_attempts=10" do |db|
|
||||
expect_raises DB::PoolRetryAttemptsExceeded do
|
||||
db.exec("raise ConnectionLost")
|
||||
end
|
||||
# 1 initial + 10 retries
|
||||
DummyDriver::DummyConnection.connections.size.should eq(11)
|
||||
end
|
||||
end
|
||||
|
||||
describe "prepared_statements connection option" do
|
||||
it "defaults to true" do
|
||||
with_dummy "dummy://localhost:1027" do |db|
|
||||
|
@ -239,24 +250,6 @@ describe DB::Database do
|
|||
end
|
||||
end
|
||||
|
||||
describe "prepared_statements_cache connection option" do
|
||||
it "should reuse prepared statements if true" do
|
||||
with_dummy "dummy://localhost:1027?prepared_statements=true&prepared_statements_cache=true" do |db|
|
||||
stmt1 = db.build("the query")
|
||||
stmt2 = db.build("the query")
|
||||
stmt1.object_id.should eq(stmt2.object_id)
|
||||
end
|
||||
end
|
||||
|
||||
it "should not reuse prepared statements if false" do
|
||||
with_dummy "dummy://localhost:1027?prepared_statements=true&prepared_statements_cache=false" do |db|
|
||||
stmt1 = db.build("the query")
|
||||
stmt2 = db.build("the query")
|
||||
stmt1.object_id.should_not eq(stmt2.object_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "unprepared statements in pool" do
|
||||
it "creating statements should not create new connections" do
|
||||
with_dummy "dummy://localhost:1027?initial_pool_size=1" do |db|
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
require "spec"
|
||||
require "../src/db"
|
||||
|
||||
class DummyDriver < DB::Driver
|
||||
|
@ -17,20 +16,37 @@ class DummyDriver < DB::Driver
|
|||
end
|
||||
|
||||
class DummyConnection < DB::Connection
|
||||
@@connections = [] of DummyConnection
|
||||
@@connections_count = Atomic(Int32).new(0)
|
||||
|
||||
def initialize(options : DB::Connection::Options)
|
||||
super(options)
|
||||
Fiber.yield
|
||||
@@connections_count.add(1)
|
||||
@connected = true
|
||||
@@connections ||= [] of DummyConnection
|
||||
@@connections.not_nil! << self
|
||||
{% unless flag?(:preview_mt) %}
|
||||
# @@connections is only used in single-threaded mode in specs
|
||||
# for benchmarks we want to avoid the overhead of synchronizing this array
|
||||
@@connections << self
|
||||
{% end %}
|
||||
end
|
||||
|
||||
def self.connections_count
|
||||
@@connections_count.get
|
||||
end
|
||||
|
||||
def self.connections
|
||||
@@connections.not_nil!
|
||||
{% if flag?(:preview_mt) %}
|
||||
raise "DummyConnection.connections is only available in single-threaded mode"
|
||||
{% end %}
|
||||
@@connections
|
||||
end
|
||||
|
||||
def self.clear_connections
|
||||
@@connections.try &.clear
|
||||
{% if flag?(:preview_mt) %}
|
||||
raise "DummyConnection.clear_connections is only available in single-threaded mode"
|
||||
{% end %}
|
||||
@@connections.clear
|
||||
end
|
||||
|
||||
def build_prepared_statement(query) : DB::Statement
|
||||
|
@ -117,17 +133,31 @@ class DummyDriver < DB::Driver
|
|||
end
|
||||
|
||||
class DummyStatement < DB::Statement
|
||||
@@statements_count = Atomic(Int32).new(0)
|
||||
@@statements_exec_count = Atomic(Int32).new(0)
|
||||
property params
|
||||
|
||||
def initialize(connection, command : String, @prepared : Bool)
|
||||
@params = Hash(Int32 | String, DB::Any | Array(DB::Any)).new
|
||||
super(connection, command)
|
||||
@@statements_count.add(1)
|
||||
raise DB::Error.new(command) if command == "syntax error"
|
||||
raise DB::ConnectionLost.new(connection) if command == "raise ConnectionLost"
|
||||
end
|
||||
|
||||
def self.statements_count
|
||||
@@statements_count.get
|
||||
end
|
||||
|
||||
def self.statements_exec_count
|
||||
@@statements_exec_count.get
|
||||
end
|
||||
|
||||
protected def perform_query(args : Enumerable) : DB::ResultSet
|
||||
assert_not_closed!
|
||||
|
||||
@@statements_exec_count.add(1)
|
||||
|
||||
Fiber.yield
|
||||
@connection.as(DummyConnection).check
|
||||
set_params args
|
||||
|
@ -137,6 +167,8 @@ class DummyDriver < DB::Driver
|
|||
protected def perform_exec(args : Enumerable) : DB::ExecResult
|
||||
assert_not_closed!
|
||||
|
||||
@@statements_exec_count.add(1)
|
||||
|
||||
@connection.as(DummyConnection).check
|
||||
set_params args
|
||||
raise DB::Error.new("forced exception due to query") if command == "raise"
|
||||
|
|
68
spec/manual/pool_concurrency_test.cr
Normal file
68
spec/manual/pool_concurrency_test.cr
Normal file
|
@ -0,0 +1,68 @@
|
|||
# This file is to be executed as:
|
||||
#
|
||||
# % crystal run --release [-Dpreview_mt] ./spec/manual/pool_concurrency_test.cr -- --options="max_pool_size=5" --duration=30 --concurrency=4
|
||||
#
|
||||
#
|
||||
|
||||
require "option_parser"
|
||||
require "../dummy_driver"
|
||||
require "../../src/db"
|
||||
|
||||
options = ""
|
||||
duration = 3
|
||||
concurrency = 4
|
||||
|
||||
OptionParser.parse do |parser|
|
||||
parser.banner = "Usage: pool_concurrency_test [arguments]"
|
||||
parser.on("-o", "--options=VALUE", "Connection string options") { |v| options = v }
|
||||
parser.on("-d", "--duration=SECONDS", "Specifies the duration in seconds") { |v| duration = v.to_i }
|
||||
parser.on("-c", "--concurrency=VALUE", "Specifies the concurrent requests to perform") { |v| concurrency = v.to_i }
|
||||
parser.on("-h", "--help", "Show this help") do
|
||||
puts parser
|
||||
exit
|
||||
end
|
||||
parser.invalid_option do |flag|
|
||||
STDERR.puts "ERROR: #{flag} is not a valid option."
|
||||
STDERR.puts parser
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
multi_threaded = {% if flag?(:preview_mt) %} ENV["CRYSTAL_WORKERS"]?.try(&.to_i?) || 4 {% else %} false {% end %}
|
||||
release = {% if flag?(:release) %} true {% else %} false {% end %}
|
||||
|
||||
if !release
|
||||
puts "WARNING: This should be run in release mode."
|
||||
end
|
||||
|
||||
db = DB.open "dummy://host?#{options}"
|
||||
|
||||
start_time = Time.monotonic
|
||||
|
||||
puts "Starting test for #{duration} seconds..."
|
||||
|
||||
concurrency.times do
|
||||
spawn do
|
||||
loop do
|
||||
db.scalar "1"
|
||||
Fiber.yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sleep duration.seconds
|
||||
|
||||
end_time = Time.monotonic
|
||||
|
||||
puts " Options : #{options}"
|
||||
puts " Duration (sec) : #{duration} (actual #{end_time - start_time})"
|
||||
puts " Concurrency : #{concurrency}"
|
||||
puts " Multi Threaded : #{multi_threaded ? "Yes (#{multi_threaded})" : "No"}"
|
||||
puts "Total Connections : #{DummyDriver::DummyConnection.connections_count}"
|
||||
puts " Total Statements : #{DummyDriver::DummyStatement.statements_count}"
|
||||
puts " Total Queries : #{DummyDriver::DummyStatement.statements_exec_count}"
|
||||
puts " Throughput (q/s) : #{DummyDriver::DummyStatement.statements_exec_count / duration}"
|
||||
|
||||
if !release
|
||||
puts "WARNING: This should be run in release mode."
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue