Transaction_id trong PaperTrail
Bài đăng này đã không được cập nhật trong 8 năm
Trong việc sử dụng gem PaperTrail (https://github.com/airblade/paper_trail) để tạo log, việc quản lý tranction_id
đôi lúc gặp khá nhiều vấn đề, bài viết sau hi vọng giúp bạn phần nào.
Đầu tiên transaction_id
có tác dụng đánh dấu những version được tạo ra cùng 1 thời điểm hoặc trong cùng 1 action submit.
Tại bài viết trước (https://viblo.asia/pham.huy.cuong/posts/E7bGo9LOR5e2) đã trình bày về việc log cho các mối quan hệ nhiều nhiều bằng gem này, ta vẫn sử dụng các model User
và House
để làm ví dụ.
Tại console update bản ghi đầu tiên của User
:
User.first.update house_ids: [3,4], name: "Name_0_new"
Action tạo version
của đối với bản ghi đầu tiên của model House
là :
SQL (0.1ms) INSERT INTO "versions" ("event", "object", "created_at", "transaction_id", "item_id", "item_type") VALUES (?, ?, ?, ?, ?, ?) [["event", "update"], ["object", "---\nid: 1\nname: Name_0\ncreated_at: 2015-12-30 10:20:52.589033000 Z\nupdated_at: 2015-12-30 10:20:52.589033000 Z\n"], ["created_at", "2016-02-24 01:50:43.120334"], ["transaction_id", 26], ["item_id", 1], ["item_type", "User"]]
Ta có thể thấy transaction_id
được gán vào là 26
, thử gọi ra tất cả các version
được gán transaction_id
là 26
ta có:
PaperTrail::Version.where transaction_id: 26
PaperTrail::Version Load (0.4ms) SELECT "versions".* FROM "versions" WHERE "versions"."transaction_id" = ? [["transaction_id", 26]]
=> #<ActiveRecord::Relation [#<PaperTrail::Version id: 26, item_type: "HousesUser", item_id: 11, event: "create", whodunnit: nil, object: nil, created_at: "2016-02-24 01:50:43", transaction_id: 26>, #<PaperTrail::Version id: 27, item_type: "User", item_id: 1, event: "update", whodunnit: nil, object: "---\nid: 1\nname: Name_0\ncreated_at: 2015-12-30 10:2...", created_at: "2016-02-24 01:50:43", transaction_id: 26>]>
Ta thấy có 2 version
được tạo cùng với 1 transaction_id
là 26
: PaperTrail::Version id: 26
và PaperTrail::Version id: 27
.
PaperTrail::Version id: 26
được tạo cho bản ghi của model HousesUser
:
PaperTrail::Version.find 26
PaperTrail::Version Load (0.1ms) SELECT "versions".* FROM "versions" WHERE "versions"."id" = ? LIMIT 1 [["id", 26]]
=> #<PaperTrail::Version id: 26, item_type: "HousesUser", item_id: 11, event: "create", whodunnit: nil, object: nil, created_at: "2016-02-24 01:50:43", transaction_id: 26>
irb(main):012:0>
PaperTrail::Version id: 27
được tạo cho bản ghi của model User
:
PaperTrail::Version.find 27
PaperTrail::Version Load (0.2ms) SELECT "versions".* FROM "versions" WHERE "versions"."id" = ? LIMIT 1 [["id", 27]]
=> #<PaperTrail::Version id: 27, item_type: "User", item_id: 1, event: "update", whodunnit: nil, object: "---\nid: 1\nname: Name_0\ncreated_at: 2015-12-30 10:2...", created_at: "2016-02-24 01:50:43", transaction_id: 26>
2 version
được tạo ra có cùng 1 transaction_id
vì cùng đươc submit 1 lúc. Từ đây ta có thể tìm được các version được tạo ra cùng lúc với 1 version bằng scope trong model version
:
scope :submit_with, ->do
PaperTrail::Version.where transaction_id: transaction_id
end
Khi update bản ghi đầu tiên của model User
, 2 version
với 2 transaction_id
được tạo ra:
User.first.update name: "Name_0_update_first"
User.first.update name: "Name_0_update_second"
User.first.versions.last 2
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
PaperTrail::Version Load (0.5ms) SELECT "versions".* FROM "versions" WHERE "versions"."item_id" = ? AND "versions"."item_type" = ? ORDER BY "versions"."created_at" ASC, "versions"."id" ASC [["item_id", 1], ["item_type", "User"]]
=> [#<PaperTrail::Version id: 28, item_type: "User", item_id: 1, event: "update", whodunnit: nil, object: "---\nid: 1\nname: Name_0_new\ncreated_at: 2015-12-30 ...", created_at: "2016-02-24 02:27:34", transaction_id: 28>, #<PaperTrail::Version id: 29, item_type: "User", item_id: 1, event: "update", whodunnit: nil, object: "---\nid: 1\nname: Name_0_update_first\ncreated_at: 20...", created_at: "2016-02-24 02:27:41", transaction_id: 29>]
Ta thấy 2 version
được tạo ra với transaction_id
là 28
và 29
. Vậy nếu muốn tạo ra các version
được submit tại 2 thời điểm khác nhau có cùng 1 transaction_id
ta phải làm như thế nào? Có thể gói trong 1 transaction do end
.
User.transaction do
User.first.update name: "Name_0_3th"
User.first.update name: "Name_0_4th"
end
(0.2ms) begin transaction
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
SQL (0.3ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "Name_0_3th"], ["updated_at", "2016-02-24 02:32:45.390444"], ["id", 1]]
SQL (0.1ms) INSERT INTO "versions" ("event", "object", "created_at", "item_id", "item_type") VALUES (?, ?, ?, ?, ?) [["event", "update"], ["object", "---\nid: 1\nname: Name_0_update_second\ncreated_at: 2015-12-30 10:20:52.589033000 Z\nupdated_at: 2016-02-24 02:27:41.069064000 Z\n"], ["created_at", "2016-02-24 02:32:45.390444"], ["item_id", 1], ["item_type", "User"]]
SQL (0.1ms) UPDATE "versions" SET "transaction_id" = ? WHERE "versions"."id" = ? [["transaction_id", 30], ["id", 30]]
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
SQL (0.0ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "Name_0_4th"], ["updated_at", "2016-02-24 02:32:45.395115"], ["id", 1]]
SQL (0.0ms) INSERT INTO "versions" ("event", "object", "created_at", "transaction_id", "item_id", "item_type") VALUES (?, ?, ?, ?, ?, ?) [["event", "update"], ["object", "---\nid: 1\nname: Name_0_3th\ncreated_at: 2015-12-30 10:20:52.589033000 Z\nupdated_at: 2016-02-24 02:32:45.390444000 Z\n"], ["created_at", "2016-02-24 02:32:45.395115"], ["transaction_id", 30], ["item_id", 1], ["item_type", "User"]]
(147.4ms) commit transaction
=> true
Ta thấy 2 version
được tạo ra với cùng 1 transaction_id
là 30
.
Transaction_id
được tính như thế nào?, thực chất việc ghi lại transaction_id
nhằm phân biệt các version
tại các lần submit khác nhau, việc tính toán transaction_id
khá đơn gỉan, nó được lấy bằng id
của version
đầu tiên được tạo ra tại 1 lần submit (hay trong 1 transaction do end
).
User.first.versions.last 2
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
PaperTrail::Version Load (0.3ms) SELECT "versions".* FROM "versions" WHERE "versions"."item_id" = ? AND "versions"."item_type" = ? ORDER BY "versions"."created_at" ASC, "versions"."id" ASC [["item_id", 1], ["item_type", "User"]]
=> [#<PaperTrail::Version id: 30, item_type: "User", item_id: 1, event: "update", whodunnit: nil, object: "---\nid: 1\nname: Name_0_update_second\ncreated_at: 2...", created_at: "2016-02-24 02:32:45", transaction_id: 30>, #<PaperTrail::Version id: 31, item_type: "User", item_id: 1, event: "update", whodunnit: nil, object: "---\nid: 1\nname: Name_0_3th\ncreated_at: 2015-12-30 ...", created_at: "2016-02-24 02:32:45", transaction_id: 30>]
Ta dễ dàng nhận thấy version
đầu tiên được tạo ra có id
và transaction_id
đều là 30
.
Có thể xem ở https://github.com/airblade/paper_trail/blob/master/lib/paper_trail/has_paper_trail.rb#L458
def set_transaction_id(version)
return unless self.class.paper_trail_version_class.column_names.include?('transaction_id')
if PaperTrail.transaction? && PaperTrail.transaction_id.nil?
PaperTrail.transaction_id = version.id
version.transaction_id = version.id
version.save
end
end
Việc gán theo id
của version
đầu tiên trong lượt submit được tạo ra luôn chắc chắn rằng transaction_id
ở 2 lượt submit khác nhau là không trùng nhau.
Muốn set transaction_id
cho version
sắp được tạo ra, ta chỉ việc gán PaperTrail.transaction_id
với gía trị mong muốn trước khi submit.
PaperTrail.transaction_id = 100
=> 100
irb(main):010:0> User.first.update name: "name1"
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
(0.1ms) begin transaction
SQL (0.3ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "name1"], ["updated_at", "2016-02-24 10:38:27.471171"], ["id", 1]]
SQL (0.2ms) INSERT INTO "versions" ("event", "object", "created_at", "transaction_id", "item_id", "item_type") VALUES (?, ?, ?, ?, ?, ?) [["event", "update"], ["object", "---\nid: 1\nname: name\ncreated_at: 2015-12-30 10:20:52.589033000 Z\nupdated_at: 2016-02-24 10:36:40.345223000 Z\n"], ["created_at", "2016-02-24 10:38:27.471171"], ["transaction_id", 100], ["item_id", 1], ["item_type", "User"]]
(153.2ms) commit transaction
Ta thấy transaction_id
của version
mới được tạo ra là 100
đúng với số được gán cho PaperTrail.transaction_id
trước đó. Tuy nhiên việc gán này làm cho ý nghĩa của transaction_id
trong việc đánh dấu những version
thuộc 1 lần submit không còn nữa.
Cảm ơn và hi vọng bài viết giúp ích phần nào trong công việc của bạn.
All rights reserved