为什么提高ActiveRecord :: Rollback不会删除在另一个线程中创建的记录
代码:
namespace :import do
task test: :environment do
Amchart.delete_all # just to make reproduceable runs
ActiveRecord::Base.transaction do
Thread.new do
sleep 0.1
puts 'Action in thread 1'
Amchart.create!(js: 'a', html: 'a', title: 'a')
end.join
sleep 0.2
puts 'Action in main thread'
raise ActiveRecord::Rollback
end
puts "Amchart.count = #{Amchart.count}" # prints 1, should be 0
end
end
我做什么
$ rake import:test
D, [2018-01-25T19:39:39.746612 #1] DEBUG -- : SQL (0.5ms) DELETE FROM "amcharts"
D, [2018-01-25T19:39:39.747092 #1] DEBUG -- : (0.2ms) BEGIN
Action in thread 1
D, [2018-01-25T19:39:39.880325 #1] DEBUG -- : (0.4ms) BEGIN
D, [2018-01-25T19:39:39.883673 #1] DEBUG -- : SQL (0.9ms) INSERT INTO "amcharts" ("js", "html", "title", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["js", "a"], ["html", "a"], ["title", "a"], ["created_at", "2018-01-25 19:39:39.881157"], ["updated_at", "2018-01-25 19:39:39.881157"]]
D, [2018-01-25T19:39:39.888244 #1] DEBUG -- : (1.9ms) COMMIT
Action in main thread
D, [2018-01-25T19:39:40.089325 #1] DEBUG -- : (0.4ms) ROLLBACK
D, [2018-01-25T19:39:40.091474 #1] DEBUG -- : (0.9ms) SELECT COUNT(*) FROM "amcharts"
Amchart.count = 1
答案 0 :(得分:1)
线程使用不同的数据库连接。您没有提及您的数据库,但在默认的PostgreSQL安装中,每个连接都会隔离事务。
# frozen_string_literal: true
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "activerecord", "5.1.3"
gem "pg", "< 1.0"
end
require "active_record"
require "minitest/autorun"
require "logger"
# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
ActiveRecord::Base.establish_connection(
adapter: 'postgresql',
database: 'test_connections',
username: <username>,
password: <password>,
host: 'localhost',
pool: 5,
timeout: 5000
)
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :authors, force: :true do |t|
end
end
class Author < ActiveRecord::Base
end
class BugTest < Minitest::Test
def test_connections
Author.delete_all
puts "Main thread connection id: #{ActiveRecord::Base.connection.object_id}"
ActiveRecord::Base.transaction do
Author.create!
Thread.new do
sleep 0.1
puts 'Action in thread 1'
puts "Child thread connection id: #{ActiveRecord::Base.connection.object_id}"
Author.create!
end.join
sleep 0.2
puts 'Action in main thread'
raise ActiveRecord::Rollback
end
assert_equal 0, Author.count
end
end
运行此测试会给出
$ ruby test_connection.rb
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Using concurrent-ruby 1.0.5
Using i18n 0.9.3
Using minitest 5.11.2
Using thread_safe 0.3.6
Using tzinfo 1.2.4
Using activesupport 5.1.3
Using activemodel 5.1.3
Using arel 8.0.0
Using activerecord 5.1.3
Using bundler 1.16.1
Using pg 0.21.0
-- create_table(:authors, {:force=>:true})
D, [2018-01-25T22:38:25.564985 #45697] DEBUG -- : (4.1ms) DROP TABLE IF EXISTS "authors"
D, [2018-01-25T22:38:25.568347 #45697] DEBUG -- : (2.6ms) CREATE TABLE "authors" ("id" bigserial primary key)
-> 0.0247s
D, [2018-01-25T22:38:25.600449 #45697] DEBUG -- : ActiveRecord::InternalMetadata Load (0.5ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
D, [2018-01-25T22:38:25.605078 #45697] DEBUG -- : (0.2ms) BEGIN
D, [2018-01-25T22:38:25.606997 #45697] DEBUG -- : (1.2ms) COMMIT
Run options: --seed 6953
# Running:
D, [2018-01-25T22:38:25.615613 #45697] DEBUG -- : SQL (0.5ms) DELETE FROM "authors"
Main thread connection id: 70288221049540
D, [2018-01-25T22:38:25.615908 #45697] DEBUG -- : (0.1ms) BEGIN
D, [2018-01-25T22:38:25.620995 #45697] DEBUG -- : SQL (0.4ms) INSERT INTO "authors" DEFAULT VALUES RETURNING "id"
Action in thread 1
Child thread connection id: 70288224727480
D, [2018-01-25T22:38:25.740323 #45697] DEBUG -- : (0.1ms) BEGIN
D, [2018-01-25T22:38:25.743468 #45697] DEBUG -- : SQL (2.5ms) INSERT INTO "authors" DEFAULT VALUES RETURNING "id"
D, [2018-01-25T22:38:25.745825 #45697] DEBUG -- : (2.1ms) COMMIT
Action in main thread
D, [2018-01-25T22:38:25.949138 #45697] DEBUG -- : (0.3ms) ROLLBACK
D, [2018-01-25T22:38:25.950323 #45697] DEBUG -- : (0.6ms) SELECT COUNT(*) FROM "authors"
F
Finished in 0.337003s, 2.9673 runs/s, 2.9673 assertions/s.
1) Failure:
BugTest#test_connections [test_connection.rb:64]:
Expected: 0
Actual: 1
1 runs, 1 assertions, 1 failures, 0 errors, 0 skips
请注意,ActiveRecord::Base.connection
之间的object_ids不同。
当主线程回滚时子线程INSERT和COMMIT。因此,在transaction
块退出后,一个Author仍保留在数据库中。