mirror of
https://gitea.invidious.io/iv-org/shard-crystal-db.git
synced 2024-08-15 00:53:32 +00:00
resource pool implementation with
* max_pool_size * initial_pool_size * max_idle_pool_size * checkout_timeout configuration options
This commit is contained in:
parent
8a913d1ef2
commit
421996b952
4 changed files with 285 additions and 2 deletions
|
@ -119,6 +119,7 @@ module DB
|
|||
end
|
||||
end
|
||||
|
||||
require "./db/pool"
|
||||
require "./db/query_methods"
|
||||
require "./db/disposable"
|
||||
require "./db/database"
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
module DB
|
||||
|
||||
class Error < Exception
|
||||
end
|
||||
|
||||
class MappingException < Exception
|
||||
end
|
||||
|
||||
|
||||
class PoolTimeout < Error
|
||||
end
|
||||
end
|
||||
|
|
115
src/db/pool.cr
Normal file
115
src/db/pool.cr
Normal file
|
@ -0,0 +1,115 @@
|
|||
module DB
|
||||
class Pool(T)
|
||||
@initial_pool_size : Int32
|
||||
# maximum amount of objects in the pool. Either available or in use.
|
||||
@max_pool_size : Int32
|
||||
@available = Set(T).new
|
||||
@total = [] of T
|
||||
@checkout_timeout : Float64
|
||||
|
||||
def initialize(@factory : Proc(T), @initial_pool_size = 1, @max_pool_size = 1, @max_idle_pool_size = 1, @checkout_timeout = 5.0)
|
||||
@initial_pool_size.times { build_resource }
|
||||
|
||||
@availability_channel = Channel(Nil).new
|
||||
@waiting_resource = 0
|
||||
end
|
||||
|
||||
# close all resources in the pool
|
||||
def close : Nil
|
||||
@total.each &.close
|
||||
end
|
||||
|
||||
def checkout : T
|
||||
resource = if @available.empty?
|
||||
if can_increase_pool
|
||||
build_resource
|
||||
else
|
||||
wait_for_available
|
||||
pick_available
|
||||
end
|
||||
else
|
||||
pick_available
|
||||
end
|
||||
|
||||
@available.delete resource
|
||||
resource
|
||||
end
|
||||
|
||||
def release(resource : T) : Nil
|
||||
if can_increase_idle_pool
|
||||
@available << resource
|
||||
@availability_channel.send nil if @waiting_resource > 0
|
||||
else
|
||||
resource.close
|
||||
@total.delete(resource)
|
||||
end
|
||||
end
|
||||
|
||||
private def build_resource : T
|
||||
resource = @factory.call
|
||||
@total << resource
|
||||
@available << resource
|
||||
resource
|
||||
end
|
||||
|
||||
private def can_increase_pool
|
||||
@total.size < @max_pool_size
|
||||
end
|
||||
|
||||
private def can_increase_idle_pool
|
||||
@available.size < @max_idle_pool_size
|
||||
end
|
||||
|
||||
private def pick_available
|
||||
@available.first
|
||||
end
|
||||
|
||||
private def wait_for_available
|
||||
timeout = TimeoutHelper.new(@checkout_timeout.to_f64, ->{ @availability_channel.send nil })
|
||||
@waiting_resource += 1
|
||||
|
||||
timeout.start
|
||||
# if there are no available resources, sleep until one is available
|
||||
@availability_channel.receive
|
||||
timeout.raise_if_reached
|
||||
|
||||
# double check there is something available to be checkedout
|
||||
while @available.empty?
|
||||
@availability_channel.receive
|
||||
timeout.raise_if_reached
|
||||
end
|
||||
|
||||
timeout.cancel
|
||||
@waiting_resource -= 1
|
||||
end
|
||||
|
||||
class TimeoutHelper
|
||||
def initialize(@timeout : Float64, @tick : Proc(Nil))
|
||||
@abort_timeout = false
|
||||
@should_timeout = false
|
||||
end
|
||||
|
||||
def start
|
||||
spawn do
|
||||
sleep @timeout
|
||||
unless @abort_timeout
|
||||
@should_timeout = true
|
||||
@tick.call
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cancel
|
||||
@abort_timeout = true
|
||||
end
|
||||
|
||||
def timeout_reached?
|
||||
@should_timeout
|
||||
end
|
||||
|
||||
def raise_if_reached
|
||||
raise DB::PoolTimeout.new if timeout_reached?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue