umbrellio-sequel-plugins 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7a801a66fb801ece2e3db9f48a042d34f9a5f857817cd70103b730bb5025d0fd
|
|
4
|
+
data.tar.gz: e90f785e58bf727159b83bbb0387351254279412fc2e3e7bbd41f1c33ef4767c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 949c5b6918b91001653bda2706900c9fec8eba2449425e783ba0a593e6bf938443a5723687e2c58cd887700ebeb8167eac59088105e2d12bc2668ca777246aa5
|
|
7
|
+
data.tar.gz: 2635f3b88224877118eb0ffe706e49d32753ab017309b643b661ea9c7e56ad667e9586c4246aa76a7b5d6c8ee49b4a1c9f41768a9e1acef3c96dc9a732ef8de2
|
data/Gemfile.lock
CHANGED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "concurrent"
|
|
4
|
+
|
|
5
|
+
module Sequel
|
|
6
|
+
# https://github.com/jeremyevans/sequel/blob/master/lib/sequel/extensions/async_thread_pool.rb
|
|
7
|
+
module Database::ConcurrentThreadPool
|
|
8
|
+
# Base proxy: delegates all method calls to the resolved async value.
|
|
9
|
+
class BaseProxy < BasicObject
|
|
10
|
+
def method_missing(...)
|
|
11
|
+
__value.public_send(...)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def respond_to_missing?(*args)
|
|
15
|
+
__value.respond_to?(*args)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
[:!, :==, :!=, :instance_eval, :instance_exec].each do |method|
|
|
19
|
+
define_method(method) do |*args, &block|
|
|
20
|
+
__value.public_send(method, *args, &block)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Default proxy: schedules block via Concurrent::Future, blocks on first access.
|
|
26
|
+
class Proxy < BaseProxy
|
|
27
|
+
def initialize(executor, &block)
|
|
28
|
+
super()
|
|
29
|
+
|
|
30
|
+
@future = Concurrent::Promises.future_on(executor, &block)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def __value
|
|
34
|
+
@future.value!
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Preemptable proxy: calling thread runs the block if the pool hasn't started it yet.
|
|
39
|
+
class PreemptableProxy < BaseProxy
|
|
40
|
+
def initialize(executor, &block)
|
|
41
|
+
super()
|
|
42
|
+
|
|
43
|
+
@mutex = Mutex.new
|
|
44
|
+
@block = block
|
|
45
|
+
@done = false
|
|
46
|
+
@result = nil
|
|
47
|
+
@error = nil
|
|
48
|
+
|
|
49
|
+
executor.post { __run }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def __value
|
|
53
|
+
error, result = @mutex.synchronize do
|
|
54
|
+
__execute unless @done
|
|
55
|
+
[@error, @result]
|
|
56
|
+
end
|
|
57
|
+
::Kernel.raise error if error
|
|
58
|
+
result
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def __run
|
|
64
|
+
@mutex.synchronize { __execute unless @done }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def __execute
|
|
68
|
+
@result = @block.call
|
|
69
|
+
rescue StandardError => error
|
|
70
|
+
@error = error
|
|
71
|
+
ensure
|
|
72
|
+
@done = true
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
module DatabaseMethods
|
|
77
|
+
class << self
|
|
78
|
+
def make_finalizer(executor) = proc { executor.shutdown }
|
|
79
|
+
|
|
80
|
+
def extended(db)
|
|
81
|
+
db.instance_exec do
|
|
82
|
+
case pool.pool_type
|
|
83
|
+
when :single, :sharded_single
|
|
84
|
+
raise Error, "cannot load concurrent_thread_pool extension " \
|
|
85
|
+
"if using single or sharded_single connection pool"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
executor, owned = choose_executor(opts)
|
|
89
|
+
proxy_klass =
|
|
90
|
+
typecast_value_boolean(opts[:preempt_async_thread]) ? PreemptableProxy : Proxy
|
|
91
|
+
|
|
92
|
+
define_singleton_method(:async_job_class) { proxy_klass }
|
|
93
|
+
define_singleton_method(:async_thread_executor) { executor }
|
|
94
|
+
|
|
95
|
+
finalizer =
|
|
96
|
+
Sequel::Database::ConcurrentThreadPool::DatabaseMethods.make_finalizer(executor)
|
|
97
|
+
ObjectSpace.define_finalizer(db, finalizer) if owned
|
|
98
|
+
|
|
99
|
+
extend_datasets(DatasetMethods)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def choose_executor(opts)
|
|
107
|
+
if opts[:async_thread_executor]
|
|
108
|
+
[opts[:async_thread_executor], false]
|
|
109
|
+
else
|
|
110
|
+
num = opts[:num_async_threads] ? typecast_value_integer(opts[:num_async_threads]) :
|
|
111
|
+
Integer(opts[:max_connections] || 4)
|
|
112
|
+
raise Error, "must have positive number for num_async_threads" if num <= 0
|
|
113
|
+
[Concurrent::ThreadPoolExecutor.new(
|
|
114
|
+
min_threads: num,
|
|
115
|
+
max_threads: num,
|
|
116
|
+
max_queue: 0,
|
|
117
|
+
fallback_policy: :abort,
|
|
118
|
+
), true]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def async_run(&block)
|
|
123
|
+
async_job_class.new(async_thread_executor, &block)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
ASYNC_METHODS = ((
|
|
128
|
+
[:all?, :any?, :drop, :entries, :grep_v, :include?, :inject, :member?, :minmax,
|
|
129
|
+
:none?, :one?, :reduce, :sort, :take, :tally, :to_a, :to_h, :uniq, :zip] &
|
|
130
|
+
Enumerable.instance_methods
|
|
131
|
+
) + (Dataset::ACTION_METHODS - [:map, :paged_each])).freeze
|
|
132
|
+
|
|
133
|
+
ASYNC_BLOCK_METHODS = ((
|
|
134
|
+
[:collect, :collect_concat, :detect, :drop_while, :each_cons, :each_entry, :each_slice,
|
|
135
|
+
:each_with_index, :each_with_object, :filter_map, :find, :find_all, :find_index,
|
|
136
|
+
:flat_map, :max_by, :min_by, :minmax_by, :partition, :reject, :reverse_each,
|
|
137
|
+
:sort_by, :take_while] & Enumerable.instance_methods
|
|
138
|
+
) + [:paged_each]).freeze
|
|
139
|
+
|
|
140
|
+
ASYNC_ARGS_OR_BLOCK_METHODS = [:map].freeze
|
|
141
|
+
|
|
142
|
+
module DatasetMethods
|
|
143
|
+
def self.define_async_method(mod, method)
|
|
144
|
+
mod.send(:define_method, method) do |*args, &block|
|
|
145
|
+
if @opts[:async]
|
|
146
|
+
ds = sync
|
|
147
|
+
db.send(:async_run) { ds.send(method, *args, &block) }
|
|
148
|
+
else
|
|
149
|
+
super(*args, &block)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.define_async_block_method(mod, method)
|
|
155
|
+
mod.send(:define_method, method) do |*args, &block|
|
|
156
|
+
if block && @opts[:async]
|
|
157
|
+
ds = sync
|
|
158
|
+
db.send(:async_run) { ds.send(method, *args, &block) }
|
|
159
|
+
else
|
|
160
|
+
super(*args, &block)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def self.define_async_args_or_block_method(mod, method)
|
|
166
|
+
mod.send(:define_method, method) do |*args, &block|
|
|
167
|
+
if (block || !args.empty?) && @opts[:async]
|
|
168
|
+
ds = sync
|
|
169
|
+
db.send(:async_run) { ds.send(method, *args, &block) }
|
|
170
|
+
else
|
|
171
|
+
super(*args, &block)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
ASYNC_METHODS.each { |m| define_async_method(self, m) }
|
|
177
|
+
ASYNC_BLOCK_METHODS.each { |m| define_async_block_method(self, m) }
|
|
178
|
+
ASYNC_ARGS_OR_BLOCK_METHODS.each { |m| define_async_args_or_block_method(self, m) }
|
|
179
|
+
|
|
180
|
+
def async
|
|
181
|
+
cached_dataset(:_async) { clone(async: true) }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def sync
|
|
185
|
+
cached_dataset(:_sync) { clone(async: false) }
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
Database.register_extension(
|
|
191
|
+
:concurrent_thread_pool, Database::ConcurrentThreadPool::DatabaseMethods
|
|
192
|
+
)
|
|
193
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module Plugins
|
|
5
|
+
# Concurrent eager loading using concurrent_thread_pool extension.
|
|
6
|
+
# Adapted from Sequel's built-in concurrent_eager_loading plugin but uses
|
|
7
|
+
# our concurrent_thread_pool extension instead of async_thread_pool.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
#
|
|
11
|
+
# DB.extension(:concurrent_thread_pool)
|
|
12
|
+
# Album.plugin :concurrent_thread_pool_eager_loading
|
|
13
|
+
# Album.eager_load_concurrently.eager(:artist, :genre, :tracks).all
|
|
14
|
+
#
|
|
15
|
+
# # Always concurrent by default:
|
|
16
|
+
# Album.plugin :concurrent_thread_pool_eager_loading, always: true
|
|
17
|
+
module ConcurrentThreadPoolEagerLoading
|
|
18
|
+
def self.configure(mod, opts = OPTS)
|
|
19
|
+
if opts.key?(:always)
|
|
20
|
+
mod.instance_variable_set(:@always_eager_load_concurrently, opts[:always])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module ClassMethods
|
|
25
|
+
Plugins.inherited_instance_variables(self, :@always_eager_load_concurrently => nil)
|
|
26
|
+
Plugins.def_dataset_methods(self, [:eager_load_concurrently, :eager_load_serially])
|
|
27
|
+
|
|
28
|
+
def always_eager_load_concurrently?
|
|
29
|
+
@always_eager_load_concurrently
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module DatasetMethods
|
|
34
|
+
def eager_load_concurrently
|
|
35
|
+
cached_dataset(:_eager_load_concurrently) do
|
|
36
|
+
clone(eager_load_concurrently: true)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def eager_load_serially
|
|
41
|
+
cached_dataset(:_eager_load_serially) do
|
|
42
|
+
clone(eager_load_concurrently: false)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def eager_load_concurrently?
|
|
49
|
+
v = @opts[:eager_load_concurrently]
|
|
50
|
+
v.nil? ? model.always_eager_load_concurrently? : v
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def perform_eager_loads(eager_load_data)
|
|
54
|
+
return super if !eager_load_concurrently? || eager_load_data.length < 2
|
|
55
|
+
|
|
56
|
+
mutex = Mutex.new
|
|
57
|
+
eager_load_data.each_value do |elo|
|
|
58
|
+
elo[:mutex] = mutex
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
super.each do |v|
|
|
62
|
+
if Sequel::Database::ConcurrentThreadPool::BaseProxy === v # rubocop:disable Style/CaseEquality
|
|
63
|
+
v.__value
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def perform_eager_load(loader, elo)
|
|
69
|
+
elo[:mutex] ? db.send(:async_run) { super } : super
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -5,7 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = "umbrellio-sequel-plugins"
|
|
8
|
-
spec.version = "0.
|
|
8
|
+
spec.version = "0.18.0"
|
|
9
9
|
spec.required_ruby_version = ">= 3.0"
|
|
10
10
|
|
|
11
11
|
spec.authors = ["Team Umbrellio"]
|
|
@@ -18,5 +18,6 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
+
spec.add_runtime_dependency "concurrent-ruby"
|
|
21
22
|
spec.add_runtime_dependency "sequel"
|
|
22
23
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: umbrellio-sequel-plugins
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Team Umbrellio
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: concurrent-ruby
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: sequel
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -43,6 +56,7 @@ files:
|
|
|
43
56
|
- bin/console
|
|
44
57
|
- bin/setup
|
|
45
58
|
- lib/clickhouse/migrator.rb
|
|
59
|
+
- lib/sequel/extensions/concurrent_thread_pool.rb
|
|
46
60
|
- lib/sequel/extensions/currency_rates.rb
|
|
47
61
|
- lib/sequel/extensions/deferrable_foreign_keys.rb
|
|
48
62
|
- lib/sequel/extensions/fibered_connection_pool.rb
|
|
@@ -54,6 +68,7 @@ files:
|
|
|
54
68
|
- lib/sequel/extensions/synchronize.rb
|
|
55
69
|
- lib/sequel/plugins/attr_encrypted.rb
|
|
56
70
|
- lib/sequel/plugins/attr_encrypted/simple_crypt.rb
|
|
71
|
+
- lib/sequel/plugins/concurrent_thread_pool_eager_loading.rb
|
|
57
72
|
- lib/sequel/plugins/duplicate.rb
|
|
58
73
|
- lib/sequel/plugins/get_column_value.rb
|
|
59
74
|
- lib/sequel/plugins/money_accessors.rb
|
|
@@ -77,7 +92,6 @@ homepage: https://github.com/umbrellio/umbrellio-sequel-plugins
|
|
|
77
92
|
licenses:
|
|
78
93
|
- MIT
|
|
79
94
|
metadata: {}
|
|
80
|
-
post_install_message:
|
|
81
95
|
rdoc_options: []
|
|
82
96
|
require_paths:
|
|
83
97
|
- lib
|
|
@@ -92,8 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
92
106
|
- !ruby/object:Gem::Version
|
|
93
107
|
version: '0'
|
|
94
108
|
requirements: []
|
|
95
|
-
rubygems_version:
|
|
96
|
-
signing_key:
|
|
109
|
+
rubygems_version: 4.0.6
|
|
97
110
|
specification_version: 4
|
|
98
111
|
summary: Sequel plugins
|
|
99
112
|
test_files: []
|