(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': data().getTime(),įvykis:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-5LHNRP9'); "GraphQL Ruby". O kaip dėl našumo? - The Codest
The Codest
  • Apie mus
  • Paslaugos
    • Programinės įrangos kūrimas
      • Priekinės dalies kūrimas
      • Galinės dalies kūrimas
    • Staff Augmentation
      • Priekinės dalies kūrėjai
      • Atgalinės versijos kūrėjai
      • Duomenų inžinieriai
      • Debesų inžinieriai
      • QA inžinieriai
      • Kita
    • Patariamoji tarnyba
      • Auditas ir konsultacijos
  • Pramonės šakos
    • Fintech ir bankininkystė
    • E-commerce
    • Adtech
    • Sveikatos technologijos
    • Gamyba
    • Logistika
    • Automobiliai
    • IOT
  • Vertė už
    • CEO
    • CTO
    • Pristatymo vadybininkas
  • Mūsų komanda
  • Case Studies
  • Sužinokite, kaip
    • Tinklaraštis
    • Susitikimai
    • Interneto seminarai
    • Ištekliai
Karjera Susisiekite su mumis
  • Apie mus
  • Paslaugos
    • Programinės įrangos kūrimas
      • Priekinės dalies kūrimas
      • Galinės dalies kūrimas
    • Staff Augmentation
      • Priekinės dalies kūrėjai
      • Atgalinės versijos kūrėjai
      • Duomenų inžinieriai
      • Debesų inžinieriai
      • QA inžinieriai
      • Kita
    • Patariamoji tarnyba
      • Auditas ir konsultacijos
  • Vertė už
    • CEO
    • CTO
    • Pristatymo vadybininkas
  • Mūsų komanda
  • Case Studies
  • Sužinokite, kaip
    • Tinklaraštis
    • Susitikimai
    • Interneto seminarai
    • Ištekliai
Karjera Susisiekite su mumis
Atgal rodyklė GRĮŽTI ATGAL
2019-10-08
Programinės įrangos kūrimas

"GraphQL Ruby". O kaip dėl našumo?

The Codest

Tomasz Szkaradek

Plėtros architektas

GraphQL, kaip ir bet kuri kita technologija, turi problemų, kai kurios iš jų kyla tiesiogiai dėl architektūros, o kai kurios yra tokios pačios kaip ir bet kurioje kitoje programoje. Tačiau sprendimai yra visiškai skirtingi.

Norėdami pristatyti problemą, tarkime, kad yra tokia taikomosios programos architektūra:

https://drive.google.com/file/d/1N4sWPJSls0S8FFHbpHCUVHBNBpEuSsyz/view

Ir čia atitinkama užklausa GraphQL atsisiųsti duomenys. Atrenkame visas nuorodas kartu su plakatu ir jo nuorodomis, įtrauktomis į sistemą,

{
  allLinks {
    id
    url
    aprašymas
    createdAt
    postedBy {
      id
      pavadinimas
      nuorodos {
        id
      }
    }
  }
}

Kaip parodyta toliau, čia matome klasikinę n + 1 problemą su santykiais.

Nuorodų apkrovimas (0,4 ms) SELECT "links".* FROM "links" ORDER BY created_at DESC
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Naudotojo įkėlimas (0.3 ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 40], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Nuorodų įkėlimas (0,3 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 40]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Naudotojo įkėlimas (0,1 ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 38], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Nuorodų įkėlimas (0,1 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 38]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Naudotojo įkėlimas (0,2 ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 36], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Nuorodų įkėlimas (0,1 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 36]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Naudotojo įkėlimas (0,1 ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 34], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Nuorodų įkėlimas (0,2 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 34]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Naudotojo įkėlimas (0,1 ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 32], ["LIMIT", 1]]]

Šiuo atveju jis veikia lygiai taip pat, kaip ir šis elementas kodas:
Link.all.map(&:user).map(&:links).

Atrodo, kad žinome problemos sprendimą: Link.includes(user: :links).map(&:user).map(&:links), bet ar tai tikrai veiks? Patikrinkime!

Norėdamas patikrinti pataisymą, pakeičiau GraphQL užklausą, kad būtų naudojami tik keli laukai ir nebūtų jokių ryšių.

