Hatena Engineer Seminar #14 GraphQL編

Hatena Engineer Seminar #14 GraphQL編

サブタイトル: 〜ユーザーも開発者も快適なサービスであるために〜

魔法のiらんどでは、クライアント・サーバー間のインターフェースとしてGraphQLを採用しました。この技術選択は、はてなの技術スタックのモダン化を進め、ユーザーと開発に携わるエンジニア双方の体験が快適であることを目指したものです。この発表では、魔法のiらんどリニューアルを成功に導いたGraphQLの強みと、月間3億PVをさばくためのパフォーマンス面の検討について紹介します。

5ef13e6d95d73c58a4641bf20083c245?s=128

Yuu Igarashi

July 15, 2020
Tweet

Transcript

  1. GraphQLฤ ʙϢʔβʔ΋։ൃऀ΋շదͳαʔϏεͰ͋ΔͨΊʹʙ Hatena Engineer Seminar #14 2020/07/15 id:yigarashi

  2. ࣗݾ঺հ • id:yigarashi • ৽ଔ2೥໨ • WebΞϓϦέʔγϣϯΤϯδχΞ • ڵຯ •

    ։ൃϓϩηεɺϓϩδΣΫτ؅ཧ • ϓϩάϥϛϯάݴޠཧ࿦
  3. ຐ๏ͷiΒΜͲ

  4. γεςϜͷ֓؍ /FYUKT αʔόʔαΠυϨϯμϦϯά (SBQI2-"1* σʔλϕʔε

  5. γεςϜͷ֓؍ /FYUKT αʔόʔαΠυϨϯμϦϯά (SBQI2-"1* σʔλϕʔε ͍·͔Β͜͜ͷ࿩Λ͠·͢

  6. GraphQLฤ - ໨࣍ • GraphQLͱ͸ • ͳͥGraphQLΛ࠾༻͔ͨ͠ • GraphQLͷԸܙ •

    ݄ؒ3ԯPV΁ͷඋ͑
  7. GraphQLͱ͸

  8. GraphQLͱ͸ "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ

  9. GraphQLͱ͸ "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ ΫϥΠΞϯτ αʔόʔ ʢϥϯλΠϜʣ

  10. GraphQLͱ͸ { me { name interests } } ΫΤϦ "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ

    ΫϥΠΞϯτ αʔόʔ ʢϥϯλΠϜʣ
  11. GraphQLͱ͸ { me { name interests } } ΫΤϦ {

    "me": { "name": "yigarashi", "interests": [ "։ൃϓϩηε", "ϓϩάϥϛϯάݴޠཧ࿦", ] } } ฦΓ஋ʢ+40/ʣ "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ ΫϥΠΞϯτ αʔόʔ ʢϥϯλΠϜʣ
  12. GraphQLͱ͸ { me { name interests } } ΫΤϦ {

    "me": { "name": "yigarashi", "interests": [ "։ൃϓϩηε", "ϓϩάϥϛϯάݴޠཧ࿦", ] } } ฦΓ஋ʢ+40/ʣ ΫΤϦͱಉ͡ܗͷ +40/͕ฦ͞ΕΔ "1*ͷͨΊͷΫΤϦݴޠͱΫΤϦʹԠͨ͡σʔλΛฦ͢ϥϯλΠϜ ΫϥΠΞϯτ αʔόʔ ʢϥϯλΠϜʣ
  13. ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ

  14. ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ

  15. ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ type Query { me: Me }

    type Me { name: String interests: List[String] } εΩʔϚͷྫ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ
  16. ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ type Query { me: Me }

    type Me { name: String interests: List[String] } εΩʔϚͷྫ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ { me { name interests } }
  17. ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ type Query { me: Me }

    type Me { name: String interests: List[String] } εΩʔϚͷྫ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ { me { name interests } }
  18. type Query { me: Me } type Me { name:

    String interests: List[String] } ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ εΩʔϚͷྫ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ
  19. type Query { me: Me } type Me { name:

    String interests: List[String] } ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ εΩʔϚͷྫ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ { me { name interests age } }
  20. type Query { me: Me } type Me { name:

    String interests: List[String] } ॏཁͳ֓೦(1) - εΩʔϚ ϥϯλΠϜ͕ͲΜͳσʔλΛฦͤΔ͔هड़ͨ͠΋ͷ εΩʔϚͷྫ αʔόʔ ʢϥϯλΠϜʣ ΫϥΠΞϯτ { me { name interests age } }
  21. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺

  22. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺ { me { name interests }

    }
  23. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺ { me { name interests }

    } యܕతͳ࣮ߦ
  24. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺ { me { name interests }

    } • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ యܕతͳ࣮ߦ
  25. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺ { me { name interests }

    } • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ • nameΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • Me.nameΛฦ͢ యܕతͳ࣮ߦ
  26. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺ { me { name interests }

    } • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ • nameΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • Me.nameΛฦ͢ • interestsΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • Me.interests͕idͷྻʹͳ͍ͬͯΔͷͰDB͔Βऔಘ͢Δ యܕతͳ࣮ߦ
  27. ॏཁͳ֓೦(2) - Ϧκϧό ϥϯλΠϜʹ͓͍ͯϑΟʔϧυͷ஋Λܭࢉ͢Δؔ਺ { me { name interests }

    } • meΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • DB͔ΒmeʹରԠ͢ΔΦϒδΣΫτMeΛऔಘ • nameΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • Me.nameΛฦ͢ • interestsΛܭࢉ͢ΔϦκϧόΛ࣮ߦ • Me.interests͕idͷྻʹͳ͍ͬͯΔͷͰDB͔Βऔಘ͢Δ • ࠷ޙʹ׬੒ͨ͠஋ΛJSONʹͯ͠ฦ͢ యܕతͳ࣮ߦ
  28. ͳͥGraphQLΛ࠾༻͔ͨ͠

  29. Ϣʔβʔମݧɾ։ൃޮ཰Λ޲্͢Δ • Ϣʔβʔମݧ • SPA(Single Page Application) + APIͷߏ੒ʹ͢Δ͜ͱͰɺϞμϯͳ ΞϓϦతͳମݧΛ࣮૷͠΍͘͢͢Δ

    • ։ൃޮ཰ • ΫΤϦݴޠ͕ڧྗͰը໘ͷมߋʹڧ͍ • Ϟμϯͳٕज़Λ࠾༻͢Δ͜ͱͰϞνϕʔγϣϯ޲্ • ͔ͦ͠͠Ε͚ͩͰ͸ͳ͍……
  30. ΧΫϤϜ

  31. ຐ๏ͷiΒΜͲϦχϡʔΞϧͷϙΠϯτ • ΧΫϤϜͷίΞϩδοΫΛ࢖ͬͯAPIΛ࣮૷͢Δ • ྫ: ࡞඼ʹؔ͢ΔCRUDૢ࡞ • ൓ରʹɺຐ๏ͷiΒΜͲͷͨΊʹ࣮૷ͨ͠APIΛΧΫϤϜͰ΋࢖͑ΔΑ ͏ʹ͓ͯ͘͠ ΧΫϤϜͱίʔυϕʔεΛڞ༗͠ɺ։ൃɾӡ༻ίετͷ࡟ݮΛૂ͏

  32. ঢ়گΛ෼ੳ͢Δ • খઆΛॻ͘ɺಡΉɺڞ༗͢Δͱ͍͏େے͸͔֬ʹࣅ͍ͯΔ • ࡉ෦͕ͪΐͬͱͣͭҧ͏ • ϑΟʔϧυͷ༗ແ • ຐ๏ͷiΒΜͲʹ͋Δ͕ΧΫϤϜʹ͸ͳ͍ɺٯ΋વΓ •

    ஋Λฦͨ͢Ίͷॲཧͷҧ͍
  33. REST API ͸ͭΒ͍ ίΞϩδοΫΛ࢖ͬͯJSON Λฦ͢෦෼ͷංେԽɾΧΦ εԽ͕༧૝͞Εͨ

  34. REST API ͸ͭΒ͍ sub example_handler { my $common_field = get_common_field;

    if (is_kakuyomu) { $common_field = kakuyomu_specific_operation($common_field); } my $kakuyomu_field = undef; if (is_kakuyomu) { $kakuyomu_field = get_kakuyomu_field; } ... } ίΞϩδοΫΛ࢖ͬͯJSON Λฦ͢෦෼ͷංେԽɾΧΦ εԽ͕༧૝͞Εͨ
  35. REST API ͸ͭΒ͍ sub example_handler { my $common_field = get_common_field;

    if (is_kakuyomu) { $common_field = kakuyomu_specific_operation($common_field); } my $kakuyomu_field = undef; if (is_kakuyomu) { $kakuyomu_field = get_kakuyomu_field; } ... } ίΞϩδοΫΛ࢖ͬͯJSON Λฦ͢෦෼ͷංେԽɾΧΦ εԽ͕༧૝͞Εͨ ڞ௨ͷϑΟʔϧυ͕ͩΧΫϤϜͰ ͸ಛघͳՃ޻͕ඞཁ
  36. REST API ͸ͭΒ͍ sub example_handler { my $common_field = get_common_field;

    if (is_kakuyomu) { $common_field = kakuyomu_specific_operation($common_field); } my $kakuyomu_field = undef; if (is_kakuyomu) { $kakuyomu_field = get_kakuyomu_field; } ... } ίΞϩδοΫΛ࢖ͬͯJSON Λฦ͢෦෼ͷංେԽɾΧΦ εԽ͕༧૝͞Εͨ ڞ௨ͷϑΟʔϧυ͕ͩΧΫϤϜͰ ͸ಛघͳՃ޻͕ඞཁ ΧΫϤϜͷΈଘࡏ͢ΔϑΟʔϧυ
  37. GraphQLͰղܾ ϑΟʔϧυ͝ͱʹϦκϧ όΛఆٛ͢ΔͷͰɺॲཧ ͷৼΓ෼͚Λॊೈ͔ͭ៉ ྷʹߦ͏͜ͱ͕Ͱ͖Δ

  38. GraphQLͰղܾ GraphQL::Type::Object->new( name => 'Example', fields => +{ common_field =>

    is_kakuyomu ? kakuyomu_resolver : maho_resolver, (is_kakuyomu ? ( kakuyomu_field => kakuyomu_field_resolver ) : ()) ... } ) ϑΟʔϧυ͝ͱʹϦκϧ όΛఆٛ͢ΔͷͰɺॲཧ ͷৼΓ෼͚Λॊೈ͔ͭ៉ ྷʹߦ͏͜ͱ͕Ͱ͖Δ
  39. GraphQLͰղܾ GraphQL::Type::Object->new( name => 'Example', fields => +{ common_field =>

    is_kakuyomu ? kakuyomu_resolver : maho_resolver, (is_kakuyomu ? ( kakuyomu_field => kakuyomu_field_resolver ) : ()) ... } ) ϑΟʔϧυ͝ͱʹϦκϧ όΛఆٛ͢ΔͷͰɺॲཧ ͷৼΓ෼͚Λॊೈ͔ͭ៉ ྷʹߦ͏͜ͱ͕Ͱ͖Δ ݺͼग़͢ϦκϧόΛৼΓ෼͚Δͩ ͚Ͱྑ͍
  40. GraphQLͰղܾ GraphQL::Type::Object->new( name => 'Example', fields => +{ common_field =>

    is_kakuyomu ? kakuyomu_resolver : maho_resolver, (is_kakuyomu ? ( kakuyomu_field => kakuyomu_field_resolver ) : ()) ... } ) ϑΟʔϧυ͝ͱʹϦκϧ όΛఆٛ͢ΔͷͰɺॲཧ ͷৼΓ෼͚Λॊೈ͔ͭ៉ ྷʹߦ͏͜ͱ͕Ͱ͖Δ ݺͼग़͢ϦκϧόΛৼΓ෼͚Δͩ ͚Ͱྑ͍ αʔϏε͝ͱʹඞཁͳϑΟʔϧυ ͚ͩ࿐ग़͢Δ
  41. ࠾༻ཧ༝ͷ·ͱΊ • SPA + API ʹΑΔϞμϯͳϢʔβʔମݧͷ࣮ݱ • ։ൃޮ཰ͷ޲্ • ΧΫϤϜͱͷίʔυϕʔεͷڞ༗

    ҎԼͷͭͷधཁΛ͏·͘ຬͨ͢ͷ͕(SBQI2-ͩͬͨ
  42. GraphQLͷԸܙ

  43. Ըܙ(1) - ϑϩϯτΤϯυ։ൃମݧͷ޲্ • ύϫϑϧͳΫΤϦݴޠʹΑΔॊೈͳσʔλऔಘ • Τϐιʔυ --> ࡞඼ -->

    ࡞ऀͱ͍ͬͨωετͨ͠σʔλ΋αʔόʔ ͷมߋͳ͠ʹ؆୯ʹऔಘͰ͖Δ • Apollo ClientͷԸܙ • Reactͱ࿈ܞ͢ΔGraphQLΫϥΠΞϯτͷσϑΝΫτ • ߥ࡟Γͳ෦෼΋͋Δ͕ϞμϯͳUIΛ࡞ΔͨΊͷػೳ͕๛෋
  44. Ըܙ(2) - API։ൃͷޮ཰Խ • ࠷ॳʹશͯͷը໘Λਫ਼ࠪͯ͠1000ߦ͍ۙεΩʔϚΛઃܭͨ͠ • εΩʔϚϑΝʔετͳ։ൃ • εΩʔϚઃܭΛ௨ͯ͠ϓϩμΫτ΁ͷཧղ͕ਂ·ͬͨ •

    ΤϯδχΞ8ਓͷେॴଳ͕͏·͘ػೳ͢Δ͖͔͚ͬʹ • εΩʔϚத৺ͷίϛϡχέʔγϣϯͰԿΛ࡞Δ͔ͷೝ͕ࣝἧͬͨ • type ΍ mutation ୯Ґͷ෼୲Ͱߴ౓ͳฒྻ։ൃ͕Մೳʹ
  45. Ըܙ(3) - ։ൃϞνϕʔγϣϯͷ޲্ • ϦϦʔεޙͷ;Γ͔͑ΓͰʮϞνϕʔγϣϯΛߴ͘อͯͨʯͱͷ੠ • ϕςϥϯ΋஌Βͳ͍͜ͱ͕ͨ͘͞Μ͋Γܹࢗతͳ೔ʑ • ແҋʹ৽ٕज़Λ࢖͏ͷ͸ةݥ͕ͩɺద੾ʹऔΓೖΕΔͷ͸ॏཁ •

    ྲྀߦ͍ͬͯΔٕज़͸৘ใ΋ଟ͍
  46. Ըܙ(4) - ΧΫϤϜͱͷίʔυϕʔεڞ༗ • ೉͍͠෦෼΋ଟ͕͋ͬͨ֓͘Ͷ៉ྷʹ࣮૷Ͱ͖ͨ • ॲཧΛ෼͚͍ͨ৔߹ͷΈαʔϏε͝ͱͷϦκϧόΛఆٛ • αʔϏε͝ͱʹεΩʔϚ͕͋ΔͷͰϑΟʔϧυͷࠩ෼΋໌Β͔

  47. ݄ؒ3ԯPV΁ͷඋ͑

  48. ݄ؒ3ԯPVͷڪා • ࣄલͷݟੵ΋Γ͸ϐʔΫ࣌Ͱຖඵ350ϦΫΤετ • طଘαʔϏεͷϦϓϨΠε • ϦϦʔεͨ͠௚ޙ͔ΒେྔͷΞΫηε͕΍ͬͯ͘Δ • ҆શ͔ͭద੾ʹඋ͑Δඞཁ͕͋Δ •

    Πϯϑϥඅ༻͸༗ݶ
  49. ࡞ઓ • N+1໰୊ͷରࡦͳͲجຊతͳ͜ͱ͸΍Δ • Ωϟογϡ͕೉͍͠ͷΛͲ͏͢Δ͔ • ΤϯυϙΠϯτ͕ /graphql ʹू໿͞Εͯ͠·͏ •

    ΞϓϦέʔγϣϯϨΠϠʔͰͷΩϟογϡ͸࣮૷ίετ͕ߴ͍ • ຊ౰ʹΩϟογϡ͕ඞཁ͔ෛՙࢼݧΛߦͬͯ໌Β͔ʹ͢Δ • ਪଌ͢ΔͳɺܭଌͤΑ
  50. ෛՙࢼݧ - 1ճ໨ͷ݁Ռ • Ωϟογϡͱؔ܎ͳ͘ϨΠςϯγ͕ҟৗʹѱ͍ • ͔͠΋ϨεϙϯεαΠζʹൺྫͯ͠ϨΠςϯγ͕ѱԽ͍ͯ͠Δ… • ϓϩϑΝΠϦϯάͯ͠ௐࠪ •

    ࣮ߦ࣌ͷܕνΣοΫϥΠϒϥϦ͕ɺ֤ϑΟʔϧυͷϦκϧόݺͼग़ ͠͝ͱʹݺ͹Ε͍ͯͨͷ͕ݪҼ • production؀ڥͰܕνΣοΫΛແޮʹ͢ΔύονΛ౰ͯͯ࠶τϥΠ
  51. ෛՙࢼݧ - 2ճ໨ͷ݁Ռ • ര଎ • ฏۉϨΠςϯγ͸100msۙ͘ • ݱ࣮తͳΠϯϑϥͰຊ൪૝ఆΛେ্͖͘ճΔෛՙʹ଱͑ΒΕͨ •

    ݁࿦: Ωϟογϡͳ͠ͰϦϦʔεޙͷෛՙʹ଱͑ΒΕΔ • ϦϦʔεޙɺෛՙͷ໰୊ͰαʔϏεμ΢ϯ͢Δ͜ͱ͸ͳ͔ͬͨ • ܭଌʹΑͬͯඞཁे෼ͳରࡦΛߦ͑ͨ
  52. ·ͱΊ

  53. • ຐ๏ͷiΒΜͲͰ͸ GraphQL Λ࠾༻͠·ͨ͠ɹ • ϞμϯͳϢʔβʔମݧͷ࣮ݱʹߩݙ • ։ൃޮ཰ɾ։ൃϞνϕʔγϣϯ͕޲্ • ΧΫϤϜͱͷϩδοΫڞ༗ͱ͍͏೉͍͠ཁٻʹϚον

    • ύϑΥʔϚϯε໘΋ཱ྆ • ͸ͯͳͱGraphQL • ϋϚΔͱ͜ΖͰ࠾༻͞ΕΔ͜ͱ͕૿͖͑ͯͨ • ͜Ε͔Β΋ϓϥΫςΟεͷ஝ੵɾڞ༗ΛਐΊ͍͖ͯ·͢ʂ