Skip to content

Commit 1dd7dcc

Browse files
authored
Merge pull request rails#45440 from Shopify/association-proxy-load-async
Fix CollectionProxy#load_async
2 parents 8a91f72 + 4b121f6 commit 1dd7dcc

File tree

3 files changed

+63
-16
lines changed

3 files changed

+63
-16
lines changed

activerecord/lib/active_record/associations/collection_proxy.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@ def inspect # :nodoc:
11081108
].flat_map { |klass|
11091109
klass.public_instance_methods(false)
11101110
} - self.public_instance_methods(false) - [:select] + [
1111-
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
1111+
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
11121112
]
11131113

11141114
delegate(*delegate_methods, to: :scope)

activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def shared_cache? # :nodoc:
364364
end
365365

366366
def get_database_version # :nodoc:
367-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
367+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
368368
end
369369

370370
def check_version # :nodoc:

activerecord/test/cases/relation/load_async_test.rb

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22

33
require "cases/helper"
44
require "models/post"
5+
require "models/category"
56
require "models/comment"
67
require "models/other_dog"
78

89
module ActiveRecord
910
module WaitForAsyncTestHelper
1011
private
11-
def wait_for_async_query(relation, timeout: 5)
12-
if !relation.connection.async_enabled? || relation.instance_variable_get(:@records)
13-
return relation
14-
end
12+
def wait_for_async_query(connection = ActiveRecord::Base.connection, timeout: 5)
13+
return unless connection.async_enabled?
1514

16-
future_result = relation.instance_variable_get(:@future_result)
15+
executor = connection.pool.async_executor
1716
(timeout * 100).times do
18-
return relation unless future_result.pending?
17+
return unless executor.scheduled_task_count > executor.completed_task_count
1918
sleep 0.01
2019
end
20+
2121
raise Timeout::Error, "The async executor wasn't drained after #{timeout} seconds"
2222
end
2323
end
@@ -27,7 +27,7 @@ class LoadAsyncTest < ActiveRecord::TestCase
2727

2828
self.use_transactional_tests = false
2929

30-
fixtures :posts, :comments
30+
fixtures :posts, :comments, :categories, :categories_posts
3131

3232
def test_scheduled?
3333
deferred_posts = Post.where(author_id: 1).load_async
@@ -52,6 +52,48 @@ def test_reset
5252
assert_not_predicate deferred_posts, :scheduled?
5353
end
5454

55+
unless in_memory_db?
56+
def test_load_async_has_many_association
57+
post = Post.first
58+
59+
defered_comments = post.comments.load_async
60+
assert_predicate defered_comments, :scheduled?
61+
62+
events = []
63+
callback = -> (event) do
64+
events << event unless event.payload[:name] == "SCHEMA"
65+
end
66+
67+
wait_for_async_query
68+
ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
69+
defered_comments.to_a
70+
end
71+
72+
assert_equal [["Comment Load", true]], events.map { |e| [e.payload[:name], e.payload[:async]] }
73+
assert_not_predicate post.comments, :loaded?
74+
end
75+
76+
def test_load_async_has_many_through_association
77+
post = Post.first
78+
79+
defered_categories = post.scategories.load_async
80+
assert_predicate defered_categories, :scheduled?
81+
82+
events = []
83+
callback = -> (event) do
84+
events << event unless event.payload[:name] == "SCHEMA"
85+
end
86+
87+
wait_for_async_query
88+
ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
89+
defered_categories.to_a
90+
end
91+
92+
assert_equal [["Category Load", true]], events.map { |e| [e.payload[:name], e.payload[:async]] }
93+
assert_not_predicate post.scategories, :loaded?
94+
end
95+
end
96+
5597
def test_notification_forwarding
5698
expected_records = Post.where(author_id: 1).to_a
5799

@@ -66,7 +108,8 @@ def test_notification_forwarding
66108
end
67109
end
68110

69-
deferred_posts = wait_for_async_query(Post.where(author_id: 1).load_async)
111+
deferred_posts = Post.where(author_id: 1).load_async
112+
wait_for_async_query
70113

71114
assert_equal expected_records, deferred_posts.to_a
72115
assert_equal Post.connection.supports_concurrent_connections?, status[:async]
@@ -92,7 +135,8 @@ def test_simple_query
92135
end
93136
end
94137

95-
deferred_posts = wait_for_async_query(Post.where(author_id: 1).load_async)
138+
deferred_posts = Post.where(author_id: 1).load_async
139+
wait_for_async_query
96140

97141
assert_equal expected_records, deferred_posts.to_a
98142
assert_equal Post.connection.supports_concurrent_connections?, status[:async]
@@ -130,7 +174,8 @@ def test_eager_loading_query
130174
end
131175
end
132176

133-
deferred_posts = wait_for_async_query(Post.where(author_id: 1).eager_load(:comments).load_async)
177+
deferred_posts = Post.where(author_id: 1).eager_load(:comments).load_async
178+
wait_for_async_query
134179

135180
if in_memory_db?
136181
assert_not_predicate deferred_posts, :scheduled?
@@ -355,7 +400,8 @@ def test_simple_query
355400
end
356401
end
357402

358-
deferred_posts = wait_for_async_query(Post.where(author_id: 1).load_async)
403+
deferred_posts = Post.where(author_id: 1).load_async
404+
wait_for_async_query
359405

360406
assert_equal expected_records, deferred_posts.to_a
361407
assert_equal Post.connection.supports_concurrent_connections?, status[:async]
@@ -388,7 +434,8 @@ def test_eager_loading_query
388434
end
389435
end
390436

391-
deferred_posts = wait_for_async_query(Post.where(author_id: 1).eager_load(:comments).load_async)
437+
deferred_posts = Post.where(author_id: 1).eager_load(:comments).load_async
438+
wait_for_async_query
392439

393440
assert_predicate deferred_posts, :scheduled?
394441

@@ -497,8 +544,8 @@ def test_simple_query
497544
deferred_posts = Post.where(author_id: 1).load_async
498545
deferred_dogs = OtherDog.where(id: 1).load_async
499546

500-
wait_for_async_query(deferred_posts)
501-
wait_for_async_query(deferred_dogs)
547+
wait_for_async_query
548+
wait_for_async_query
502549

503550
assert_equal expected_records, deferred_posts.to_a
504551
assert_equal expected_dogs, deferred_dogs.to_a

0 commit comments

Comments
 (0)