informasi kontak saya
Suratmesophia@protonmail.com
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
oleh active_record Parsing ke dalam pernyataan sqlSetelah itu kirimkan ke database,Jalankan PREPARE terlebih dahulu Pernyataan yang disiapkan, pernyataan SQL akan diurai, dianalisis, dioptimalkan dan ditulis ulang.Saat tindak lanjutnyaKeluarkan perintah EXECUTE, pernyataan yang disiapkan akan direncanakan dan dilaksanakan.pg_prepared_statements
untuk memfasilitasi lain kali Anda memanggil pernyataan serupa.mengeksekusi secara langsung pernyataan, alih-alih menguraikan, menganalisis, dan mengoptimalkan, menghindari duplikasi pekerjaan dan meningkatkan efisiensi.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]]
Seperti pada contoh berikut, saat menjalankan SELECT setelah menambah atau menghapus bidang,basis data halakan melemparcached plan must not change result type
, di relcatatan_aktifDapatkan kesalahan ini lalu lemparActiveRecord::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
kesalahanexec_cache
Metodenya, saya menemukan bahwa metode penanganan kesalahan Rails untuk pg adalah: raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
prepare_statement
metode ke dalam cache pernyataan yang disiapkanmodule 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 dan yang lebih baru dapat menonaktifkan fitur ini dengan menyetel pernyataan_siap di database ke false.
default: &default
adapter: postgresql
encoding: unicode
prepared_statements: false
Rails6 dan di bawahnya belum diuji. Jika cara di atas tidak berhasil, Anda dapat mencoba membuat file inisialisasi baru.
# 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)
memeriksa:
User.all
ActiveRecord::Base.connection.execute('select * from pg_prepared_statements').values
==> []
Kesimpulan: Dalam proyek kecil, tidak masalah jika Anda menonaktifkan fungsi ini, dan kinerjanya hampir tidak terpengaruh. Namun, dalam proyek besar, semakin banyak pengguna dan semakin kompleks pernyataan kueri, semakin besar manfaat yang didapat dari fungsi ini. sehingga Anda dapat memutuskan apakah akan menonaktifkannya sesuai dengan situasi sebenarnya.
select *
menjadiselect id, name
Bidang spesifik seperti itu, di Rails7Solusi resmiItu dia# 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
Kesimpulan: Masalah dengan solusi ini adalah menambahkan field dapat diselesaikan dengan sempurna, tetapi menghapus field masih akan menyebabkan kesalahan. Misalnya, setelah menghapus field nama, jika nama dalam pernyataan yang disiapkan pilih id, nama dari pengguna tidak ada, kesalahan akan dilaporkan. Solusi resmi Rails7 juga mengalami masalah ini
Kesimpulan: Restart aplikasi akan menyebabkan layanan sementara 502 Tidak Tersedia. Tentu saja, ketika menerapkan aplikasi, layanan harus di-restart, dan 502 juga akan muncul, jadi yang terbaik adalah menerapkan ketika tidak ada yang mengaksesnya (di tengah-tengah). malam?), sehingga kesalahan yang muncul sesedikit mungkinPreparedStatementCacheExpired
Laporkan kesalahan
transaction
metodeclass 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
atauMyModel.transaction
Kesimpulan: Catatan penting: Jika Anda mengirim email, memposting ke API, atau melakukan operasi lain yang berinteraksi dengan dunia luar sebagai bagian dari transaksi, hal ini dapat menyebabkan beberapa operasi tersebut terkadang terjadi dua kali. Inilah sebabnya mengapa Rails secara resmi tidak melakukan percobaan ulang secara otomatis, tetapi menyerahkannya kepada pengembang aplikasi.
>>>>>>>Ketika saya menguji metode ini sendiri, saya masih mendapatkan kesalahan.
ActiveRecord::Base.connection.clear_cache!
Tidak menemukan solusi yang tepat