{
  allLinks {
    id
    url
    aprašymas
    createdAt
  }
}

Deja, rezultatas rodo, kad, nepaisant to, jog trūksta nuorodų, susijusių su naudotoju ir jo nuorodomis, mes vis tiek pridedame šiuos duomenis prie duomenų bazės užklausos. Deja, jie yra pertekliniai, o esant dar sudėtingesnei struktūrai, pasirodo, kad tai tiesiog neefektyvu.

GraphqlController#execute apdorojimas kaip */*
  Parametrai: {"query"=>"{n allLinks {n idn urln descriptionn createdAtn }n}", "graphql"=>{"query"=>"{n allLinks {n idn urln descriptionn createdAtn }n}"}}
  Nuorodų įkėlimas (0,3 ms) SELECT "links".* FROM "links" ORDER BY created_at DESC
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Vartotojo įkėlimas (0.3 ms) SELECT "users".* FROM "users" WHERE "users". "id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["id", 40], ["id", 38], ["id", 36], ["id", 34], ["id", 32], ["id", 30], ["id", 28], ["id", 26], ["id", 24], ["id", 22], ["id", 20], ["id", 18], ["id", 16], ["id", 14], ["id", 12], ["id", 10], ["id", 8], ["id", 6], ["id", 4], ["id", 2]].
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Nuorodų įkėlimas (0,3 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["user_id", 2], ["user_id", 4], ["user_id", 6], ["user_id", 8], ["user_id", 10], ["user_id", 12], ["user_id", 14], ["user_id", 16], ["user_id", 18], ["user_id", 20], ["user_id", 22], ["user_id", 24], ["user_id", 26], ["user_id", 28], ["user_id", 30], ["user_id", 32], ["user_id", 34], ["user_id", 36], ["user_id", 38], ["user_id", 40]].
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
Užbaigta 200 OK per 39 ms (Peržiūros: 0,7 ms | ActiveRecord: 0,9 ms | Alokacijos: 8730)

Svetainėje GraphQL, tokios problemos sprendžiamos kitaip, tiesiog pakraunant duomenis partijomis, darant prielaidą, kad duomenų reikia tada, kai jie pateikiami užklausai. Tai toks tingus įkėlimas. Viena iš populiariausių bibliotekų yra https://github.com/Shopify/graphql-batch/.

Deja, jos diegimas nėra toks paprastas, kaip gali atrodyti. Duomenų įkroviklius galima rasti čia: https://github.com/Shopify/graphql-batch/tree/master/examples, turiu omenyje RecordLoader klasė irAssociationLoader klasė. Įdiekime klasikinį gem 'graphql-batch' biblioteką ir pridėti ją prie mūsų schemos, taip pat įkroviklius:

# graphql-ruby/app/graphql/graphql_tutorial_schema.rb
klasė GraphqlTutorialSchema < GraphQL::Schema
  užklausa Types::QueryType
  mutation Types::MutationType
  use GraphQL::Batch
  ...
end

Ir mūsų tipai:

# graphql-ruby/app/graphql/types/link_type.rb
modulis Types
  klasė LinkType < BaseNode
    field :created_at, DateTimeType, null: false
    laukas :url, String, null: false
    field :description, String, null: false
    laukas :posted_by, UserType, null: false, metodas: :user
    laukas :votes, [Types::VoteType], null: false

    def user
      Loaders::RecordLoader.for(User).load(object.user_id)
    end
  end
end

# graphql-ruby/app/graphql/types/user_type.rb
modulis Types
  klasė UserType < BaseNode
    field :created_at, DateTimeType, null: false
    laukas :name, String, null: false
    laukas :email, String, null: false
    laukas :votes, [VoteType], null: false
    laukas :links, [LinkType], null: false

    def links
      Loaders::AssociationLoader.for(User, :links).load(object)
    end
  end
end

Naudodami įkroviklius, mes partijomis pateikiame duomenis ir užklausiame duomenų dviem paprastomis sql užklausomis:

Pradėtas POST "/graphql" ::1 2021-06-16 22:40:17 +0200
   (0,1 ms) SELECT sqlite_version(*)
