كيف يمكنك منع تغييرات قاعدة البيانات داخل ActivereCord قبل _create من التراجع عندما تعود كاذبة؟
-
22-09-2019 - |
سؤال
لقد أضفت مرشحًا قبل _create إلى أحد طرازات Rails ActivereCord وداخل هذا المرشح أقوم ببعض تحديثات قاعدة البيانات.
أحيانًا أعود خطأ من المرشح لمنع إنشاء النموذج المستهدف ، لكن هذا يسبب كل آخر تغييرات قاعدة البيانات التي أجريتها (أثناء داخل المرشح) لتتخلف عن التراجع.
كيف يمكنني منع ذلك؟
تحديث #1: إليك بعض الكود الزائف الذي يشرح مشكلتي:
class Widget < ActiveRecord::Base
before_create :update_instead
def update_instead
if some_condition?
update_some_record_in_same_model # this is getting rolled back
return false # don't create a new record
else
return true # let it continue
end
end
end
تحديث رقم 2: بعض الإجابات الجيدة أدناه ولكن كان لكل منها أوجه القصور. انتهى بي الأمر تجاوز طريقة إنشاء مثل SO:
def create
super unless update_instead? # yes I reversed the return values from above
end
المحلول
كان علي فقط القيام بذلك مؤخرًا. تحتاج إلى طلب اتصال آخر على وجه التحديد من AR. ثم قم بتنفيذ التغييرات الخاصة بك على هذا الاتصال. وبهذه الطريقة ، إذا فشل الخلق وتراجع المعاملة ، فقد تم بالفعل ارتكاب تغييرات رد الاتصال في معاملة مختلفة.
تجاهل إجابتي أعلاه. رمز المثال الذي قدمته للتو أشياء موضحة حقًا.
class Foo < ActiveRecord::Base
before_create :update_instead
def update_instead
dbconn = self.class.connection_pool.checkout
dbconn.transaction do
dbconn.execute("update foos set name = 'updated'")
end
self.class.connection_pool.checkin(dbconn)
false
end
end
>> Foo.create(:name => 'sam')
=> #<Foo id: nil, name: "sam", created_at: nil, updated_at: nil>
>> Foo.all
=> [#<Foo id: 2, name: "updated", created_at: "2009-10-21 15:12:55", updated_at: "2009-10-21 15:12:55">]
نصائح أخرى
استخدم معاملة في المرشح.
هل حاولت الكتابة فوق إنشاء/حفظ وإصداراتها المدمرة؟ ActivereCord :: base.create ، ActivereCord :: base.save وإصداراتها المدمرة ملفوفة في معاملة ، فهي أيضًا ما هي عمليات الاسترجاعات والتحققات. إذا كنت تتجاوز ذلك ، فإن الأشياء التي تقوم بها Super فقط ستكون جزءًا من المعاملة. إذا كنت بحاجة إلى عمليات التحقق من تشغيلك من قبل ، فيمكنك الاتصال الصحيح بشكل صريح لتشغيلها جميعًا.
مثال:
before_create :before_create_actions_that_can_be_rolled_back
def create
if valid? && before_create_actions_that_wont_be_rolled_back
super
end
end
def before_create_actions_that_wont_be_rolled_back
# exactly what it sounds like
end
def before_create_actions_that_can_be_rolled_back
# exactly what it sounds like
end
التحذير: مع هذه التعديلات ، سيتم استدعاء الأساليب بهذا الترتيب:
- قبل التحقق من الصحة (ON_CREATE)
- التحقق
- بعد التحقق من الصحة (ON_CREATE)
- قبل _create_actions_that_wont_be_rolled_back
- قبل التحقق من الصحة (ON_CREATE)
- التحقق
- بعد التحقق من الصحة (ON_CREATE)
- قبل حفظ عمليات الاستدعاء
- قبل إنشاء عمليات الاسترجاعات
- يتم إنشاء السجل
- بعد إنشاء عمليات الاسترجاعات
- بعد حفظ عمليات الاستدعاء
إذا فشلت أي عمليات التحقق من صحة أو إذا كان أي رد فعل يعيد خطأ في الخطوات 5-12 ، فسيتم إعادة قاعدة البيانات إلى الحالة التي كانت فيها قبل الخطوة 5.
إذا كان صالحا؟ يفشل ، أو قبل تفشل _create_actions_that_wont_be_rolled_back من توقف السلسلة بأكملها.
هل يمكن إجراء هذه التغييرات في after_create
في حين أن؟
هذا يستخدم نفس مفهوم إجابة ريتش كافانو. أضفت نموذجًا للأصل حتى يكون أكثر وضوحًا ما يفعله المرشح. المفتاح هو استخدام مؤشر ترابط + تسجيل الوصول التلقائي/تسجيل الوصول لاتصال منفصل. ملاحظة: يجب عليك التأكد من ضبط قيمة البلياردو على 2 على الأقل في مواصفات الاتصال الخاصة بك ، اعتمادًا على عدد المواضيع المتزامنة التي ستشغلها. أعتقد أنه افتراضي إلى 5.
class Gadget < ActiveRecord::Base
has_many :widgets
end
class Widget < ActiveRecord::Base
belongs_to :gadget
before_create :update_instead
def update_some_record_in_same_model
# the thread forces a new connection to be checked out
Thread.new do
ActiveRecord::Base.connection_pool.with_connection do |conn|
# try this part without the 2 surrounding blocks and it will be rolled back
gadget.touch_count += 1
gadget.save!
end
end.join
end
def some_condition?
true
end
def update_instead
if some_condition?
update_some_record_in_same_model # this is getting rolled back
p [:touch_count_in_filter, gadget.reload.touch_count]
return false # don't create a new record
else
return true # let it continue
end
end
end
اختبار:
g = Gadget.create(:name => 'g1')
puts "before:"
p [:touch_count, g.reload.touch_count]
p [:widget_count, Widget.count]
g.widgets.create(:name => 'w1')
puts "after:"
# Success means the count stays incremented
p [:touch_count, g.reload.touch_count]
p [:widget_count, Widget.count]
قراءة متعمقة: http://bibwild.wordpress.com/2011/11/14/multi-threading-in-trails-activerecord-3-0-3-1/