2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
it 'not raise ActiveRecord::PreparedStatementCacheExpired' do
create(:user)
User.first
User.find_by_sql('ALTER TABLE users ADD new_metric_column integer;')
ActiveRecord::Base.transaction { User.first }
end
User.all
von active_record In SQL-Anweisung analysierenSenden Sie es anschließend an die Datenbank.Führen Sie zuerst PREPARE aus Vorbereitete Anweisungen, SQL-Anweisungen werden geparst, analysiert, optimiert und neu geschrieben.Wenn das Follow-upGeben Sie einen EXECUTE-Befehl aus, wird die vorbereitete Stellungnahme geplant und ausgeführt.pg_prepared_statements
um den nächsten Aufruf ähnlicher Aussagen zu erleichtern.direkt ausführen Anweisungen statt Parsen, Analysieren und Optimieren, wodurch Doppelarbeit vermieden und die Effizienz verbessert wird.User.first
User.all
# 执行上面的2个查询后,用connection.instance_variable_get(:@statements)就可以看到缓存的准备语句
ActiveRecord::Base.connection.instance_variable_get(:@statements)
==> <ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::StatementPool:0x00000001086b13c8
@cache={78368=>{""$user", public-SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT
$1"=>"a7", ""$user", public-SELECT "users".* FROM "users" /* loading for inspect */ LIMIT $1"=>"a8"}},
@statement_limit=1000, @connection=#<PG::Connection:0x00000001086b31a0>, @counter=8>
# 这个也可以看到,会在数据库中去查询
ActiveRecord::Base.connection.execute('select * from pg_prepared_statements').values
(0.5ms) select * from pg_prepared_statements
==> [["a7", "SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1", "2024-07-
11T07:03:06.891+00:00", "{bigint}", false], ["a8", "SELECT "users".* FROM "users" /* loading for inspect
*/ LIMIT $1", "2024-07-11T07:04:47.772+00:00", "{bigint}", false]]
Wie im folgenden Beispiel: Wenn Sie SELECT nach dem Hinzufügen oder Löschen von Feldern ausführen,pg-Datenbankwird werfencached plan must not change result type
, in SchienenAktiver RekordErhalten Sie diesen Fehler und werfen Sie ihn dannActiveRecord::PreparedStatementCacheExpired
ALTER TABLE users ADD COLUMN new_column integer;
ALTER TABLE users DROP COLUMN old_column;
添加或删除列,然后执行 SELECT *
删除 old_column 列然后执行 SELECT users.old_column
cached plan must not change result type
Fehlerexec_cache
Methode: Ich habe festgestellt, dass die Fehlerbehandlungsmethode von Rails für pg wie folgt lautet: raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
prepare_statement
-Methode in den vorbereiteten Anweisungscachemodule ActiveRecord
module ConnectionHandling
def exec_cache(sql, name, binds)
materialize_transactions
mark_transaction_written_if_write(sql)
update_typemap_for_default_timezone
stmt_key = prepare_statement(sql, binds)
type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds, stmt_key) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.exec_prepared(stmt_key, type_casted_binds)
end
end
rescue ActiveRecord::StatementInvalid => e
raise unless is_cached_plan_failure?(e)
# Nothing we can do if we are in a transaction because all commands
# will raise InFailedSQLTransaction
if in_transaction?
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
else
@lock.synchronize do
# outside of transactions we can simply flush this query and retry
@statements.delete sql_key(sql)
end
retry
end
end
end
end
Rails6 und höher können diese Funktion deaktivieren, indem vorbereitete_Anweisungen in der Datenbank auf „false“ gesetzt werden.
default: &default
adapter: postgresql
encoding: unicode
prepared_statements: false
Rails6 und niedriger wurden nicht getestet. Wenn das oben Genannte nicht funktioniert, können Sie versuchen, eine neue Initialisierungsdatei zu erstellen.
# config/initializers/disable_prepared_statements.rb:
db_configuration = ActiveRecord::Base.configurations[Rails.env]
db_configuration.merge!('prepared_statements' => false)
ActiveRecord::Base.establish_connection(db_configuration)
verifizieren:
User.all
ActiveRecord::Base.connection.execute('select * from pg_prepared_statements').values
==> []
Fazit: Bei kleinen Projekten spielt es keine Rolle, ob Sie diese Funktion deaktivieren, und die Leistung bleibt nahezu unverändert. Bei großen Projekten sind jedoch die Vorteile dieser Funktion umso größer, je mehr Benutzer und je komplexer die Abfrageanweisungen sind. So können Sie entsprechend der tatsächlichen Situation entscheiden, ob Sie es deaktivieren möchten.
select *
werdenselect id, name
Solche spezifischen Felder in Rails7Offizielle LösungDas ist es# config/application.rb
module MyApp
class Application < Rails::Application
config.active_record.enumerate_columns_in_select_statements = true
end
end
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
#__fake_column__是自定义的,不要是某个表中的字段就行,如果是[:id],那么 User.all就会被解析为select name from users,没有id了
self.ignored_columns = [:__fake_column__]
end
Fazit: Das Problem bei dieser Lösung besteht darin, dass das Hinzufügen von Feldern perfekt gelöst werden kann, das Löschen von Feldern jedoch immer noch Fehler verursacht. Wenn beispielsweise nach dem Löschen des Namensfelds der Name in der vorbereiteten Anweisung „select id“ (Name von Benutzern) nicht vorhanden ist, Bei der offiziellen Rails7-Lösung wird dieses Problem ebenfalls gemeldet
Fazit: Ein Neustart der Anwendung führt dazu, dass der Dienst vorübergehend 502 nicht verfügbar ist. Natürlich muss der Dienst beim Bereitstellen neu gestartet werden und es wird auch 502 angezeigt. Daher ist es am besten, ihn bereitzustellen, wenn niemand darauf zugreift (in der Mitte). in der Nacht?), damit möglichst wenige Fehler auftretenPreparedStatementCacheExpired
Melden Sie einen Fehler
transaction
Methodeclass ApplicationRecord < ActiveRecord::Base
class << self
def transaction(*args, &block)
retried ||= false
super
rescue ActiveRecord::PreparedStatementCacheExpired
if retried
raise
else
retried = true
retry
end
end
end
end
ApplicationRecord.transaction do ... end
oderMyModel.transaction
Fazit: Wichtiger Hinweis: Wenn Sie E-Mails senden, an APIs posten oder andere Vorgänge ausführen, die im Rahmen einer Transaktion mit der Außenwelt interagieren, kann dies dazu führen, dass einige dieser Vorgänge gelegentlich zweimal ausgeführt werden. Aus diesem Grund führt Rails offiziell Wiederholungsversuche nicht automatisch durch, sondern überlässt dies den Anwendungsentwicklern.
>>>>>>>Wenn ich diese Methode selbst teste, erhalte ich immer noch Fehler.
ActiveRecord::Base.connection.clear_cache!
Habe keine perfekte Lösung gefunden