В моем приложении Rails 5.2.4.1, работающем с ruby 2.6.5, postgres 10.11 и pg gem версии 1.1.4, у меня есть такая модель ActiveRecord:
class RealEstate < ApplicationRecord
belongs_to :developer, optional: true
belongs_to :customer, optional: true
# Do not fetch heavy/noisy columns such as JSONB metadata
scope :light, -> { select(RealEstate.column_names.map!(&:to_sym) - [:metadata]) }
scope :active, -> { where(active: true) }
has_many :lots, -> { where(lot: true) }, class_name: 'RealEstate', foreign_key: 'program_id'
belongs_to :program, class_name: 'RealEstate', optional: true
enum old_or_new: {
old: 0,
new_building: 1 # developers new real estate programs
}
enum transaction_type: {
rent: 0,
buy: 1
}
enum property_category: {
apartment: 0,
house: 1,
other: 2
}
end
Базовая модель данных в db/structure.sql
выглядит следующим образом (некоторые столбцы анонимны):
CREATE TABLE public.real_estates (
id bigint NOT NULL,
geom public.geometry(Point),
title character varying,
description text,
address character varying,
old_or_new integer DEFAULT 0 NOT NULL,
property_category integer DEFAULT 0 NOT NULL,
some_string character varying,
some_string_2 text,
price integer,
created_at timestamp without time zone DEFAULT now() NOT NULL,
updated_at timestamp without time zone DEFAULT now() NOT NULL,
some_string_3 character varying,
metadata jsonb DEFAULT '{}'::jsonb NOT NULL,
some_string_4 character varying,
active boolean DEFAULT true NOT NULL,
customer_id bigint,
some_string_5 character varying,
transaction_type integer DEFAULT 0 NOT NULL,
some_string_6 character varying,
some_boolean_1 boolean DEFAULT false NOT NULL,
country_code character varying(2) DEFAULT 'FR'::character varying,
some_boolean_2 boolean DEFAULT true NOT NULL,
some_timestamp_1 timestamp without time zone,
some_timestamp_2 timestamp without time zone,
some_array integer[] DEFAULT '{}'::integer[],
some_string_7 character varying,
some_count integer,
some_other_integer integer,
some_boolean_3 boolean DEFAULT true NOT NULL,
some_string_8 character varying,
some_timestamp_3 timestamp without time zone,
some_boolean_4 boolean DEFAULT false NOT NULL,
program_id bigint,
some_string_9 character varying,
developer_id bigint,
some_boolean_4 boolean DEFAULT false,
some_boolean_5 boolean DEFAULT false NOT NULL,
CONSTRAINT countrycode CHECK (((country_code)::text ~* '[A-Z][A-Z]'::text))
);
Как вы можете видеть, обратных вызовов Rails нет. «Странности» - мы используем postgres столбец JSONB и postgres столбец целочисленного массива.
Теперь у нас есть маршрут обновления администратора для редактирования некоторых из этих записей. Он не следует соглашениям о присвоении имен и не использует активные проверки записей, но dry-validations
:
require 'dry-validation'
module Admin
class NewRealEstatesController < Admin::BaseController
before_action :set_form, only: [:edit, :update]
def update
validation_result = AdminRealEstateSchema.call(real_estate_listing_params.to_h)
if validation_result.success?
# Don't mind column mappings with the dry schema validator, there might be inconsistencies
# because I anonymized the column names, but the mapping is correct and validations pass
@estate.some_string_1 = params[:real_estate][:some_string_1]
@estate.some_string_2 = params[:real_estate][:some_string_2]
@estate.some_string_3 = params[:real_estate][:some_string_3]
@estate.property_category = params[:real_estate][:property_category]
@estate.some_string_4 = params[:real_estate][:some_string_4]
@estate.price = params[:real_estate][:price]
@estate.some_string_5 = params[:real_estate][:some_string_5]
@estate.active = params[:real_estate][:active]
@estate.some_timestamp_1 = params[:real_estate][:some_timestamp_1]
@estate.some_timestamp_2 = params[:real_estate][:some_timestamp_2]
@estate.some_count = params[:real_estate][:some_count]
@estate.surface = params[:real_estate][:surface]
@estate.some_boolean_1 = params[:real_estate][:some_boolean_1]
@estate.some_boolean_2 = params[:real_estate][:some_boolean_2]
res = @estate.save
render json: { result: 'done', debug: { res_save_ar: res, changed: @estate.saved_changes?, \
changes: @estate.saved_changes, previous_changes: @estate.previous_changes } }, status: 200
else
respond_to do |format|
format.html { render :edit }
format.json { render json: { errors: validation_result.errors.map{|key, error| "#{key} : #{error[0]}"} }, \
status: :unprocessable_entity, serializer: nil }
end
end
end
def edit; end
private
def set_form
@estate = RealEstate.find(params[:id])
end
def real_estate_listing_params
params[:real_estate_listing].permit(...) # all strong params edited in route, it's correct
end
end
end
AdminRealEstateSchema = Dry::Validation.Form do
configure { config.type_specs = true, config.messages = :i18n }
PROPERTY_CATEGORIES = %w(house apartment other)
required(:property_category, :string).filled(:str?, included_in?: PROPERTY_CATEGORIES)
required(:some_string_1, :string).filled(:str?)
required(:some_string_2, :string).filled
optional(:some_string_3).maybe(:str?)
optional(:some_string_4).maybe(:str?)
optional(:some_string_4).maybe(:str?)
optional(:some_string_5).maybe(:str?)
required(:price, :int).filled(:int?)
optional(:some_other_int).maybe(:int?)
optional(:some_count).maybe(:int?)
required(:active, :bool).filled(:bool?)
required(:some_boolean_1, :bool).filled(:bool?)
required(:some_boolean_2, :bool).filled(:bool?)
required(:some_time_1, :date_time).filled(:date_time?)
optional(:some_time_2).maybe(:date_time?)
end
Представление правильное и используется remote: true
.
Теперь о проблеме: иногда, но без согласованности, запись сохраняется и обновляется благодаря форме, иногда абсолютно не , как откат изменений, хотя save
возвращает true, как видно из возвращенного JSON в веб-консоли.
ActiveRecord также правильно и отображает все столбцы, которые должны быть обновлены / сохранены с правильными значениями, как видно из @estate.saved_changes
Rspe c проверяет все проходы. Но только в процессе производства мы сталкиваемся с этой ошибкой.
Теперь отметим несколько важных моментов:
- Мы уже сталкивались с ошибкой
create_or_find_by
через несколько месяцев go. Невозможно правильно создавать записи. Из-за крайних сроков я прибегнул к raw SQL через ActiveRecord::Base.connection.execute
, и он работает. - Эта таблица очень активна, с большим количеством фоновых заданий для вставки / чтения данных от клиентов.
- Там в этой таблице нет пользовательских триггеров postgres.
Я впадаю в отчаяние и думаю, что это может быть ошибка в нашем коде (очевидно) или ошибка в activerecord
.
Есть идеи или мысли?