graphql-batchのメモ

version graphql-batch-0.5.3

use GraphQL::Batch
GraphQL::Schema.use
は指定されたpluginのuserメソッドを呼ぶため以下が実行される。

遅延読込の対象オブジェクトとメソッドの指定
lazy_resolve(loading-class, load-method)
を実行すると、fieldのresolveがloading-class
のインスタンスを返す場合に、そのインスタンスのload-method
が遅延実行される
そのため、graphql-batchでは::Promise#sync
が遅延実行される

::Promise
はRubyでJavaScriptのPromiseを実装したようなもので、Promises/A+
仕様を満たしている
::Promise#sync
はPromiseの処理が完了するまでまつ。(fullfilledかrejected)
なおsyncを使う場合#wait
を実装する必要がある
graphql-batchにおいてはGraphQL::Batch::Loader#wait
が実行される
GraphQL::Batch::Executor#resolve
からLoaderのresovle
が実行される
GraphQL::Batch::Loader#resolve
からperform(load_keys)
が実行される
...中間を省略
GraphQL::Schema#sync_lazy
が実行される。graphql-batchにおいてはvalueは::Promise
のインスタンス、lazy_methodは:sync
になる

参考

Loaders::AssociationLoaderの使用例
module Types
class ArticleType < Types::BaseObject
def comments
Loaders::AssociationLoader.for(Post, :comments).load(object)
end
end
end

メモ
Executorの@loaders
は重複したキーでPromiseが作成されないようにしている
キーはLoader#で指定される
最終的にLoader#resolveが実行されるときには@loaders
は利用されない。ただし、@loadersを設定すると同時にloaderのexecutorに設定されているため、結果として@loaders
に入っているloaderに対してしかresolveは実行されない

executorのインスタンスはスレッド固有データに格納される

以下が実行されたときにどういう処理がされるか
Loaders::AssociationLoader.for(Post, :comments).load(object)
Loader.for
forで指定した引数がそのままLoaderのinitializeに渡されて、インスタンス生成する。
生成されたLoaderのインスタンスはexecutorの@loadersに格納される
object(articleのインスタンス)がkeyとなりPromiseが生成されてcacheに格納される
cache_keyは Loaders::AssociationLoader#cache_key(key)
でoverrideされている
queueにもobject(articleのインスタンス)が格納される

execute_query_lazy
で遅延実行がされるっぽい
14: def self.resolve_each_depth(lazies_at_depth, dataloader)
15: depths = lazies_at_depth.keys
16: depths.sort!
17: next_depth = depths.first
18: if next_depth
19: lazies = lazies_at_depth[next_depth]
=> 20: lazies_at_depth.delete(next_depth)
21: if lazies.any?
22: dataloader.append_job {
23: lazies.each(&:value) # resolve these Lazy instances
24: }
25: # Run lazies _and_ dataloader, see if more are enqueued
26: dataloader.run
27: resolve_each_depth(lazies_at_depth, dataloader)
28: end
29: end
30: nil
31: end
[8] pry(GraphQL::Execution::Interpreter::Resolve)> lazies
=> [#<GraphQL::Execution::Lazy:0x0000000115263750
@field=#<Types::BaseField CompaniesWork.developmentSkills: [DevelopmentSkill!]!>,
@get_value_func=#<Proc:0x0000000114927df8 /Users/kota/workspace/Gravide/vendor/bundle/ruby/3.2.0/gems/graphql-2.1.6/lib/graphql/execution/interpreter/runtime.rb:783>,
@resolved=false>,
#<GraphQL::Execution::Lazy:0x0000000115263070
@field=#<Types::BaseField CompaniesWork.developmentLanguages: [DevelopmentLanguage!]!>,
@get_value_func=#<Proc:0x0000000114925d50 /Users/kota/workspace/Gravide/vendor/bundle/ruby/3.2.0/gems/graphql-2.1.6/lib/graphql/execution/interpreter/runtime.rb:783>,
@resolved=false>,
#<GraphQL::Execution::Lazy:0x0000000115262df0
@field=#<Types::BaseField CompaniesWork.developmentSkills: [DevelopmentSkill!]!>,
@get_value_func=#<Proc:0x0000000114925008 /Users/kota/workspace/Gravide/vendor/bundle/ruby/3.2.0/gems/graphql-2.1.6/lib/graphql/execution/interpreter/runtime.rb:783>,
@resolved=false>,
#<GraphQL::Execution::Lazy:0x0000000115262c10
@field=#<Types::BaseField CompaniesWork.developmentLanguages: [DevelopmentLanguage!]!>,
@get_value_func=#<Proc:0x000

最終的にsync_lazyが実行される。valueはPromise, lazy_methodがsync,synced_valueがpromiseの結果解決した値が入る
From: /Users/kota/workspace/Gravide/vendor/bundle/ruby/3.2.0/gems/graphql-2.1.6/lib/graphql/schema.rb:1281 GraphQL::Schema.sync_lazy:
1275: def sync_lazy(value)
1276: lazy_method = lazy_method_name(value)
1277: pp "sync_lazy #{value} with #{lazy_method}"
1278: if lazy_method
1279: synced_value = value.public_send(lazy_method)
1280: binding.pry
=> 1281: sync_lazy(synced_value)
1282: else
1283: binding.pry
1284: value
1285: end
1286: end