sequel 5.45.0 → 5.77.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 +4 -4
- data/CHANGELOG +434 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_array_associations.rb +46 -34
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +41 -11
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +109 -19
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'threaded'
|
4
4
|
|
5
|
-
# The slowest and most advanced connection, dealing with both multi-threaded
|
5
|
+
# The slowest and most advanced connection pool, dealing with both multi-threaded
|
6
6
|
# access and configurations with multiple shards/servers.
|
7
7
|
#
|
8
8
|
# In addition, this pool subclass also handles scheduling in-use connections
|
@@ -22,6 +22,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
22
22
|
@connections_to_disconnect = []
|
23
23
|
@servers = opts.fetch(:servers_hash, Hash.new(:default))
|
24
24
|
remove_instance_variable(:@waiter)
|
25
|
+
remove_instance_variable(:@allocated)
|
26
|
+
@allocated = {}
|
25
27
|
@waiters = {}
|
26
28
|
|
27
29
|
add_servers([:default])
|
@@ -36,7 +38,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
36
38
|
unless @servers.has_key?(server)
|
37
39
|
@servers[server] = server
|
38
40
|
@available_connections[server] = []
|
39
|
-
|
41
|
+
allocated = {}
|
42
|
+
allocated.compare_by_identity
|
43
|
+
@allocated[server] = allocated
|
40
44
|
@waiters[server] = ConditionVariable.new
|
41
45
|
end
|
42
46
|
end
|
@@ -108,7 +112,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
108
112
|
# available, creates a new connection. Passes the connection to the supplied
|
109
113
|
# block:
|
110
114
|
#
|
111
|
-
# pool.hold {|conn| conn.execute('DROP TABLE posts')}
|
115
|
+
# pool.hold(:server1) {|conn| conn.execute('DROP TABLE posts')}
|
112
116
|
#
|
113
117
|
# Pool#hold is re-entrant, meaning it can be called recursively in
|
114
118
|
# the same thread without blocking.
|
@@ -141,12 +145,13 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
141
145
|
# except that after it is used, future requests for the server will use the
|
142
146
|
# :default server instead.
|
143
147
|
def remove_servers(servers)
|
144
|
-
conns =
|
148
|
+
conns = []
|
149
|
+
raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
|
150
|
+
|
145
151
|
sync do
|
146
|
-
raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
|
147
152
|
servers.each do |server|
|
148
153
|
if @servers.include?(server)
|
149
|
-
conns
|
154
|
+
conns.concat(disconnect_server_connections(server))
|
150
155
|
@waiters.delete(server)
|
151
156
|
@available_connections.delete(server)
|
152
157
|
@allocated.delete(server)
|
@@ -155,9 +160,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
155
160
|
end
|
156
161
|
end
|
157
162
|
|
158
|
-
|
159
|
-
|
160
|
-
|
163
|
+
nil
|
164
|
+
ensure
|
165
|
+
disconnect_connections(conns)
|
161
166
|
end
|
162
167
|
|
163
168
|
# Return an array of symbols for servers in the connection pool.
|
@@ -182,7 +187,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
182
187
|
# is available. The calling code should NOT already have the mutex when
|
183
188
|
# calling this.
|
184
189
|
#
|
185
|
-
# This should return a connection
|
190
|
+
# This should return a connection if one is available within the timeout,
|
186
191
|
# or nil if a connection could not be acquired within the timeout.
|
187
192
|
def acquire(thread, server)
|
188
193
|
if conn = assign_connection(thread, server)
|
@@ -321,7 +326,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
321
326
|
# Create the maximum number of connections immediately. The calling code should
|
322
327
|
# NOT have the mutex before calling this.
|
323
328
|
def preconnect(concurrent = false)
|
324
|
-
conn_servers = @servers.keys.map!{|s| Array.new(max_size - _size(s), s)}.flatten!
|
329
|
+
conn_servers = sync{@servers.keys}.map!{|s| Array.new(max_size - _size(s), s)}.flatten!
|
325
330
|
|
326
331
|
if concurrent
|
327
332
|
conn_servers.map!{|s| Thread.new{[s, make_new(s)]}}.map!(&:value)
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
raise LoadError, "Sequel::ShardedTimedQueueConnectionPool is only available on Ruby 3.2+" unless RUBY_VERSION >= '3.2'
|
5
|
+
# :nocov:
|
6
|
+
|
7
|
+
# A connection pool allowing multi-threaded access to a sharded pool of connections,
|
8
|
+
# using a timed queue (only available in Ruby 3.2+).
|
9
|
+
class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
|
10
|
+
# The maximum number of connections this pool will create per shard.
|
11
|
+
attr_reader :max_size
|
12
|
+
|
13
|
+
# The following additional options are respected:
|
14
|
+
# :max_connections :: The maximum number of connections the connection pool
|
15
|
+
# will open (default 4)
|
16
|
+
# :pool_timeout :: The amount of seconds to wait to acquire a connection
|
17
|
+
# before raising a PoolTimeout (default 5)
|
18
|
+
# :servers :: A hash of servers to use. Keys should be symbols. If not
|
19
|
+
# present, will use a single :default server.
|
20
|
+
# :servers_hash :: The base hash to use for the servers. By default,
|
21
|
+
# Sequel uses Hash.new(:default). You can use a hash with a default proc
|
22
|
+
# that raises an error if you want to catch all cases where a nonexistent
|
23
|
+
# server is used.
|
24
|
+
def initialize(db, opts = OPTS)
|
25
|
+
super
|
26
|
+
|
27
|
+
@max_size = Integer(opts[:max_connections] || 4)
|
28
|
+
raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
|
29
|
+
@mutex = Mutex.new
|
30
|
+
@timeout = Float(opts[:pool_timeout] || 5)
|
31
|
+
|
32
|
+
@allocated = {}
|
33
|
+
@sizes = {}
|
34
|
+
@queues = {}
|
35
|
+
@servers = opts.fetch(:servers_hash, Hash.new(:default))
|
36
|
+
|
37
|
+
add_servers([:default])
|
38
|
+
add_servers(opts[:servers].keys) if opts[:servers]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Adds new servers to the connection pool. Allows for dynamic expansion of the potential replicas/shards
|
42
|
+
# at runtime. +servers+ argument should be an array of symbols.
|
43
|
+
def add_servers(servers)
|
44
|
+
sync do
|
45
|
+
servers.each do |server|
|
46
|
+
next if @servers.has_key?(server)
|
47
|
+
|
48
|
+
@servers[server] = server
|
49
|
+
@sizes[server] = 0
|
50
|
+
@queues[server] = Queue.new
|
51
|
+
(@allocated[server] = {}).compare_by_identity
|
52
|
+
end
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Yield all of the available connections, and the one currently allocated to
|
58
|
+
# this thread (if one is allocated). This will not yield connections currently
|
59
|
+
# allocated to other threads, as it is not safe to operate on them.
|
60
|
+
def all_connections
|
61
|
+
thread = Sequel.current
|
62
|
+
sync{@queues.to_a}.each do |server, queue|
|
63
|
+
if conn = owned_connection(thread, server)
|
64
|
+
yield conn
|
65
|
+
end
|
66
|
+
|
67
|
+
# Use a hash to record all connections already seen. As soon as we
|
68
|
+
# come across a connection we've already seen, we stop the loop.
|
69
|
+
conns = {}
|
70
|
+
conns.compare_by_identity
|
71
|
+
while true
|
72
|
+
conn = nil
|
73
|
+
begin
|
74
|
+
break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
|
75
|
+
conns[conn] = true
|
76
|
+
yield conn
|
77
|
+
ensure
|
78
|
+
queue.push(conn) if conn
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Removes all connections currently in the pool's queue. This method has the effect of
|
87
|
+
# disconnecting from the database, assuming that no connections are currently
|
88
|
+
# being used.
|
89
|
+
#
|
90
|
+
# Once a connection is requested using #hold, the connection pool
|
91
|
+
# creates new connections to the database.
|
92
|
+
#
|
93
|
+
# If the :server option is provided, it should be a symbol or array of symbols,
|
94
|
+
# and then the method will only disconnect connectsion from those specified shards.
|
95
|
+
def disconnect(opts=OPTS)
|
96
|
+
(opts[:server] ? Array(opts[:server]) : sync{@servers.keys}).each do |server|
|
97
|
+
raise Sequel::Error, "invalid server" unless queue = sync{@queues[server]}
|
98
|
+
while conn = queue.pop(timeout: 0)
|
99
|
+
disconnect_pool_connection(conn, server)
|
100
|
+
end
|
101
|
+
fill_queue(server)
|
102
|
+
end
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# Chooses the first available connection for the given server, or if none are
|
107
|
+
# available, creates a new connection. Passes the connection to the supplied
|
108
|
+
# block:
|
109
|
+
#
|
110
|
+
# pool.hold(:server1) {|conn| conn.execute('DROP TABLE posts')}
|
111
|
+
#
|
112
|
+
# Pool#hold is re-entrant, meaning it can be called recursively in
|
113
|
+
# the same thread without blocking.
|
114
|
+
#
|
115
|
+
# If no connection is immediately available and the pool is already using the maximum
|
116
|
+
# number of connections, Pool#hold will block until a connection
|
117
|
+
# is available or the timeout expires. If the timeout expires before a
|
118
|
+
# connection can be acquired, a Sequel::PoolTimeout is raised.
|
119
|
+
def hold(server=:default)
|
120
|
+
server = pick_server(server)
|
121
|
+
t = Sequel.current
|
122
|
+
if conn = owned_connection(t, server)
|
123
|
+
return yield(conn)
|
124
|
+
end
|
125
|
+
|
126
|
+
begin
|
127
|
+
conn = acquire(t, server)
|
128
|
+
yield conn
|
129
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
130
|
+
if disconnect_error?(e)
|
131
|
+
oconn = conn
|
132
|
+
conn = nil
|
133
|
+
disconnect_pool_connection(oconn, server) if oconn
|
134
|
+
sync{@allocated[server].delete(t)}
|
135
|
+
fill_queue(server)
|
136
|
+
end
|
137
|
+
raise
|
138
|
+
ensure
|
139
|
+
release(t, conn, server) if conn
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# The total number of connections in the pool. Using a non-existant server will return nil.
|
144
|
+
def size(server=:default)
|
145
|
+
sync{@sizes[server]}
|
146
|
+
end
|
147
|
+
|
148
|
+
# Remove servers from the connection pool. Similar to disconnecting from all given servers,
|
149
|
+
# except that after it is used, future requests for the servers will use the
|
150
|
+
# :default server instead.
|
151
|
+
#
|
152
|
+
# Note that an error will be raised if there are any connections currently checked
|
153
|
+
# out for the given servers.
|
154
|
+
def remove_servers(servers)
|
155
|
+
conns = []
|
156
|
+
raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
|
157
|
+
|
158
|
+
sync do
|
159
|
+
servers.each do |server|
|
160
|
+
next unless @servers.has_key?(server)
|
161
|
+
|
162
|
+
queue = @queues[server]
|
163
|
+
|
164
|
+
while conn = queue.pop(timeout: 0)
|
165
|
+
@sizes[server] -= 1
|
166
|
+
conns << conn
|
167
|
+
end
|
168
|
+
|
169
|
+
unless @sizes[server] == 0
|
170
|
+
raise Sequel::Error, "cannot remove server #{server} as it has allocated connections"
|
171
|
+
end
|
172
|
+
|
173
|
+
@servers.delete(server)
|
174
|
+
@sizes.delete(server)
|
175
|
+
@queues.delete(server)
|
176
|
+
@allocated.delete(server)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
nil
|
181
|
+
ensure
|
182
|
+
disconnect_connections(conns)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Return an array of symbols for servers in the connection pool.
|
186
|
+
def servers
|
187
|
+
sync{@servers.keys}
|
188
|
+
end
|
189
|
+
|
190
|
+
def pool_type
|
191
|
+
:sharded_timed_queue
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
# Create a new connection, after the pool's current size has already
|
197
|
+
# been updated to account for the new connection. If there is an exception
|
198
|
+
# when creating the connection, decrement the current size.
|
199
|
+
#
|
200
|
+
# This should only be called after can_make_new?. If there is an exception
|
201
|
+
# between when can_make_new? is called and when preallocated_make_new
|
202
|
+
# is called, it has the effect of reducing the maximum size of the
|
203
|
+
# connection pool by 1, since the current size of the pool will show a
|
204
|
+
# higher number than the number of connections allocated or
|
205
|
+
# in the queue.
|
206
|
+
#
|
207
|
+
# Calling code should not have the mutex when calling this.
|
208
|
+
def preallocated_make_new(server)
|
209
|
+
make_new(server)
|
210
|
+
rescue Exception
|
211
|
+
sync{@sizes[server] -= 1}
|
212
|
+
raise
|
213
|
+
end
|
214
|
+
|
215
|
+
# Disconnect all available connections immediately, and schedule currently allocated connections for disconnection
|
216
|
+
# as soon as they are returned to the pool. The calling code should NOT
|
217
|
+
# have the mutex before calling this.
|
218
|
+
def disconnect_connections(conns)
|
219
|
+
conns.each{|conn| disconnect_connection(conn)}
|
220
|
+
end
|
221
|
+
|
222
|
+
# Decrement the current size of the pool for the server when disconnecting connections.
|
223
|
+
#
|
224
|
+
# Calling code should not have the mutex when calling this.
|
225
|
+
def disconnect_pool_connection(conn, server)
|
226
|
+
sync{@sizes[server] -= 1}
|
227
|
+
disconnect_connection(conn)
|
228
|
+
end
|
229
|
+
|
230
|
+
# If there are any threads waiting on the queue, try to create
|
231
|
+
# new connections in a separate thread if the pool is not yet at the
|
232
|
+
# maximum size.
|
233
|
+
#
|
234
|
+
# The reason for this method is to handle cases where acquire
|
235
|
+
# could not retrieve a connection immediately, and the pool
|
236
|
+
# was already at the maximum size. In that case, the acquire will
|
237
|
+
# wait on the queue until the timeout. This method is called
|
238
|
+
# after disconnecting to potentially add new connections to the
|
239
|
+
# pool, so the threads that are currently waiting for connections
|
240
|
+
# do not timeout after the pool is no longer full.
|
241
|
+
def fill_queue(server)
|
242
|
+
queue = sync{@queues[server]}
|
243
|
+
if queue.num_waiting > 0
|
244
|
+
Thread.new do
|
245
|
+
while queue.num_waiting > 0 && (conn = try_make_new(server))
|
246
|
+
queue.push(conn)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Whether the given size is less than the maximum size of the pool.
|
253
|
+
# In that case, the pool's current size is incremented. If this
|
254
|
+
# method returns true, space in the pool for the connection is
|
255
|
+
# preallocated, and preallocated_make_new should be called to
|
256
|
+
# create the connection.
|
257
|
+
#
|
258
|
+
# Calling code should have the mutex when calling this.
|
259
|
+
def can_make_new?(server, current_size)
|
260
|
+
if @max_size > current_size
|
261
|
+
@sizes[server] += 1
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Try to make a new connection if there is space in the pool.
|
266
|
+
# If the pool is already full, look for dead threads/fibers and
|
267
|
+
# disconnect the related connections.
|
268
|
+
#
|
269
|
+
# Calling code should not have the mutex when calling this.
|
270
|
+
def try_make_new(server)
|
271
|
+
return preallocated_make_new(server) if sync{can_make_new?(server, @sizes[server])}
|
272
|
+
|
273
|
+
to_disconnect = nil
|
274
|
+
do_make_new = false
|
275
|
+
|
276
|
+
sync do
|
277
|
+
current_size = @sizes[server]
|
278
|
+
alloc = @allocated[server]
|
279
|
+
alloc.keys.each do |t|
|
280
|
+
unless t.alive?
|
281
|
+
(to_disconnect ||= []) << alloc.delete(t)
|
282
|
+
current_size -= 1
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
do_make_new = true if can_make_new?(server, current_size)
|
287
|
+
end
|
288
|
+
|
289
|
+
begin
|
290
|
+
preallocated_make_new(server) if do_make_new
|
291
|
+
ensure
|
292
|
+
if to_disconnect
|
293
|
+
to_disconnect.each{|conn| disconnect_pool_connection(conn, server)}
|
294
|
+
fill_queue(server)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Assigns a connection to the supplied thread, if one
|
300
|
+
# is available.
|
301
|
+
#
|
302
|
+
# This should return a connection if one is available within the timeout,
|
303
|
+
# or raise PoolTimeout if a connection could not be acquired within the timeout.
|
304
|
+
#
|
305
|
+
# Calling code should not have the mutex when calling this.
|
306
|
+
def acquire(thread, server)
|
307
|
+
queue = sync{@queues[server]}
|
308
|
+
if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
|
309
|
+
sync{@allocated[server][thread] = conn}
|
310
|
+
else
|
311
|
+
name = db.opts[:name]
|
312
|
+
raise ::Sequel::PoolTimeout, "timeout: #{@timeout}, server: #{server}#{", database name: #{name}" if name}"
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Returns the connection owned by the supplied thread for the given server,
|
317
|
+
# if any. The calling code should NOT already have the mutex before calling this.
|
318
|
+
def owned_connection(thread, server)
|
319
|
+
sync{@allocated[server][thread]}
|
320
|
+
end
|
321
|
+
|
322
|
+
# If the server given is in the hash, return it, otherwise, return the default server.
|
323
|
+
def pick_server(server)
|
324
|
+
sync{@servers[server]}
|
325
|
+
end
|
326
|
+
|
327
|
+
# Create the maximum number of connections immediately. This should not be called
|
328
|
+
# with a true argument unless no code is currently operating on the database.
|
329
|
+
#
|
330
|
+
# Calling code should not have the mutex when calling this.
|
331
|
+
def preconnect(concurrent = false)
|
332
|
+
conn_servers = sync{@servers.keys}.map!{|s| Array.new(@max_size - @sizes[s], s)}.flatten!
|
333
|
+
|
334
|
+
if concurrent
|
335
|
+
conn_servers.map! do |server|
|
336
|
+
queue = sync{@queues[server]}
|
337
|
+
Thread.new do
|
338
|
+
if conn = try_make_new(server)
|
339
|
+
queue.push(conn)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end.each(&:value)
|
343
|
+
else
|
344
|
+
conn_servers.each do |server|
|
345
|
+
if conn = try_make_new(server)
|
346
|
+
sync{@queues[server]}.push(conn)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
nil
|
352
|
+
end
|
353
|
+
|
354
|
+
# Releases the connection assigned to the supplied thread back to the pool.
|
355
|
+
#
|
356
|
+
# Calling code should not have the mutex when calling this.
|
357
|
+
def release(thread, _, server)
|
358
|
+
checkin_connection(sync{@allocated[server].delete(thread)}, server)
|
359
|
+
nil
|
360
|
+
end
|
361
|
+
|
362
|
+
# Adds a connection to the queue of available connections, returns the connection.
|
363
|
+
def checkin_connection(conn, server)
|
364
|
+
sync{@queues[server]}.push(conn)
|
365
|
+
conn
|
366
|
+
end
|
367
|
+
|
368
|
+
# Yield to the block while inside the mutex.
|
369
|
+
#
|
370
|
+
# Calling code should not have the mutex when calling this.
|
371
|
+
def sync
|
372
|
+
@mutex.synchronize{yield}
|
373
|
+
end
|
374
|
+
end
|
@@ -24,15 +24,13 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
|
|
24
24
|
|
25
25
|
# Yield the connection to the block.
|
26
26
|
def hold(server=nil)
|
27
|
-
|
28
|
-
|
29
|
-
@conn.replace([c = make_new(:default)])
|
30
|
-
end
|
31
|
-
yield c
|
32
|
-
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
33
|
-
disconnect if disconnect_error?(e)
|
34
|
-
raise
|
27
|
+
unless c = @conn.first
|
28
|
+
@conn.replace([c = make_new(:default)])
|
35
29
|
end
|
30
|
+
yield c
|
31
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
32
|
+
disconnect if disconnect_error?(e)
|
33
|
+
raise
|
36
34
|
end
|
37
35
|
|
38
36
|
# The SingleConnectionPool always has a maximum size of 1.
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# A connection pool allowing multi-threaded access to a pool of connections.
|
4
4
|
# This is the default connection pool used by Sequel.
|
5
5
|
class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
6
|
-
USE_WAITER = true
|
6
|
+
USE_WAITER = true # SEQUEL6: Remove
|
7
7
|
Sequel::Deprecation.deprecate_constant(self, :USE_WAITER)
|
8
8
|
|
9
9
|
# The maximum number of connections this pool will create (per shard/server
|
@@ -12,17 +12,17 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
12
12
|
|
13
13
|
# An array of connections that are available for use by the pool.
|
14
14
|
# The calling code should already have the mutex before calling this.
|
15
|
-
attr_reader :available_connections
|
15
|
+
attr_reader :available_connections # SEQUEL6: Remove
|
16
16
|
|
17
|
-
# A hash with thread keys and connection values for currently allocated connections.
|
17
|
+
# A hash with thread/fiber keys and connection values for currently allocated connections.
|
18
18
|
# The calling code should already have the mutex before calling this.
|
19
|
-
attr_reader :allocated
|
19
|
+
attr_reader :allocated # SEQUEL6: Remove
|
20
20
|
|
21
21
|
# The following additional options are respected:
|
22
22
|
# :max_connections :: The maximum number of connections the connection pool
|
23
23
|
# will open (default 4)
|
24
24
|
# :pool_timeout :: The amount of seconds to wait to acquire a connection
|
25
|
-
# before raising a
|
25
|
+
# before raising a PoolTimeout error (default 5)
|
26
26
|
def initialize(db, opts = OPTS)
|
27
27
|
super
|
28
28
|
@max_size = Integer(opts[:max_connections] || 4)
|
@@ -31,6 +31,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
31
31
|
@connection_handling = opts[:connection_handling]
|
32
32
|
@available_connections = []
|
33
33
|
@allocated = {}
|
34
|
+
@allocated.compare_by_identity
|
34
35
|
@timeout = Float(opts[:pool_timeout] || 5)
|
35
36
|
@waiter = ConditionVariable.new
|
36
37
|
end
|
@@ -49,8 +50,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
# Removes all connections currently available
|
53
|
-
# yielding each connection to the given block. This method has the effect of
|
53
|
+
# Removes all connections currently available. This method has the effect of
|
54
54
|
# disconnecting from the database, assuming that no connections are currently
|
55
55
|
# being used. If you want to be able to disconnect connections that are
|
56
56
|
# currently in use, use the ShardedThreadedConnectionPool, which can do that.
|
@@ -134,7 +134,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
134
134
|
# calling this.
|
135
135
|
#
|
136
136
|
# This should return a connection is one is available within the timeout,
|
137
|
-
# or
|
137
|
+
# or raise PoolTimeout if a connection could not be acquired within the timeout.
|
138
138
|
def acquire(thread)
|
139
139
|
if conn = assign_connection(thread)
|
140
140
|
return conn
|
@@ -274,6 +274,12 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
274
274
|
end
|
275
275
|
|
276
276
|
@waiter.signal
|
277
|
+
|
278
|
+
# Ensure that after signalling the condition, some other thread is given the
|
279
|
+
# opportunity to acquire the mutex.
|
280
|
+
# See <https://github.com/socketry/async/issues/99> for more context.
|
281
|
+
sleep(0)
|
282
|
+
|
277
283
|
nil
|
278
284
|
end
|
279
285
|
|