mirror of
https://github.com/discourse/discourse.git
synced 2024-12-18 16:13:45 +08:00
3f6c2f82d0
This adapter ensures that MiniSql locks the ActiveRecord mutex before using the raw PG connection. This ensures that multiple threads will not attempt to use the same connection simultaneously. This commit also removes the schema_cache_concurrency freedom-patch, which is no longer required now that cross-thread connection use is controlled by the mutex. For the original root cause of this issue, see https://github.com/rails/rails/pull/38577
112 lines
2.8 KiB
Ruby
112 lines
2.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class MiniSqlMultisiteConnection < MiniSql::ActiveRecordPostgres::Connection
|
|
|
|
class CustomBuilder < MiniSql::Builder
|
|
|
|
def initialize(connection, sql)
|
|
super
|
|
end
|
|
|
|
def secure_category(secure_category_ids, category_alias = 'c')
|
|
if secure_category_ids.present?
|
|
where("NOT COALESCE(#{category_alias}.read_restricted, false) OR #{category_alias}.id in (:secure_category_ids)", secure_category_ids: secure_category_ids)
|
|
else
|
|
where("NOT COALESCE(#{category_alias}.read_restricted, false)")
|
|
end
|
|
self
|
|
end
|
|
end
|
|
|
|
class ParamEncoder
|
|
def encode(*sql_array)
|
|
# use active record to avoid any discrepencies
|
|
ActiveRecord::Base.public_send(:sanitize_sql_array, sql_array)
|
|
end
|
|
end
|
|
|
|
class AfterCommitWrapper
|
|
def initialize(&blk)
|
|
raise ArgumentError, "tried to create a Proc without a block in AfterCommitWrapper" if !blk
|
|
@callback = blk
|
|
end
|
|
|
|
def committed!(*)
|
|
if DB.transaction_open?
|
|
# Nested transaction. Pass the callback to the parent
|
|
ActiveRecord::Base.connection.add_transaction_record(self)
|
|
else
|
|
@callback.call
|
|
end
|
|
end
|
|
|
|
def before_committed!(*); end
|
|
def rolledback!(*); end
|
|
def trigger_transactional_callbacks?
|
|
true
|
|
end
|
|
end
|
|
|
|
def transaction_open?
|
|
ActiveRecord::Base.connection.transaction_open?
|
|
end
|
|
|
|
if Rails.env.test?
|
|
def test_transaction=(transaction)
|
|
@test_transaction = transaction
|
|
end
|
|
|
|
def transaction_open?
|
|
ActiveRecord::Base.connection.current_transaction != @test_transaction
|
|
end
|
|
end
|
|
|
|
# Allows running arbitrary code after the current transaction has been committed.
|
|
# Works with nested ActiveRecord transaction blocks. Useful for scheduling sidekiq jobs.
|
|
# If not currently in a transaction, will execute immediately
|
|
def after_commit(&blk)
|
|
return blk.call if !transaction_open?
|
|
|
|
ActiveRecord::Base.connection.add_transaction_record(
|
|
AfterCommitWrapper.new(&blk)
|
|
)
|
|
end
|
|
|
|
def self.instance
|
|
new(nil, param_encoder: ParamEncoder.new)
|
|
end
|
|
|
|
# we need a tiny adapter here so we always run against the
|
|
# correct multisite connection
|
|
def active_record_connection
|
|
ActiveRecord::Base.connection
|
|
end
|
|
|
|
# make for a multisite friendly prepared statement cache
|
|
def prepared(condition = true)
|
|
if condition
|
|
conn = raw_connection.instance_variable_get(:@mini_sql_prepared_connection)
|
|
if !conn
|
|
conn = MiniSql::Postgres::PreparedConnection.new(self)
|
|
raw_connection.instance_variable_set(:@mini_sql_prepared_connection, conn)
|
|
end
|
|
conn
|
|
else
|
|
self
|
|
end
|
|
end
|
|
|
|
def build(sql)
|
|
CustomBuilder.new(self, sql)
|
|
end
|
|
|
|
def sql_fragment(query, *args)
|
|
if args.length > 0
|
|
param_encoder.encode(query, *args)
|
|
else
|
|
query
|
|
end
|
|
end
|
|
|
|
end
|