Apdorojimas pagal GraphqlController#execute kaip */*
  Parametrai: {"query"=>"{n allLinks {n idn urln descriptionn createdAtn postedBy {n idn namen links {n idn }n }n }n }n}", "graphql"=>{{"query"=>"{n allLinks {n idn urln descriptionn createdAtn postedBy {n idn namen links {n idn }n }n }n }n}"}}}
  Nuorodų įkėlimas (0.4 ms) SELECT "links".* FROM "links"
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Naudotojo įkėlimas (0.9 ms) SELECT "users".* FROM "users" WHERE "users". "id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["id", 2], ["id", 4], ["id", 6], ["id", 8], ["id", 10], ["id", 12], ["id", 14], ["id", 16], ["id", 18], ["id", 20], ["id", 22], ["id", 24], ["id", 26], ["id", 28], ["id", 30], ["id", 32], ["id", 34], ["id", 36], ["id", 38], ["id", 40]].
  ↳ app/graphql/loaders/record_loader.rb:12:in `perform'
  Nuorodų įkėlimas (0,5 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["user_id", 2], ["user_id", 4], ["user_id", 6], ["user_id", 8], ["user_id", 10], ["user_id", 12], ["user_id", 14], ["user_id", 16], ["user_id", 18], ["user_id", 20], ["user_id", 22], ["user_id", 24], ["user_id", 26], ["user_id", 28], ["user_id", 30], ["user_id", 32], ["user_id", 34], ["user_id", 36], ["user_id", 38], ["user_id", 40]].
  ↳ app/graphql/loaders/association_loader.rb:46:in `preload_association'
Užbaigta 200 OK per 62 ms (Peržiūros: 1,3 ms | ActiveRecord: 1,8 ms | Alokacijos: 39887)

Šią problemą galima išspręsti ir kitais būdais, pvz:

https://github.com/exAspArk/batch-loader#basic-example

Užklausų sudėtingumas

N + 1 užklausos nėra viskas, nes GraphQL galime laisvai perkelti kitus atributus. Pagal numatytuosius nustatymus jis yra lygus 1. Kartais serveriui to gali būti per daug, ypač tais atvejais, kai galime laisvai įterpti duomenis. Kaip su tuo susidoroti? Galime apriboti užklausos sudėtingumą, tačiau tam taip pat turime atributuose nurodyti jų kainą. Pagal numatytuosius nustatymus ji nustatyta lygi 1. Šią kainą nustatome naudodami sudėtingumas: atributas, į kurį galime įvesti duomenis: laukas: links, [LinkType], null: false, sudėtingumas: 101. Jei ribojimas iš tikrųjų veikia, vis tiek reikia į savo sistemą įtraukti didžiausią ribą:

klasė GraphqlTutorialSchema < GraphQL::Schema
  užklausa Types::QueryType
  mutation Types::MutationType
  use GraphQL::Batch
  max_complexity 100
  ...
pabaiga

Sekimas

GraphQL užklausas apdoroja skirtingai, o atsekimas nėra toks paprastas, palyginti su tuo, ką galime atlikti vietoje. Deja, stovo mini profileris arba įprastas SQL žurnalas nepasakys mus viską ir nenurodys, kuri užklausos dalis yra atsakinga už tam tikrą laiko atkarpą. GraphQL-Ruby atveju galime naudoti komercinius sprendimus, kuriuos galima rasti čia: https://graphql-ruby.org/queries/tracing, arba pabandyti parengti savo atsekamąją medžiagą. Toliau pateikta fragmentas atrodo kaip vietinis sekimas.

