τα στοιχεία επικοινωνίας μου
Ταχυδρομείο[email protected]
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
από active_record Ανάλυση σε δήλωση sqlΜετά από αυτό, στείλτε το στη βάση δεδομένων,Εκτελέστε πρώτα το PREPARE Οι προετοιμασμένες δηλώσεις, οι εντολές SQL θα αναλυθούν, θα αναλυθούν, θα βελτιστοποιηθούν και θα ξαναγραφτούν.Όταν η παρακολούθησηΕκδώστε μια εντολή EXECUTE, η ετοιμασμένη δήλωση θα προγραμματιστεί και θα εκτελεστεί.pg_prepared_statements
για να διευκολύνετε την επόμενη φορά που θα καλέσετε παρόμοιες δηλώσεις.εκτελέσει απευθείας δηλώσεις, αντί για ανάλυση, ανάλυση και βελτιστοποίηση, αποφεύγοντας την επικάλυψη της εργασίας και βελτιώνοντας την αποτελεσματικότητα.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]]
Όπως στο ακόλουθο παράδειγμα, κατά την εκτέλεση του SELECT μετά την προσθήκη ή τη διαγραφή πεδίων,pg βάσης δεδομένωνθα ρίξειcached plan must not change result type
, σε ράγεςενεργή_εγγραφήΛάβετε αυτό το σφάλμα και μετά ρίχνετεActiveRecord::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
λάθοςexec_cache
Μέθοδος, βρήκα ότι η μέθοδος χειρισμού σφαλμάτων των σιδηροτροχιών για το pg είναι: raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
prepare_statement
μέθοδος στην έτοιμη προσωρινή μνήμη δηλώσεωνmodule 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 και παραπάνω μπορούν να απενεργοποιήσουν αυτήν τη δυνατότητα ορίζοντας τις προετοιμασίες_διατάξεις στη βάση δεδομένων σε false.
default: &default
adapter: postgresql
encoding: unicode
prepared_statements: false
Τα Rails6 και κάτω δεν έχουν δοκιμαστεί εάν τα παραπάνω δεν λειτουργούν, μπορείτε να δοκιμάσετε να δημιουργήσετε ένα νέο αρχείο προετοιμασίας.
# 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)
επαληθεύω:
User.all
ActiveRecord::Base.connection.execute('select * from pg_prepared_statements').values
==> []
Συμπέρασμα: Σε μικρά έργα, δεν έχει σημασία αν απενεργοποιήσετε αυτήν τη λειτουργία και η απόδοση θα παραμείνει σχεδόν ανεπηρέαστη, ωστόσο, σε μεγάλα έργα, όσο περισσότερους χρήστες και όσο πιο σύνθετες δηλώσεις ερωτημάτων, τόσο μεγαλύτερα θα είναι τα οφέλη αυτής της λειτουργίας. ώστε να μπορείτε να αποφασίσετε εάν θα το απενεργοποιήσετε σύμφωνα με την πραγματική κατάσταση.
select *
γίνομαιselect id, name
Τέτοια συγκεκριμένα πεδία, σε ράγες7Επίσημη λύσηΑυτό είναι# 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
Συμπέρασμα: Το πρόβλημα με αυτήν τη λύση είναι ότι η προσθήκη πεδίων μπορεί να λυθεί τέλεια, αλλά η διαγραφή πεδίων θα εξακολουθεί να προκαλεί σφάλματα, για παράδειγμα, μετά τη διαγραφή του πεδίου ονόματος, εάν το όνομα στην προετοιμασμένη δήλωση επιλέξτε το αναγνωριστικό, το όνομα από τους χρήστες δεν υπάρχει. θα αναφερθεί ένα σφάλμα Rails7 επίσημη λύση έχει επίσης αυτό το πρόβλημα
Συμπέρασμα: Η επανεκκίνηση της εφαρμογής θα προκαλέσει μια προσωρινή υπηρεσία 502 Μη διαθέσιμη. Φυσικά, κατά την ανάπτυξη της εφαρμογής, πρέπει επίσης να επανεκκινήσετε την υπηρεσία και θα εμφανιστεί επίσης το 502, επομένως είναι καλύτερο να το αναπτύξετε όταν κανείς δεν έχει πρόσβαση σε αυτήν (στην μέσα στη νύχτα;), ώστε να υπάρχουν όσο το δυνατόν λιγότερα λάθη ΕμφανίζονταιPreparedStatementCacheExpired
Αναφέρετε ένα σφάλμα
transaction
μέθοδοςclass 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
ήMyModel.transaction
Συμπέρασμα: Σημαντική σημείωση: Εάν στέλνετε μηνύματα ηλεκτρονικού ταχυδρομείου, δημοσιεύετε σε API ή πραγματοποιείτε άλλες λειτουργίες που αλληλεπιδρούν με τον έξω κόσμο ως μέρος μιας συναλλαγής, αυτό μπορεί να προκαλέσει μερικές από αυτές τις λειτουργίες να πραγματοποιούνται περιστασιακά δύο φορές. Αυτός είναι ο λόγος που το Rails επίσημα δεν εκτελεί αυτόματα επαναλήψεις, αλλά το αφήνει στους προγραμματιστές εφαρμογών.
>>>>>>>Όταν δοκιμάζω μόνος μου αυτήν τη μέθοδο, εξακολουθώ να λαμβάνω σφάλματα.
ActiveRecord::Base.connection.clear_cache!
Δεν βρέθηκε τέλεια λύση