-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
I recently added async data loader to my rails application as mentioned here https://graphql-ruby.org/dataloader/async_dataloader and set a fiber_limit in hope to restrict how many database connections might be checked out simultaneously (I also increased my pool size after I had some errors where database connections could not be checked out within the limit)
However I could use some guidance on setting the fiber_limit value other than the minimum value of 4. I noticed that it is not 1:1 with the number of dataloader sources that can run, and when set too low it seems to unpredictably change a single batch load into n+1 queries that run serially.
class CustomAsyncDataloader < GraphQL::Dataloader::AsyncDataloader
def get_fiber_variables
vars = super
vars[:connected_to] = {
role: ActiveRecord::Base.current_role,
shard: ActiveRecord::Base.current_shard,
prevent_writes: ActiveRecord::Base.current_preventing_writes,
}
vars
end
def set_fiber_variables(vars)
connection_config = vars.delete(:connected_to)
ActiveRecord::Base.connecting_to(**connection_config)
super
end
enduse CustomAsyncDataloader, fiber_limit: ENV.fetch('RAILS_GQL_FIBER_LIMIT', 10).to_iclass Types::ViewerType < Types::BaseObject
field :example_field, String do
argument :key, String, required: false
end
def example_field(key: 'default')
dataloader.with(Sources::SlowSource, key).load(SecureRandom.uuid)
end
endquery {
viewer {
default: exampleField
# simulate loading the same nested source for a connection type
b1result1: exampleField(key: "batch1")
b1result2: exampleField(key: "batch1")
b1result3: exampleField(key: "batch1")
b1result4: exampleField(key: "batch1")
b1result5: exampleField(key: "batch1")
b1result6: exampleField(key: "batch1")
b1result7: exampleField(key: "batch1")
b1result8: exampleField(key: "batch1")
b1result9: exampleField(key: "batch1")
b1result10: exampleField(key: "batch1")
b1result11: exampleField(key: "batch1")
b1result12: exampleField(key: "batch1")
b1result13: exampleField(key: "batch1")
b1result14: exampleField(key: "batch1")
b1result15: exampleField(key: "batch1")
}
}I would expect that this performs 2 queries: one for the "default" batch and 1 for "batch1"
But instead we see this:
00:00:35.373351 Rails -- [default] Fetching ids: ["56015c91-35d3-4843-a194-f9c52c24a0ef"]
00:00:35.375195 Rails -- [batch1] Fetching ids: ["64acf353-cf27-4e93-a46d-364196bc5f13", "2521c561-9183-42cc-9ee5-bdefa5862388", "4db4d60e-8639-4978-bf7b-1b64b2894bd7", "98ade717-5e1d-4e77-8272-46edf771d050", "e64be3b6-6c7b-4789-8ec6-156d04cae73e", "bfd59cc0-0f23-4db2-83de-0191f212e220"]
00:00:37.378597 ActiveRecord::Base -- (2004.4ms) select pg_sleep(2)
00:00:37.409277 ActiveRecord::Base -- (2033.3ms) select pg_sleep(2)
00:00:37.412677 Rails -- [batch1] Fetching ids: ["270cd2e4-eb26-4e21-b9db-1b923058f7c5", "f9c17bdc-47ad-4f97-8b69-dcd142748787", "011c1986-42b9-4b36-9864-a3e16cbe3fe0", "61cb7178-5c30-4fac-a95b-bd534a89a9d2", "74c93d5f-1d3b-4e47-865e-1024ba97a51d", "c11259e8-48b2-4912-a4d9-ff05d89259fa", "080040cd-09b2-456a-8214-a5faac750765"]
00:00:39.442014 ActiveRecord::Base -- (2028.7ms) select pg_sleep(2)
00:00:39.444284 Rails -- [batch1] Fetching ids: ["b9caade4-cba1-4e08-a47e-a5ef8325d95b", "6ce90e58-af0c-46a4-b486-5f3bd319d549"]
00:00:41.463919 ActiveRecord::Base -- (2019.0ms) select pg_sleep(2)
The first batch for "default" works as expected but the second "batch" is split into 3 different groups which don't complete until the previous completed.
Increasing the fiber_limit makes this work as expected. I'm guessing this needs to be in M*N minimum to work well with M sources that might be batch loading N items?
Using:
graphql-pro (1.29.14)
graphql (2.5.16)
async (2.24.0)
rails (7.2.x)