Closed11

graphql-batchのメモ

2bo2bo

遅延読込の対象オブジェクトとメソッドの指定

lazy_resolve(loading-class, load-method)を実行すると、fieldのresolveがloading-classのインスタンスを返す場合に、そのインスタンスのload-methodが遅延実行される

https://graphql-ruby.org/schema/lazy_execution

そのため、graphql-batchでは::Promise#syncが遅延実行される

2bo2bo

::PromiseはRubyでJavaScriptのPromiseを実装したようなもので、Promises/A+仕様を満たしている

::Promise#syncはPromiseの処理が完了するまでまつ。(fullfilledかrejected)
なおsyncを使う場合#waitを実装する必要がある

https://github.com/lgierth/promise.rb#waiting-for-fulfillmentrejection

https://github.com/lgierth/promise.rb/blob/a5314555fee77e8e1f566738d56b50eda0fff506/lib/promise.rb#L85-L92

graphql-batchにおいてはGraphQL::Batch::Loader#waitが実行される

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L84-L91

GraphQL::Batch::Executor#resolveからLoaderのresovleが実行される
https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/executor.rb#L48-L54

GraphQL::Batch::Loader#resolveからperform(load_keys)が実行される
https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L65-L77

...中間を省略

GraphQL::Schema#sync_lazyが実行される。graphql-batchにおいてはvalueは::Promiseのインスタンス、lazy_methodは:syncになる

https://github.com/rmosolgo/graphql-ruby/blob/3a82807e07b244ec9e02a2be5a51598d561f64f5/lib/graphql/schema.rb#L1255-L1269

2bo2bo

メモ

Executorの@loadersは重複したキーでPromiseが作成されないようにしている

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/executor.rb#L41-L46

キーはLoader#で指定される

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L16-L18

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L6-L8

最終的にLoader#resolveが実行されるときには@loadersは利用されない。ただし、@loadersを設定すると同時にloaderのexecutorに設定されているため、結果として@loadersに入っているloaderに対してしかresolveは実行されない

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L87-L88

2bo2bo

以下が実行されたときにどういう処理がされるか

     Loaders::AssociationLoader.for(Post, :comments).load(object)

Loader.for

forで指定した引数がそのままLoaderのinitializeに渡されて、インスタンス生成する。
生成されたLoaderのインスタンスはexecutorの@loadersに格納される

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L6-L8

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/executor.rb#L40-L46

object(articleのインスタンス)がkeyとなりPromiseが生成されてcacheに格納される

https://github.com/Shopify/graphql-batch/blob/8f5b102abada9703617807cc32f68e96a7031fa2/lib/graphql/batch/loader.rb#L54-L59

cache_keyは Loaders::AssociationLoader#cache_key(key)でoverrideされている
queueにもobject(articleのインスタンス)が格納される

2bo2bo

execute_query_lazyで遅延実行がされるっぽい

https://github.com/rmosolgo/graphql-ruby/blob/109c26c5fa9118a9c69ed4c4823048c1ce1aea6d/lib/graphql/execution/interpreter.rb#L98-L100

    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
2bo2bo

最終的に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
このスクラップは1ヶ月前にクローズされました