Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RailsConf 2019: New HotN+1ness - Hard lessons migrating from REST to GraphQL

RailsConf 2019: New HotN+1ness - Hard lessons migrating from REST to GraphQL

Our desire to use new tools and learn new technologies backfires when we don't take the time to fully understand them.

In 2018, we migrated from a REST API to GraphQL. Patterns were introduced, copied, pasted, and one day we woke up with queries taking 6s and page load times > 10s. Customers were complaining. How did we get here?

In this talk, we will discuss why we chose GraphQL, show practical examples of the mistakes we made in our implementation, and demonstrate how we eliminated all N+1 queries.

I'll answer the question, "if I knew then what I know now... Would I stick with a REST API?"

Eric Allen

May 02, 2019
Tweet

Other Decks in Programming

Transcript

  1. Country Load (37.7ms) SELECT 'countries'.* FROM 'countries' LIMIT 10 City

    Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 1 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 2 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 3 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 4 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 5 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 6 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 7 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 8 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 9 City Load (2.0ms) SELECT 'cities'.* from 'cities' WHERE 'cities.country_id' = 10 @_ejallday_
  2. Credential Load (0.3ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" =

    $1 LIMIT 1 [["id", 1]] Doctor Load (0.3ms) SELECT "doctors".* FROM "doctors" WHERE "doctors"."id" = $1 LIMIT 1 [["id", 1]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" = $1 ORDER BY "credentials"."id" ASC LIMIT 1 [["id", 1]] ProviderGroup Load (0.2ms) SELECT "provider_groups".* FROM "provider_groups" WHERE "provider_groups"."id" = $1 LIMIT 1 [["id", 50000]] Stream Load (0.7ms) SELECT "streams".* FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1 ORDER BY patients.last_name ASC, patients.first_name ASC LIMIT 25 OFFSET 0 [["provider_group_id", 50000]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 23]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "74808465-e9d8-4977-b6e5-fc366ba4446d"]] Stream Load (0.3ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 23]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 23]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.2ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 13]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 13], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 20]] Patient Load (0.2ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 10]] Credential Load (0.1ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 10], ["owner_type", "Patient"]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 2]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 19]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "11873eca-18f2-45b0-868b-4b0c8f4403f5"]] Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 19]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.2ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 19]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.1ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 10]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 10], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 9]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "cd6847a1-b490-442d-909f-c2235ece720c"]] Stream Load (0.1ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 9]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 9]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.1ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 5]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 5], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 10]] CACHE (0.0ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 5]] CACHE (0.0ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 5], ["owner_type", "Patient"]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 2]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 4]] Patient Load (0.1ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 2]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 2], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 2]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 3]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "19095b47-592d-4687-9cc2-396def04a971"]] Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 3]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 3]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.3ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 2]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 2], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "a9526e2b-b9ed-47b4-91b6-3da3c2e3f586"]] Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 1]] @_ejallday_
  3. Why am I here? And, why I'm gonna take about

    40 minutes of your time. @_ejallday_
  4. • I ❤ API's & Performance • I ❤ decisions

    that are good for the business @_ejallday_
  5. • I ❤ API's & Performance • I ❤ decisions

    that are good for the business • I ❤ software community @_ejallday_
  6. GraphQL is a query language for APIs and a runtime

    for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. -- graphql.org @_ejallday_
  7. REST API class Api::V1::StreamsController < Api::V1::ApplicationController def index streams =

    Stream.all.includes( :plan, encounters: [:provider, :patient], patient: :credential ) render json: CollectionSerializer.new( streams, serializer: StreamSerializer, path: request.url ).as_json end end @_ejallday_
  8. GraphQL class Resolvers::Streams < Resolvers::BaseResolver description 'Get all streams' type

    [Types::StreamType], null: false def resolve Stream.all.includes( :plan, encounters: [:provider, :patient], patient: :credential ) end end @_ejallday_
  9. REST API class Api::V1::StreamsController < Api::V1::ApplicationController def index streams =

    Stream.all.includes( :plan, encounters: [:provider, :patient], patient: :credential ) render json: CollectionSerializer.new( streams, serializer: StreamSerializer, path: request.url ).as_json end end @_ejallday_
  10. GraphQL class Resolvers::Streams < Resolvers::BaseResolver description 'Get all streams' type

    [Types::StreamType], null: false def resolve Stream.all.includes( :plan, encounters: [:provider, :patient], patient: :credential ) end end @_ejallday_
  11. REST API class Api::V1::StreamSerializer < ActiveModel::Serializer attributes :id, :active_encounter has_one

    :patient, serializer: PatientSerializer has_one :plan, serializer: PlanSerializer def active_encounter EncounterSerializer.new(object.encounters.first) end end @_ejallday_
  12. GraphQL class Types::StreamType < Types::BaseObject graphql_name 'Stream' description 'A stream'

    field :id, String, null: false field :active_encounter, Types::EncounterType, null: true field :patient, Types::PatientType, null: false field :plan, Types::PlanType, null: false def active_encounter object.encounters.first end end @_ejallday_
  13. REST API class Api::V1::StreamSerializer < ActiveModel::Serializer attributes :id, :active_encounter has_one

    :patient, serializer: PatientSerializer has_one :plan, serializer: PlanSerializer def active_encounter EncounterSerializer.new(object.encounters.first) end end @_ejallday_
  14. GraphQL class Types::StreamType < Types::BaseObject graphql_name 'Stream' description 'A stream'

    field :id, String, null: false field :active_encounter, Types::EncounterType, null: true field :patient, Types::PatientType, null: false field :plan, Types::PlanType, null: false def active_encounter object.encounters.first end end @_ejallday_
  15. Credential Load (0.3ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" =

    $1 LIMIT 1 [["id", 1]] Doctor Load (0.3ms) SELECT "doctors".* FROM "doctors" WHERE "doctors"."id" = $1 LIMIT 1 [["id", 1]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" = $1 ORDER BY "credentials"."id" ASC LIMIT 1 [["id", 1]] ProviderGroup Load (0.2ms) SELECT "provider_groups".* FROM "provider_groups" WHERE "provider_groups"."id" = $1 LIMIT 1 [["id", 50000]] Stream Load (0.7ms) SELECT "streams".* FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1 ORDER BY patients.last_name ASC, patients.first_name ASC LIMIT 25 OFFSET 0 [["provider_group_id", 50000]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 23]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "74808465-e9d8-4977-b6e5-fc366ba4446d"]] Stream Load (0.3ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 23]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 23]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.2ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 13]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 13], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 20]] Patient Load (0.2ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 10]] Credential Load (0.1ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 10], ["owner_type", "Patient"]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 2]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 19]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "11873eca-18f2-45b0-868b-4b0c8f4403f5"]] Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 19]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.2ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 19]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.1ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 10]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 10], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 9]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "cd6847a1-b490-442d-909f-c2235ece720c"]] Stream Load (0.1ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 9]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 9]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.1ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 5]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 5], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 10]] CACHE (0.0ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 5]] CACHE (0.0ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 5], ["owner_type", "Patient"]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 2]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 4]] Patient Load (0.1ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 2]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 2], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 2]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 3]] Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "19095b47-592d-4687-9cc2-396def04a971"]] Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 3]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE "streams"."id" = $1 LIMIT 1 [["id", 3]] CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.3ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = $1 LIMIT 1 [["id", 2]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND "credentials"."owner_type" = $2 LIMIT 1 [["owner_id", 2], ["owner_type", "Patient"]] CACHE (0.0ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" = $1 AND "encounters"."state" = 'active' ORDER BY "encounters"."id" ASC LIMIT 1 [["stream_id", 1]] Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1 [["id", "a9526e2b-b9ed-47b4-91b6-3da3c2e3f586"]] Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 1]]** @_ejallday_
  16. class Resolvers::Streams < Resolvers::BaseResolver description 'Get all streams' type [Types::StreamType],

    null: false def resolve Stream.all.includes( :plan, encounters: [:provider, :patient], patient: :credential ) end end @_ejallday_
  17. class Resolvers::Streams < Resolvers::BaseResolver description 'Get all streams' type [Types::StreamType],

    null: false def resolve Stream.all.includes( :plan, encounters: [:provider, :patient], patient: :credential ) end end @_ejallday_
  18. query Streams($limit: Int, $page: Int) { streams(limit: $limit, page: $page)

    { data { id activeEncounter { ...Encounter { provider { ...Provider } state } patient { ...Patient } plan { ...Plan } } } } @_ejallday_
  19. class Types::StreamType < Types::BaseObject graphql_name 'Stream' description 'A stream' field

    :id, String, null: false field :active_encounter, Types::EncounterType, null: true field :patient, Types::PatientType, null: false field :plan, Types::PlanType, null: false def active_encounter object.encounters.first end end @_ejallday_
  20. class RecordLoader < GraphQL::Batch::Loader def initialize(model, column: model.primary_key, where: nil)

    @model = model @column = column.to_s @column_type = model.type_for_attribute(@column) @where = where end def load(key) super(@column_type.cast(key)) end def perform(keys) query(keys).each { |record| fulfill(record.public_send(@column), record) } keys.each { |key| fulfill(key, nil) unless fulfilled?(key) } end private def query(keys) scope = @model scope = scope.where(@where) if @where scope.where(@column => keys) end end @_ejallday_
  21. class Types::StreamType < Types::BaseObject field :id, String, null: false field

    :active_encounter, Types::EncounterType, null: true field :patient, Types::PatientType, null: false field :plan, Types::PlanType, null: false def active_encounter AssociationLoader.for(Stream, :encounters).load(object).then(&:first) end def patient RecordLoader.for(Patient).load(object.patient_id) end def plan RecordLoader.for(Plan).load(object.plan_id) end end @_ejallday_
  22. class Types::StreamType < Types::BaseObject field :id, String, null: false field

    :active_encounter, Types::EncounterType, null: true field :patient, Types::PatientType, null: false field :plan, Types::PlanType, null: false def active_encounter AssociationLoader.for(Stream, :encounters).load(object).then(&:first) end def patient RecordLoader.for(Patient).load(object.patient_id) end def plan RecordLoader.for(Plan).load(object.plan_id) end end @_ejallday_
  23. Credential Load (0.3ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" =

    $1 LIMIT 1 [["id", 1]] Doctor Load (0.2ms) SELECT "doctors".* FROM "doctors" WHERE "doctors"."id" = $1 LIMIT 1 [["id", 1]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" = $1 ORDER BY... ProviderGroup Load (0.3ms) SELECT "provider_groups".* FROM "provider_groups" WHERE "provider_groups"."id" =... Stream Load (0.8ms) SELECT "streams".* FROM "streams" INNER JOIN "patients" ON "patients"."id" =... "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1 ORDER BY... (0.6ms) SELECT COUNT(*) FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1... Encounter Load (0.4ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" IN (23, 20, 19, 9, 10, 4, 3, 1, 2, 5, 6, 14, 13, 11, 12, 16, 15, 22, 17, 18, 7, 8, 21) Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1... Stream Load (0.2ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 23]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.2ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1... Plan Load (0.2ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE... CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.5ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" IN (13, 10, 5, 2, 1, 3, 7, 6, 8, 12, 9, 4, 11) Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND... Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" IN (1, 2) Credential Load (0.1ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."owner_id" = $1 AND... Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1... Stream Load (0.1ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 19]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1...i* @_ejallday_
  24. class Types::StreamType < Types::BaseObject field :id, String, null: false field

    :active_encounter, Types::EncounterType, null: true field :patient, Types::PatientType, null: false field :plan, Types::PlanType, null: false def active_encounter AssociationLoader.for(Stream, :encounters).load(object).then(&:first) end def patient RecordLoader.for(Patient).load(object.patient_id) end def plan RecordLoader.for(Plan).load(object.plan_id) end end @_ejallday_
  25. class Types::StreamType < Types::BaseObject ... field :patient, Types::PatientType, null: false

    def patient RecordLoader.for(Patient).load(object.patient_id) end ... end @_ejallday_
  26. class Types::PatientType < Types::BaseObject graphql_name 'Patient' description 'A patient' field

    :id, ID, null: false field :dob, Types::Scalar::DateType, null: false field :email, String, null: false field :first_name, String, null: false field :gender, String, null: false field :last_name, String, null: false end @_ejallday_
  27. class Types::PatientType < Types::BaseObject ... field :email, String, null: false

    def email RecordLoader.for(Credential) .load(object.credential_id) .then(&:email) end end @_ejallday_
  28. class Types::PatientType < Types::BaseObject ... field :email, String, null: false

    def email RecordLoader.for(Credential) .load(object.credential_id) .then(&:email) end end @_ejallday_
  29. Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" =

    $1 LIMIT 1 [["id", 1]] Doctor Load (0.1ms) SELECT "doctors".* FROM "doctors" WHERE "doctors"."id" = $1 LIMIT 1 [["id", 1]] Credential Load (0.1ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" = $1... ProviderGroup Load (0.3ms) SELECT "provider_groups".* FROM "provider_groups" WHERE "provider_groups"."id" =... Stream Load (0.8ms) SELECT "streams".* FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1 (0.5ms) SELECT COUNT(*) FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1 [["provider_group_id", 50000]] Encounter Load (0.4ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" IN (23, 20, 19, 9, 10, 4, 3, 1, 2, 5, 6, 14, 13, 11, 12, 16, 15, 22, 17, 18, 7, 8, 21) Credential Load (0.3ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" IN (23, 20, 15, 12, 11, 13, 17, 16, 18, 22, 19, 14, 21) Encounter Load (0.2ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1... Stream Load (0.1ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 23]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1... Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE... CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Patient Load (0.3ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" IN (13, 10, 5, 2, 1, 3, 7, 6, 8, 12, 9, 4, 11) Plan Load (0.3ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" IN (1, 2) Encounter Load (0.1ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1... Stream Load (0.1ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 19]] Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.1ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1... Plan Load (0.3ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE... CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] Encounter Load (0.3ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."id" = $1 LIMIT 1... Stream Load (0.1ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" = $1 LIMIT 1 [["id", 9]] Plan Load (0.1ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" = $1 LIMIT 1 [["id", 1]] PlanConfig Load (0.2ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1... Plan Load (0.2ms) SELECT "plans".* FROM "plans" INNER JOIN "streams" ON "plans"."id" = "streams"."plan_id" WHERE... CACHE (0.0ms) SELECT "plan_configs".* FROM "plan_configs" WHERE "plan_configs"."plan_id" = $1 LIMIT 1 [["plan_id", 1]] @_ejallday_
  30. class Types::StreamType < Types::BaseObject ... field :active_encounter, Types::EncounterType, null: true

    def active_encounter AssociationLoader.for(Stream, :encounters) .load(object) .then(&:first) end ... end @_ejallday_
  31. class PatientQueueStatus def self.for(encounter:) stream = encounter.stream plan = stream.plan

    unless encounter.completed? || stream.inactive? { message: "Operating Hours are between..." } end end end @_ejallday_
  32. class PatientQueueStatus def self.for(encounter:) stream = encounter.stream plan = stream.plan

    unless encounter.completed? || stream.inactive? { message: "Operating Hours are between..." } end end end @_ejallday_
  33. class Types::EncounterType < Types::BaseObject ... field :patient_queue_status, Types::PatientQueueStatusType def stream

    RecordLoader.for(Stream).load(object.stream_id) end def plan stream.then do |stream| RecordLoader.for(Plan).load(stream.plan_id) end end def patient_queue_status Promise.all([stream, plan]).then do |results| PatientQueueStatus.for( encounter: object, stream: results[0], plan: results[1] ) end end end @_ejallday_
  34. class Types::EncounterType < Types::BaseObject ... def patient_queue_status Promise.all([stream, plan]).then do

    |results| PatientQueueStatus.for( encounter: object, stream: results[0], plan: results[1] ) end end end @_ejallday_
  35. Credential Load (0.4ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" =

    $1 LIMIT 1 [["id", 1]] Doctor Load (0.3ms) SELECT "doctors".* FROM "doctors" WHERE "doctors"."id" = $1 LIMIT 1 [["id", 1]] Credential Load (0.2ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" = $1 ORDER BY... ProviderGroup Load (0.6ms) SELECT "provider_groups".* FROM "provider_groups" WHERE "provider_groups"."id" = ... Stream Load (8.4ms) SELECT "streams".* FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN "plans" ON "streams"."plan_id" = "plans"."id" WHERE "plans"."provider_group_id" = $1 ... (1.9ms) SELECT COUNT(*) FROM "streams" INNER JOIN "patients" ON "patients"."id" = "streams"."patient_id" INNER JOIN... Encounter Load (0.7ms) SELECT "encounters".* FROM "encounters" WHERE "encounters"."stream_id" IN (23, 20, 19, 9, 10, 4, 3, 1, 2, 5, 6, 14, 13, 11, 12, 16, 15, 22, 17, 18, 7, 8, 21) Patient Load (0.4ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" IN (13, 10, 5, 2, 1, 3, 7, 6, 8, 12, 9, 4, 11) Plan Load (0.2ms) SELECT "plans".* FROM "plans" WHERE "plans"."id" IN (1, 2) Stream Load (0.4ms) SELECT "streams".* FROM "streams" WHERE "streams"."id" IN (23, 19, 9, 3, 1, 5, 13, 11, 15, 22, 17, 7, 21) Doctor Load (0.2ms) SELECT "doctors".* FROM "doctors" WHERE "doctors"."id" = 1 Credential Load (0.3ms) SELECT "credentials".* FROM "credentials" WHERE "credentials"."id" IN (23, 20, 15, 12, 11, 13, 17, 16, 18, 22, 19, 14, 21) @_ejallday_
  36. What did we gain? • Flexibility • FREE documentation •

    New Dependencies graphql & graphql- batch @_ejallday_
  37. What did we give up? • Confidence (for a short

    while, and was certainly avoidable) @_ejallday_
  38. What did we give up? • Confidence (for a short

    while, and was certainly avoidable) • HTTP Response codes @_ejallday_
  39. What did we give up? • Confidence (for a short

    while, and was certainly avoidable) • HTTP Response codes • Granularity in performance monitoring @_ejallday_
  40. What did we give up? • Confidence (for a short

    while, and was certainly avoidable) • HTTP Response codes • Granularity in performance monitoring • Feature work @_ejallday_
  41. Ideas for better decision making • Remove opinions that are

    presented as facts • Say "I don't know" or "I'm not sure" more often @_ejallday_
  42. Ideas for better decision making • Remove opinions that are

    presented as facts • Say "I don't know" or "I'm not sure" more often • Embrace uncertainty @_ejallday_
  43. Ideas for better decision making • Remove opinions that are

    presented as facts • Say "I don't know" or "I'm not sure" more often • Embrace uncertainty • Focus on the process @_ejallday_
  44. Ideas for better decision making • Remove opinions that are

    presented as facts • Say "I don't know" or "I'm not sure" more often • Embrace uncertainty • Focus on the process • Remove bias based on the desire to learn a new tool @_ejallday_