# lib/my_custom_tracer.rb
klasė MyCustomTracer  'graphql.lex',
    'parse' => 'graphql.parse',
    'validate' => 'graphql.validate',
    'analyze_query' => 'graphql.analyze_query',
    'analyze_multiplex' => 'graphql.analyze_multiplex',
    'execute_multiplex' => 'graphql.execute_multiplex',
    'execute_query' => 'graphql.execute_query',
    'execute_query_lazy' => 'graphql.execute_query_lazy'
  }

  def platform_trace(platform_key, key, _data, &block)
    start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
    result = block.call
    trukmė = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
    observe(platform_key, key, duration)
    rezultatas
  pabaiga

  def platform_field_key(type, field)
    "graphql.#{type.graphql_name}.#{field.graphql_name}"
  end

  def platform_authorized_key(type)
    "graphql.authorized.#{type.graphql_name}"
  end

  def platform_resolve_type_key(type)
    "graphql.resolve_type.#{type.graphql_name}"
  end

  def observe(platform_key, key, duration)
    return if key == 'authorized'

    puts "platform_key: #{platform_key}, key: #{key}, duration: #{(trukmė * 1000).round(5)} ms".yellow
  pabaiga
pabaiga

Diegimas taip pat labai paprastas, reikia į schemą įtraukti žymeklio informaciją tracer (MyCustomTracer.new) konfigūracija. Kaip toliau pateiktame pavyzdyje:

# graphql-ruby/app/graphql/graphql_tutorial_schema.rb
klasė GraphqlTutorialSchema < GraphQL::Schema
  užklausa Types::QueryType
  mutation Types::MutationType
  use GraphQL::Batch
  tracer(MyCustomTracer.new)
  ...
pabaiga

Tokio sekimo išvestis atrodo taip:

Pradėtas POST "/graphql" ::1 2021-06-17 22:02:44 +0200
   (0,1 ms) SELECT sqlite_version(*)
Apdorojimas pagal GraphqlController#execute kaip */*
  Parametrai: {"query"=>"{n allLinks {n idn urln descriptionn createdAtn postedBy {n idn namen links {n idn }n }n }n }n}", "graphql"=>{{"query"=>"{n allLinks {n idn urln descriptionn createdAtn postedBy {n idn namen links {n idn }n }n }n }n}"}}}
