mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
refactor prepared and unprepared pool statements
This commit is contained in:
parent
0593f63dbb
commit
2cab0b37f5
6 changed files with 64 additions and 85 deletions
|
@ -42,9 +42,9 @@ describe DB::Database do
|
|||
|
||||
it "should allow creation of more statements than pool connections" do
|
||||
DB.open "dummy://localhost:1027?initial_pool_size=1&max_pool_size=2" do |db|
|
||||
db.build("query1").should be_a(DB::PoolStatement)
|
||||
db.build("query2").should be_a(DB::PoolStatement)
|
||||
db.build("query3").should be_a(DB::PoolStatement)
|
||||
db.build("query1").should be_a(DB::PoolPreparedStatement)
|
||||
db.build("query2").should be_a(DB::PoolPreparedStatement)
|
||||
db.build("query3").should be_a(DB::PoolPreparedStatement)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -59,8 +59,7 @@ describe DB::Database do
|
|||
it "should close pool statements when closing db" do
|
||||
stmt = uninitialized DB::PoolStatement
|
||||
with_dummy do |db|
|
||||
# TODO remove cast
|
||||
stmt = db.build("query1").as(DB::PoolStatement)
|
||||
stmt = db.build("query1")
|
||||
end
|
||||
stmt.closed?.should be_true
|
||||
end
|
||||
|
@ -132,7 +131,7 @@ describe DB::Database do
|
|||
|
||||
it "should build prepared statements if true" do
|
||||
with_dummy "dummy://localhost:1027?prepared_statements=true" do |db|
|
||||
db.build("the query").should be_a(DB::PoolStatement)
|
||||
db.build("the query").should be_a(DB::PoolPreparedStatement)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ require "./db/driver"
|
|||
require "./db/connection"
|
||||
require "./db/statement"
|
||||
require "./db/pool_statement"
|
||||
require "./db/pool_prepared_statement"
|
||||
require "./db/pool_unprepared_statement"
|
||||
require "./db/result_set"
|
||||
require "./db/error"
|
||||
|
|
|
@ -31,7 +31,7 @@ module DB
|
|||
|
||||
@pool : Pool(Connection)
|
||||
@setup_connection : Connection -> Nil
|
||||
@statements_cache = StringKeyCache(PoolStatement).new
|
||||
@statements_cache = StringKeyCache(PoolPreparedStatement).new
|
||||
|
||||
# :nodoc:
|
||||
def initialize(@driver : Driver, @uri : URI)
|
||||
|
@ -64,7 +64,7 @@ module DB
|
|||
end
|
||||
|
||||
# :nodoc:
|
||||
def build(query)
|
||||
def build(query) : PoolStatement
|
||||
if prepared_statements?
|
||||
fetch_or_build_prepared_statement(query)
|
||||
else
|
||||
|
@ -79,7 +79,7 @@ module DB
|
|||
|
||||
# :nodoc:
|
||||
def build_prepared_statement(query)
|
||||
PoolStatement.new(self, query)
|
||||
PoolPreparedStatement.new(self, query)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
|
|
50
src/db/pool_prepared_statement.cr
Normal file
50
src/db/pool_prepared_statement.cr
Normal file
|
@ -0,0 +1,50 @@
|
|||
module DB
|
||||
# Represents a statement to be executed in any of the connections
|
||||
# of the pool. The statement is not be executed in a prepared fashion.
|
||||
# The execution of the statement is retried according to the pool configuration.
|
||||
#
|
||||
# See `PoolStatement`
|
||||
class PoolPreparedStatement < PoolStatement
|
||||
# connections where the statement was prepared
|
||||
@connections = Set(WeakRef(Connection)).new
|
||||
|
||||
def initialize(db : Database, query : String)
|
||||
super
|
||||
# Prepares a statement on some connection
|
||||
# otherwise the preparation is delayed until the first execution.
|
||||
# After the first initialization the connection must be released
|
||||
# it will be checked out when executing it.
|
||||
statement_with_retry &.release_connection
|
||||
# TODO use a round-robin selection in the pool so multiple sequentially
|
||||
# initialized statements are assigned to different connections.
|
||||
end
|
||||
|
||||
protected def do_close
|
||||
# TODO close all statements on all connections.
|
||||
# currently statements are closed when the connection is closed.
|
||||
|
||||
# WHAT-IF the connection is busy? Should each statement be able to
|
||||
# deallocate itself when the connection is free.
|
||||
@connections.clear
|
||||
end
|
||||
|
||||
# builds a statement over a real connection
|
||||
# the conneciton is registered in `@connections`
|
||||
private def build_statement
|
||||
clean_connections
|
||||
conn, existing = @db.checkout_some(@connections)
|
||||
@connections << WeakRef.new(conn) unless existing
|
||||
conn.build(@query)
|
||||
end
|
||||
|
||||
private def clean_connections
|
||||
# remove disposed or closed connections
|
||||
@connections.each do |ref|
|
||||
conn = ref.target
|
||||
if !conn || conn.closed?
|
||||
@connections.delete ref
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,29 +3,10 @@ module DB
|
|||
# a statement from the DB needs to be able to represent a statement in any
|
||||
# of the connections of the pool. Otherwise the user will need to deal with
|
||||
# actual connections in some point.
|
||||
class PoolStatement
|
||||
abstract class PoolStatement
|
||||
include StatementMethods
|
||||
|
||||
# connections where the statement was prepared
|
||||
@connections = Set(WeakRef(Connection)).new
|
||||
|
||||
def initialize(@db : Database, @query : String)
|
||||
# Prepares a statement on some connection
|
||||
# otherwise the preparation is delayed until the first execution.
|
||||
# After the first initialization the connection must be released
|
||||
# it will be checked out when executing it.
|
||||
statement_with_retry &.release_connection
|
||||
# TODO use a round-robin selection in the pool so multiple sequentially
|
||||
# initialized statements are assigned to different connections.
|
||||
end
|
||||
|
||||
protected def do_close
|
||||
# TODO close all statements on all connections.
|
||||
# currently statements are closed when the connection is closed.
|
||||
|
||||
# WHAT-IF the connection is busy? Should each statement be able to
|
||||
# deallocate itself when the connection is free.
|
||||
@connections.clear
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
|
@ -60,22 +41,7 @@ module DB
|
|||
|
||||
# builds a statement over a real connection
|
||||
# the conneciton is registered in `@connections`
|
||||
private def build_statement
|
||||
clean_connections
|
||||
conn, existing = @db.checkout_some(@connections)
|
||||
@connections << WeakRef.new(conn) unless existing
|
||||
conn.build(@query)
|
||||
end
|
||||
|
||||
private def clean_connections
|
||||
# remove disposed or closed connections
|
||||
@connections.each do |ref|
|
||||
conn = ref.target
|
||||
if !conn || conn.closed?
|
||||
@connections.delete ref
|
||||
end
|
||||
end
|
||||
end
|
||||
private abstract def build_statement : Statement
|
||||
|
||||
private def statement_with_retry
|
||||
@db.retry do
|
||||
|
|
|
@ -4,55 +4,18 @@ module DB
|
|||
# The execution of the statement is retried according to the pool configuration.
|
||||
#
|
||||
# See `PoolStatement`
|
||||
class PoolUnpreparedStatement
|
||||
include StatementMethods
|
||||
|
||||
def initialize(@db : Database, @query : String)
|
||||
class PoolUnpreparedStatement < PoolStatement
|
||||
def initialize(db : Database, query : String)
|
||||
super
|
||||
end
|
||||
|
||||
protected def do_close
|
||||
# unprepared statements do not need to be release in each connection
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
def exec : ExecResult
|
||||
statement_with_retry &.exec
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
def exec(*args) : ExecResult
|
||||
statement_with_retry &.exec(*args)
|
||||
end
|
||||
|
||||
# See `QueryMethods#exec`
|
||||
def exec(args : Array) : ExecResult
|
||||
statement_with_retry &.exec(args)
|
||||
end
|
||||
|
||||
# See `QueryMethods#query`
|
||||
def query : ResultSet
|
||||
statement_with_retry &.query
|
||||
end
|
||||
|
||||
# See `QueryMethods#query`
|
||||
def query(*args) : ResultSet
|
||||
statement_with_retry &.query(*args)
|
||||
end
|
||||
|
||||
# See `QueryMethods#query`
|
||||
def query(args : Array) : ResultSet
|
||||
statement_with_retry &.query(args)
|
||||
end
|
||||
|
||||
# builds a statement over a real connection
|
||||
private def build_statement
|
||||
@db.pool.checkout.unprepared.build(@query)
|
||||
end
|
||||
|
||||
private def statement_with_retry
|
||||
@db.retry do
|
||||
return yield build_statement
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue