Shopify/graphql-batch がどのように batching するのかを追う
この記事は GraphQL Advent Calendar 2020 4日目の記事です。
前回の記事は @qsona さんの 動的型付け言語での GraphQL Client でした。
題の通り、Shopify/graphql-batch がどのようにリクエストを batching するのかをコードを読みつつ追っていきます。
時間の都合で、この記事ではシンプルな単一の Query のみ(Mutation でない)を実行した場合を対象とします。また Loader については README に記載されている RecordLoader の実装をそのまま使います。
3行まとめ
- graphql-ruby の lazy-loading class として Promise を使う
Loader.for(Klass).load(object.klass_id)
が呼ばれるたびに Loader に Promise を登録する- Query にある遅延評価しない field を全て解決した後、Promise を解決する
コードリーディングに使ったサンプルコード
require 'bundler/inline' require 'logger' gemfile do source 'https://rubygems.org' gem 'graphql' gem 'graphql-batch' gem 'activerecord', require: 'active_record' gem 'sqlite3' # gem 'pry-byebug' end ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :courses, force: true do |t| t.references :textbook end create_table :textbooks, force: true do |t| end end class Course < ActiveRecord::Base belongs_to :textbook end class Textbook < ActiveRecord::Base has_many :courses end textbook1 = Textbook.create! textbook2 = Textbook.create! textbook3 = Textbook.create! Course.create!(textbook: textbook1) Course.create!(textbook: textbook2) Course.create!(textbook: textbook3) class RecordLoader < GraphQL::Batch::Loader def initialize(model) @model = model end def perform(ids) @model.where(id: ids).each { |record| fulfill(record.id, record) } ids.each { |id| fulfill(id, nil) unless fulfilled?(id) } end end class TextbookType < GraphQL::Schema::Object field :id, ID, null: false end class CourseType < GraphQL::Schema::Object field :id, ID, null: false field :textbook, TextbookType, null: false def textbook RecordLoader.for(Textbook).load(object.textbook_id) end end class QueryType < GraphQL::Schema::Object field :courses, [CourseType], null: false def courses Course.all end end class MySchema < GraphQL::Schema query QueryType use GraphQL::Batch end result = MySchema.execute(<<~GQL) query Courses { courses { id textbook { id } } } GQL pp result.as_json
1. graphql-ruby の lazy-loading class として Promise を使う
- この辺り: https://github.com/Shopify/graphql-batch/blob/7c7e3016923f9aa346d0cd3b9be40f85bf61a3a1/lib/graphql/batch.rb#L18-L43
- Lazy Execution に関するドキュメントはこちら: GraphQL - Lazy Execution
MySchema
が参照されるとGraphQL::Batch.use
が実行される- この中で、
GraphQL::Batch::SetupMultiplex
を Multiplex instrumentation の instrumenter として登録し、また lazy-loading class としてPromise
(promise.rb) を、値を取得するメソッドとして:sync
を登録する
- この中で、
2. Loader.for(Klass).load(object.klass_id)
が呼ばれるたびに Loader に Promise を登録する
- この辺り: https://github.com/Shopify/graphql-batch/blob/7c7e3016923f9aa346d0cd3b9be40f85bf61a3a1/lib/graphql/batch/loader.rb#L47-L52
textbook
field を resolve した際の返り値としてはインスタンス変数@source
にRecordLoader
のオブジェクトを持つPromise
のオブジェクト(文字で表すとややこしい)となる
3. Query にある遅延評価しない field を全て解決した後、Promise を解決する
- この辺り: https://github.com/rmosolgo/graphql-ruby/blob/ea571fd8db2b20f46299313c544ba0690869df28/lib/graphql/execution/multiplex.rb#L82-L87
Promise
オブジェクトを返す field はGraphQL::Execution::Lazy
としてハンドリングされ、次実行対象の Query(複数あるときはそれらに含まれる全てっぽい)にある Lazy でない field を resolve するフェイズのあとに resolve される- Loader に cache していた Promise の object を sync するのは
multiplex.schema.query_execution_strategy.finish_multiplex(results, multiplex)
から進んでいったここ: https://github.com/rmosolgo/graphql-ruby/blob/ea571fd8db2b20f46299313c544ba0690869df28/lib/graphql/schema.rb#L123 promise.sync
が実行されるとGraphQL::Batch::Loader.resolve
が実行され、RecordLoader
に実装していたperform
が実行される
所感
思ったより理解に時間がかかり(そしてまだ十分に理解できていない)複雑度に対してだいぶラフなまとめになってしまったのですが、graphql-batch がどのように batching するか、また graphql-ruby についてもその実装の一部を理解することができ便利でした。
そしてコードリーディングってスコープを決めてやらないと一生終わらないですよね。楽しい。
『自分でやった方が早い病』を読んだ
タイトルと "孤独な成功者" というキーワードに思い当たるところがあり読んだ。
- 作者:小倉 広
- 発売日: 2012/05/25
- メディア: 新書
僕は相手に頼み事をするのが苦手(年々改善されてはいるが)で、これは思いやりではなくエゴだよなーと思っているのだけど、本の中でも「要求を遠慮するのは信頼関係が無いから」ということが書かれていて、そうだよな、となった。
僕はこの問題を「今解決しようとしている問題は同僚にとっても解決すべき問題なはずなので僕の頼み事を渋ることがあればその人は仕事をする気がないと言えるのでは」のような責任転嫁によって改善しているフシがあり、根本的な解決に向かっていないので意識を改めようと思いました…
また「仕事を任せることと仕事をふることは違う」の話は思い当たる具体的な失敗経験があり少し頭が痛くなった。半年前くらいに仕事でプロジェクトの開発側のリードをやったのだけど、今にして思うと失敗を過度に恐れるあまり不安要素ばかりに目を向け、チームメンバーを十分に信頼をせずに仕事をふることばかりをしていた。リードというポジションに気負いすぎていた気もする。
「してあげる幸せ」の話は『嫌われる勇気』でも「人は自分が役に立っていると実感できたときのみ自分の価値を実感できる」として書かれていた。これも僕は下手(自己肯定感を得づらい)なのだけど、これを書く中で、下手な原因は自己受容ができていなことにある気がしてきた。
以下は読書メモ。
- 小倉広 著
- ( ) で括られているアイテムは個人の感想
はじめに
- 自分がやったほうが早い病とは?
- あるタスクについて「自分がやったほうが早いな」と思ってしまうこと
- 自分のほうがよくできる
- 相手に頼むのは悪い
- あるタスクについて「自分がやったほうが早いな」と思ってしまうこと
- 自分がやったほうが早い病は新人リーダーに多い
- 自分がやったほうが早い病の個人・周辺への弊害
- 個人に業務が集中する
- 業務の停滞
- バス因子化
- 業務が奪われることによる周囲のモチベーションの低下
- 健康を損なう
- 個人に業務が集中する
- 自分がやったほうが早い病を克服すると
- 周囲・自身ともに成長できる
- 心身が健康になる
- 孤独な老後を過ごさずに済む
- ...
第1章 病が進行すると「孤独な成功者」になる
- 「自分がやったほうが早い」の思考の本質は「自分1人でも成功すれば良い」「自分さえ何とかなればよい」
- 頼み事ができないのは周囲を信頼していない・周囲から信頼されていない証拠
打ち上げもひとり家で缶ビールを開けることになるでしょう。
つらい
- 罹患者は仕事を抱え込むので体調を崩し仕事のクオリティが下がる
- ランニングはいいぞ
- 罹患者は周囲に不満が募るのでストレスを抱えた状態が続く
- ちゃんと仕事しない、評価してくれない、優秀な部下がつかない....
- 筆者はリクルートにいたときこういう状態で鬱を患ってしまった
- 自身を過大評価し、うまくいかない原因を他人に求め自身を肯定する
- 気づくと周りに人がいない
- 周囲は自身が必要とされていないと感じるので離れていく
- "仕事をふる" と "仕事を任せる" は違う
- 伸びてる会社の社長は「忙しい」と言わずいつも余裕がありニコニコしてる
社長やリーダーが徹夜したり、休日出勤したりしてせかせかしているような会社はどこかに問題があると思って間違いないでしょう。
経験談?
- 「まわりは使えない」と思っている人ほど「使えない」と思われている
- 上司が老害化するとズレたセンスの仕事を手伝わされる
- 今いるメンバーが現時点でのベストメンバー
- 罹患者の典型的症状の1つが周囲の悪口を言うこと
- 周囲のレベルは自分のレベル
- 優秀な人が集まらないのはあなたが優秀なリーダー的存在でないから
- 罹患者は周囲との信頼関係が希薄
- 同僚に厳しい要求ができないのは信頼関係が築けていないから
- 「遠慮」はごまかし
- 同僚に厳しい要求ができないのは信頼関係が築けていないから
- 年賀状が届かなくなるし葬式に誰も来ない
- (既に親族と業者以外から年賀状届かないな…)
第2章 病を克服すると「幸せな成功者」になれる
- 脱バス因子
- 組織の安定、業績の安定、メンバーの心の安定
- 会社経営の2つの車輪
- 外部環境適応と内部組織統合
- どちらについても個人が100の成果を出すより100人が1の成果を出していく体制の方がよく回る
- 病を克服すると全員が成長するので大きな仕事ができるようになる
- 個人の能力には限界がある
友達もお金も増える!昇進、昇給できる!
- 働くこと自体が幸せなことと思えるようになる
- 著者の経験として、自分が褒められることを目的に仕事をしたら自尊心はくすぐられたが幸せにはならなかった
- 損得勘定をし、褒められるのに必要なスコープでのみ仕事をしていた
- そうでなく、相手への貢献をスコープの基準にして本来の担当領域をもやや超えて仕事をしたところ、心の充足を得られた
- (この辺業務の成果について触れられていないので自己満足ではとも読めてしまうな(成果について良かったから書いてるんだとは思うけど))
第3章 病の根本にある「自分さえよければ」という考え方
- 「自己主義」が根本にある
- 「情けは人の為ならず」
情けを人にかけておけば、巡り巡って自分によい報いが来るということ
(大辞林 第三版)- 著者は「自分に戻ってくるかどうかに関係なく、人に優しくしたら、その瞬間に自分の心の満足が得られる」と解釈している
- 心の充足を得るには自己的で自己完結な振る舞いをするのではなく、自分の利益は置いておき相手が求めていること行う
- 人間の3つの幸せ by 鍵山秀三郎(イエローハット創業者)
- してもらう幸せ、自分でできる幸せ、してあげる幸せ
- 成長する段階として次第に右へ進む。幸せの大きさも右に行くほど大きくなる
- 「自分でやった方が早い」の思考は「自分でできる幸せ」の段階
- 「してもらう幸せと」「自分でできる幸せ」は利己的だが、「してあげる幸せ」は利他的
- 利己的な行動は他社と幸せを奪い合うが、利他的な行動は全体が自分を含めた全体が幸せになる
- 「孤独な成功者」は「自分でできる幸せ」しか知らない
- 利他主義な仕事の任せ方
- 例:部下の苦手(= 伸ばしたい)分野の業務を任せ成長を促す
- 自分だったら折れそう。相手のタフネスを見て得意だったりやりたい分野の業務も織り交ぜないと駄目では
- 人に動いてもらったり人を育てるのは辛くて時間がかかるので辛抱強く相手を信じてやっていく他ない
- 1度や2度任せて成果で判断、は早計
- 例:部下の苦手(= 伸ばしたい)分野の業務を任せ成長を促す
- プレイヤーとして目先の成果をあげることからリーダーとして仲間を成長させることへのシフト
- 短期的な成果から長期的な成果に価値をずらすということ
- 長期的な視点がない人は周囲に対して妙に優しくなる
- 相手のことを考えているようで、仲間との衝突をただ避けたい利己主義なことが多い
- 相手のためにはときには厳しいことを言う必要がある。そのためには信頼関係が必要
- 「小善は大悪に似たり 大善は非情に似たり」by 稲盛和夫(京セラ創業者)
- 目先のことだけを考え相手を気持ちよくさせることは悪いことであり、長い目で見て人を育てることは厳しくすること
- 人を育てるには非情にならざるを得ない
- (僕はそうは思わないけど)
- 厳しい要求をするほどバックアップは大変になる
- 部下に厳しくすると自分も厳しくなる
- 子供の教育に必要なたった3つのこと by 森信三
- 朝挨拶をさせること、靴を揃えて椅子を入れること(親が)、親が呼んだ時に "はい" と返事をさせること
- 上司が模範を背中で示すことが最上級のリーダーシップ
- トータルパワー一定の法則
- 会議で上司が99%しゃべったら部下はしゃべれない
- 引きこもりの子供に対しては親のパワーバランスを下げて、余白を徐々に子供に埋めさせる
- 部下を育てるというマネージャーの喜びは経験してみないと分かりづらい
- だから新人マネージャーは自分でやった方が早い病になる
- マネージャーになり部下を育てることで得られる「してあげる幸せ」はエースピッチャーとして得られる「自分でできる幸せ」よりも遥かに大きい
- 「任せる」についての3つの勘違いしやすいポイント
- 任せることは失敗が前提
- 丸投げではない
- 担当させるが途中経過はちゃんと見るしすぐに助けられる体制は整えておく
- 他人に任せても楽にはならない
- 仕事を任せても最初は必ず仕事量が増える
- 「してあげる幸せ」を感じつつ、辛抱強くやっていく
- 罹患者は「自分大好き人間」
- 無知の知(ソクラテス)
- 自身が物を知らないということを知らないと無根拠に自信家な人になり自分がやったほうが早い病になる
第4章 「自分でやった方が早い病」への処方箋
- 一度徹底的に利己的になり痛い目にあう
- 悩みの量と気付きの量はイコール
- (確かに鬱になって1日中考え事をしていた頃が一番成長した気がする)
- 道具を持たずに災害地支援ボランティアに参加する学生の話
- 悩みの量と気付きの量はイコール
- 上司のジレンマと戦う覚悟を決める
- 短期的な成果と長期的な成果の両方を追い求めるということ
- 上司は部下よりも仕事が多いということを受け入れる
- 成果が求められたら自分が頑張る、そうでないときは部下に仕事を任せる、を振り子のようにやっていく
- 「任せる」とは「失敗させる権利を与えること」
吐くくらいのプレッシャー、胃が痛くなるくらいのプレッシャーの中で実践を積むからこそ、部下は成長するのです。
- (気軽にやると人が死にそう)
部下一人でやらせて、何度も失敗を繰り返さなければ、成長にはつながらないのです。
- (これはわかる)
失敗しないために、自分の頭で考えた工夫を試行錯誤します。それが部下の血となり肉となっていくのです。
- (失敗したら答えを出した上でどうしたらその答えにたどり着けるかを更に教えたり考えさせたりするのが良いと思っている派)
- まわりの人をヒーローにする
- (読んでいてどこか腑に落ちない)
- (「リーダーとはそういうことではない、自分で考えろ」は投げやりすぎでは)
- (優秀なメンバーが他のメンバーの成長にコミットするとチーム生産性が上がる、というのは良い話だと思う)
- (読んでいてどこか腑に落ちない)
- 「任せる」は「仕事をふる」ではない
- 仕事をふる:ディレクションは自分がやって作業の一部をお願いする
- 任せる:仕事と責任をセットで任せる
- できないうちにやらせるとできるようになる
- 日々の仕事を 120% でこなすことで初めて責任感は認めてもらえる
- 任せた仕事にどうしても口出ししたいときは「これは独り言だけど…」のように「つぶやき戦術」をとる
- (これは普通に口出ししてるのと変わらないでしょ…)
- 仕事を任せるとき、PDCA の PCA はサポートする
- 罹患者は D をやってしまいその他をやらない
- 著者の経験として、面談は会議室よりコーヒーショップの方が部下からよく意見が出る
- (良いかもしれない(相手がコーヒー好きなら))
- あえて70点のマニュアルを作る
- 30点分、作業者が考える余地を残す
- (そこで挑戦させる?という気もする)
第5章 「自分でやった方が早い病」が再発しないために
- 我慢することでは治らない。自分が変わる必要がある
- 仕事とプライベートは不可分であり、仕事において良きリーダーとなるためには良き人にならねばならない
- 社内では酷い上司と見られているがお客様には尽くします、はすぐバレる
- (店内で新人を酷く叱るラーメン屋っぽい)
- 社内では酷い上司と見られているがお客様には尽くします、はすぐバレる
- 他人を変えるのではなく自分が変わる
過去と他人は変えることができません。
私の体験上、人が心の中から変わるためには、10年はかかります。
渡しの場合は10年かかりましたが、もっと早い人もいるでしょう。ただ、すぐには変われないということは断言できます。
- 心と行動の両面から変えていく
悲しいから泣くのではなく、泣くから悲しいーー。
- 気持ちが落ち込んでいるときは上を向く、苦しいときは無理してでも笑顔を作る
- (心理学的な裏付けが欲しい。論文ありそう)
- 心を変えれば行動が変わる
- 行動を変えれば心が変わる
- 部下に任せるだけじゃなく自分が変わる
- 柔道家の多くは意味が分からず礼をしているが、1万回2万回と行うと意味がわかってくるという話
- 鉢植えに水をやると我欲がなくなる
- 鉢植えに水をやるという利他行動に対して、自身の食事という我欲を優先させてしまう
- 水やりに意味をもたせることで自身の人としての成長を促す行為に変えた
- 親に呼ばれたら "はい" と返事をするのと同じ
- 面倒くささを捨てて相手を受け止めるということ
- 鉢植えに水をやるという利他行動に対して、自身の食事という我欲を優先させてしまう
- 自身が変わるためには師を見つけるのが良い
- (「あの人ならどう考えるか」という視点を持つことはしばしばある。これは師か)
- 師の師、師を師とする人を辿ると多くの師が見つかる
森信三先生は「学問の中で生き方につながらない学問は一切無駄であるといったことをおっしゃっています。
- (僅かにでも生き方につながらない学問は存在するのだろうか?つながる・つながらないは主観評価するものなのだとしたら、数学はつながらないので一切無駄とする人が多くいそう)
身近にいると、その人の嫌な部分しか目に入らないですし、その人のすごい部分に気づかないのです。
- (嫌な部分も目に入るというのはそうだけど、すごい部分にも近いほうが気づくのでは?富士のように遠くから見える凄さはなんとなくそう見える、というだけでは)
- 師が見つけられないのはこちら側に問題がある
- 自身が人として幼いと相手の嫌な部分を見るとがっかりする
自分にも欠点がいっぱいある、自分にも至らないことがたくさんあると気づいたら、他人の欠点や至らなさも許せるようになるのです。
- 自分の欠点や至らなさに気づいていないのが罹患者の特徴
我以外、みな師なり
- 陰徳を積むことで人として成長する
- 言いふらしてする施しである陽徳と陰でこっそり行う施しである陰徳
- 陰徳はエネルギーを得られ、陽徳はエネルギーが失われる
- アウトプットするとはてブで心無いコメントが付き消耗する(陽徳)
- 陰徳を積む(インプットする)と自信がつき余裕が持てる
- リーダーの仕事は陽徳なので陰徳を積んで人間的に成長しなければならない
おわりに
- 自分でやった方が早い病はあらゆる人間関係について言える病
- 夫婦関係・恋愛関係
Quipper に転職して3ヶ月+が経った
3ヶ月経った時点で書こうと思っていたのだけど、どうにも筆が進まずに追加で2ヶ月以上経ってしまった。
入社してからやっていたこと
- (6月 ~ 10月) 新規事業のチームでバックエンドの開発
- Ruby + Go の Microservices
- 新入社員のオンボーディング
- (11月) スタディサプリ(toC)の Web アプリの開発(フロントエンドもバックエンドも)
- 新たに Go で(Microservices 文脈での)サービス作ってる
良かったこと
- 転職活動時に転職先へ求めていたことが満たされていた
- 知識量、学習意欲・実績、思考の速さ・的確さについて、自分より優れている人がゴロゴロいる
- 同時に、オーバーワークを良しとする空気も感じない
- 加えて裁量労働なので自身への負荷が調整しやすい
- 目黒(オフィス最寄り駅)が比較的穏やかで良い食事にも事欠かない
- 渋谷・新宿は人が多すぎて歩くのが嫌だった…
- 予想以上に英語を使う環境だった
- 「Pull Request は基本的に英語で書くが喋る機会は殆どない」と聞いていたが配属先チームは公用語が英語だった
- ミーティングは基本的に英語で進み、詰まるときや間違いを許容できないときは英日両方で共有される感じ
- 業務と英語習得が兼ねられて大変便利だった
- …のだけど残念ながら今は所属が変わり英語を使う機会は大幅に減ってしまった
- 「Pull Request は基本的に英語で書くが喋る機会は殆どない」と聞いていたが配属先チームは公用語が英語だった
- 貯金できるようになった
- 年収が約1.5倍になり入社〜現在に安い中古のグランドピアノ分くらい預金が増えた
- お金で行動の選択肢を増やしたい(休日増やすとか)ので今後も贅沢はほどほどにしたい
予想外だったこと・不満なこと
人間関係の悩みは無く大きな問題はナシ、ありがたや。
- 自席以外の個人作業ができる静かめな空間がない
- ふらっと移動して1人で仕事できる椅子・ソファの類が無い
- オフィスを見学したときにはまあ大丈夫かなと思っていたが、しばらく働いてみて予想以上に自分がそういう空間を欲することに気がついた
- そういう空間を求めるときは大抵不調なときなので予兆を掴めれば在宅勤務でカバーできるかもしれない
- 通勤で山手線に片道20分以上乗る必要がある
- 一刻も早くやめたいのでそのうち引っ越す
今後
- 自己効力感を高めていきたい
- 自己効力感 ≒ 幸福感 = 成果 * 成果理解力 = 能力 * 行動力 * 運 * 成果理解力、みたいな考え方をしてる
- 成果理解力 = 自身の成果に納得し、自己効力として自然に受け入れられること
- "成果は全てを癒す" は納得している考え方だけど "成果の実感は全てを癒す" が正確
- 能力・行動力は一旦置いておくとして、成果理解力に乏しいと感じている
- 大きな成果を上げたとき以外に自己効力感をほとんど感じなく、精神衛生的に不健康
- 成果を出せるように能力向上を図り然るべき行動を起こしていくというのは前提
- 自己効力感 ≒ 幸福感 = 成果 * 成果理解力 = 能力 * 行動力 * 運 * 成果理解力、みたいな考え方をしてる
- 週休3日化する?
- 土日は妻や友人と過ごす時間を優先し、平日の1日をやりたい学習に充てられると幸せなのでは
- 転職意欲が全く起きていないので上り調子にやっていきたい
落ち込むこともありつつ、概ね健康です。大健康大幸福を目指すぞ。
2019-11-18 Mon. 17:30 追記
不満なことを思い出したので公平のため追記。
オフィスの無線 LAN 環境がしばしば厳しいです…(過度に遅かったりしばしば切れることがある)
2020-01-16 Thu. 10:12 追記
今では無線 LAN 環境は困らなくなりました、ありがたい!