platform_key: graphql.lex, key: lex, duration: 0.156 ms
platform_key: graphql.parse, raktas: parse, trukmė: 0,108 ms
platform_key: graphql.validate, raktas: validate, trukmė: 0.537 ms
platform_key: graphql.analyze_query, raktas: analyze_query, trukmė: 0.123 ms
platform_key: graphql.analyze_multiplex, raktas: analyze_multiplex, trukmė: 0.159 ms
  Nuorodų apkrovimas (0,4 ms) SELECT "links".* FROM "links"
  ↳ app/graphql/graphql_tutorial_schema.rb:21:in `platform_trace'
platform_key: graphql.execute_query, key: execute_query, duration: 15.562 ms
  ↳ app/graphql/loaders/record_loader.rb:12:in `perform'
  ↳ app/graphql/loaders/association_loader.rb:46:in `preload_association'
platform_key: graphql.execute_query_lazy, key: execute_query_lazy, duration: 14.12 ms
platform_key: graphql.execute_multiplex, key: execute_multiplex, duration: 31.11 ms
Užbaigta 200 OK per 48 ms (Peržiūros: 1,2 ms | ActiveRecord: 2,0 ms | Alokacijos: 40128)

Santrauka

GraphQL nebėra nauja technologija, tačiau jos problemų sprendimai nėra visiškai standartizuoti, jei jie nėra bibliotekos dalis. Šios technologijos įgyvendinimas projektas suteikia daug galimybių sąveikauti su frontend'u, ir aš asmeniškai manau, kad tai yra nauja kokybė, palyginti su tuo, ką REST API pasiūlymai.

Kodėl turėtumėte (tikriausiai) naudoti Typescript

Kaip nesunaikinti projekto dėl blogos kodavimo praktikos?

Duomenų gavimo strategijos "NextJS

Susiję straipsniai

Išmaniojo telefono sveikatos priežiūros programėlės su širdies piktograma ir kylančia sveikatos diagrama, pažymėtos The Codest logotipu, iliustracija, vaizduojanti skaitmeninės sveikatos ir sveikatos technologijų sprendimus.
Programinės įrangos kūrimas

Sveikatos priežiūros programinė įranga: Sveikatos priežiūros paslaugos: tipai, naudojimo atvejai

Įrankiai, kuriais šiandien naudojasi sveikatos priežiūros organizacijos, nė iš tolo neprimena prieš kelis dešimtmečius naudotų popierinių kortelių. sveikatos priežiūros programinė įranga dabar padeda sveikatos sistemoms, pacientų priežiūrai ir šiuolaikiniam sveikatos priežiūros paslaugų teikimui klinikinėse ir...

GERIAUSIAS
Abstrakti mažėjančios stulpelinės diagramos su kylančia rodykle ir auksine moneta, simbolizuojančia ekonomiškumą arba taupymą, iliustracija. Viršutiniame kairiajame viršutiniame kampe pavaizduotas The Codest logotipas ir šūkis "In Code We Trust" šviesiai pilkame fone.
Programinės įrangos kūrimas

Kaip padidinti savo Dev komandą neprarandant produkto kokybės

Didinate savo kūrėjų komandą? Sužinokite, kaip augti neprarandant produkto kokybės. Šiame vadove aptariami ženklai, kad atėjo laikas didinti komandą, komandos struktūra, įdarbinimas, vadovavimas ir įrankiai - ir kaip The Codest gali...

GERIAUSIAS
Programinės įrangos kūrimas

Sukurkite ateičiai atsparias žiniatinklio programas: The Codest ekspertų komandos įžvalgos

Sužinokite, kaip The Codest puikiai kuria keičiamo dydžio interaktyvias žiniatinklio programas, naudodama pažangiausias technologijas ir užtikrindama vientisą naudotojų patirtį visose platformose. Sužinokite, kaip mūsų patirtis skatina skaitmeninę transformaciją ir verslo...

GERIAUSIAS
Programinės įrangos kūrimas

10 geriausių Latvijoje įsikūrusių programinės įrangos kūrimo įmonių

Naujausiame mūsų straipsnyje sužinokite apie geriausias Latvijos programinės įrangos kūrimo įmones ir jų inovatyvius sprendimus. Sužinokite, kaip šie technologijų lyderiai gali padėti pakelti jūsų verslo lygį.

thecodest
Įmonių ir didinimo sprendimai

"Java" programinės įrangos kūrimo pagrindai: A Guide to outsourcing Outsourcing Successfully

Išnagrinėkite šį esminį vadovą, kaip sėkmingai outsourcing "Java" programinę įrangą kurti, kad padidintumėte efektyvumą, įgytumėte patirties ir sėkmingai įgyvendintumėte projektus su The Codest.

thecodest

Prenumeruokite mūsų žinių bazę ir būkite nuolat informuoti apie IT sektoriaus patirtį.

    Apie mus

    The Codest - tarptautinė programinės įrangos kūrimo bendrovė, turinti technologijų centrus Lenkijoje.

    Jungtinė Karalystė - būstinė

    • 303B biuras, 182-184 High Street North E6 2JA
      Londonas, Anglija

    Lenkija - vietiniai technologijų centrai

    • Fabryczna biurų parkas, Aleja
      Pokoju 18, 31-564 Krokuva
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšuva, Lenkija

    The Codest

    • Pagrindinis
    • Apie mus
    • Paslaugos
    • Case Studies
    • Sužinokite, kaip
    • Karjera
    • Žodynas

    Paslaugos

    • Patariamoji tarnyba
    • Programinės įrangos kūrimas
    • Galinės dalies kūrimas
    • Priekinės dalies kūrimas
    • Staff Augmentation
    • Atgalinės versijos kūrėjai
    • Debesų inžinieriai
    • Duomenų inžinieriai
    • Kita
    • QA inžinieriai

    Ištekliai

    • Faktai ir mitai apie bendradarbiavimą su išoriniu programinės įrangos kūrimo partneriu
    • Iš JAV į Europą: Kodėl Amerikos startuoliai nusprendžia persikelti į Europą?
    • Technikos plėtros centrų užsienyje palyginimas: Tech Offshore Europa (Lenkija), ASEAN (Filipinai), Eurazija (Turkija)
    • Kokie yra svarbiausi CTO ir CIO iššūkiai?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Autorinės teisės © 2026 The Codest. Visos teisės saugomos.

    lt_LTLithuanian
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian es_ESSpanish nl_NLDutch etEstonian elGreek pt_PTPortuguese cs_CZCzech lvLatvian is_ISIcelandic lt_LTLithuanian