내 연락처 정보
우편메소피아@프로톤메일.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
작성자: 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 이상에서는 데이터베이스의 prepare_statements를 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
Rails7의 특정 필드공식 솔루션그게 다야# 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
결론: 이 솔루션의 문제점은 필드 추가는 완벽하게 해결할 수 있지만 필드를 삭제하면 여전히 오류가 발생한다는 것입니다. 예를 들어 이름 필드를 삭제한 후에도 준비된 명령문의 이름인 select id, name from users가 존재하지 않는 경우, 오류가 보고됩니다. Rails7 공식 솔루션에도 이 문제가 있습니다.
결론: 애플리케이션을 다시 시작하면 임시 서비스 502 Unavailable이 발생합니다. 물론 애플리케이션을 배포할 때 서비스도 다시 시작해야 하며 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!
완벽한 해결책을 찾지 못함