GraphQL + KotlinでN+1問題 を対応する / GraphQL + Kotlin with N+1 problem

8d887e6605353ca5fb1e05f38564ef50?s=47 Shiraji
October 09, 2019

GraphQL + KotlinでN+1問題 を対応する / GraphQL + Kotlin with N+1 problem

8d887e6605353ca5fb1e05f38564ef50?s=128

Shiraji

October 09, 2019
Tweet

Transcript

  1. (SBQI2- ,PUMJOͰ/ ໰୊ ΛରԠ͢Δ ͠Β͡

  2. 8IPBN* w ,PUMJO (SBQI2- ZFBS w 0QFO4PVSDFLPUMJOHSBQIRMTBNQMF w -PWF,PUMJO w

    8PSLTGPS6CJF *OD @shiraji @shiraj_i
  3. ,PUMJO'FTU IUUQTTQFBLFSEFDLDPNTIJSBKJTFSWFSTJEFLPUMJOQMVTHSBQIRM

  4. / 1SPCMFN w 0OFDPNQBOZIBTNBOZESVHT w 0OFESVHIBTPOFDPNQBOZ

  5. w 4&-&$5 '30.ESVHXIFSFDPNQBOZ@JE w 4&-&$5 '30.DPNQBOZXIFSFJE w / 1SPCMFN

  6. w NVMUJQMFESVHTBUPODF / 1SPCMFN

  7. w 4&-&$5 '30.ESVHXIFSFJEJO    / 1SPCMFN w 4&-&$5

    '30.DPNQBOZXIFSFJE w 4&-&$5 '30.DPNQBOZXIFSFJE w 4&-&$5 '30.DPNQBOZXIFSFJE
  8. w 4&-&$5 '30.ESVHXIFSFJEJO     w 4&-&$5 '30.DPNQBOZXIFSFJEJO

        w / 1SPCMFN
  9. w 6TF%BUB-PBEFS w 3FHJTUFS%BUB-PBEFSUP(SBQI2-$POUFYUXJUI4USJOHLFZ w (FU%BUB-PBEFSVTJOH4USJOHLFZBU3FTPMWFS w *O%BUB-PBEFS XBJUVOUJMBMMJUFNT 4PMWJOH/

    XJUIHSBQIRMKBWBTUBSUFS
  10. / XJUIHSBQIRMKBWBTUBSUFS val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( )a return

    context
  11. / XJUIHSBQIRMKBWBTUBSUFS val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry() )a

    return context
  12. / XJUIHSBQIRMKBWBTUBSUFS val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register() )a

    return context
  13. / XJUIHSBQIRMKBWBTUBSUFS val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_companies_by_drugs_id”,

    DataLoader.newDataLoader<Long, Company> { companyIds, _ -> }c )b )a return context
  14. / XJUIHSBQIRMKBWBTUBSUFS val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_companies_by_drugs_id”,

    DataLoader.newDataLoader<Long, Company> { companyIds, _ -> CompletableFuture.supplyAsync { }d }c )b )a return context
  15. / XJUIHSBQIRMKBWBTUBSUFS val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_companies_by_drugs_id”,

    DataLoader.newDataLoader<Long, Company> { companyIds, _ -> CompletableFuture.supplyAsync { // SELECT * FROM company where company_id in (1, 2, 3) drugService.getCompanyByIds(companyIds) }d }c )b )a return context
  16. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompany(drug: Drug): Company {a companyService.getCompanyById(drug.companyId) }a }b
  17. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompany(drug: Drug, env: DataFetchingEnvironment): Company {a companyService.getCompanyById(drug.companyId) }a }b
  18. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompanies(drug: Drug, env: DataFetchingEnvironment): CompletableFuture<List<Company>>? {a companyService.getCompanyById(drug.companyId) }a }b
  19. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompanies(drug: Drug, env: DataFetchingEnvironment): CompletableFuture<List<Company>>? {a }a }b
  20. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompanies(drug: Drug, env: DataFetchingEnvironment): CompletableFuture<List<Company>>? {a return env .getDataLoader<Long, List<Company>>(“key_companies_by_drugs_id”) }a }b
  21. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompanies(drug: Drug, env: DataFetchingEnvironment): CompletableFuture<List<Company>>? {a return env .getDataLoader<Long, List<Company>>(“key_companies_by_drugs_id”) .load(drug.companyId) }a }b
  22. / XJUIHSBQIRMKBWBTUBSUFS @Component class DrugResolver : GraphQLResolver<Drug> { @Suppress("unused") fun

    getCompanies(drug: Drug, env: DataFetchingEnvironment): CompletableFuture<List<Company>>? {a return env .getDataLoader<Long, List<Company>>(“key_companies_by_drugs_id”) .load(drug.companyId) }a }b val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_companies_by_drugs_id”, DataLoader.newDataLoader<Long, Company> { companyIds, _ -> CompletableFuture.supplyAsync { // SELECT * FROM company where company_id in (1, 2, 3) drugService.getCompanyByIds(companyIds) }d }c )b )a return context
  23. "TTPDJBUFLFZXJUIWBMVF val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_companies_by_drugs_id”, DataLoader.newDataLoader<Long,

    Company> { companyIds, _ -> CompletableFuture.supplyAsync { // SELECT * FROM company where company_id in (1, 2, 3) drugService.getCompanyByIds(companyIds) }d }c )b )a return context w 0SEFSNBUUFS w DPNQBOZ*ET    <$PNQBOZ JE $PNQBOZ JE $PNQBOZ JE > w DPNQBOZ*ET    <$PNQBOZ JE OVMM $PNQBOZ JE >
  24. $PNQMFYSFMBUJPOT type Drug { id: ID! company: Company! kinkiDrugs: [Drug!]

    } w 0OFESVHIBTNVMUJQMF,JOLJESVHT w %SVHTNJHIUIBWFTBNF,JOLJESVHT
  25. w 4&-&$5 '30.ESVHXIFSFJEJO     w 4&-&$5 '30.LJOLJ@ESVHXIFSFESVH@B@JEJO

        w 4&-&$5 '30.ESVHXIFSFJEJO    $PNQMFYSFMBUJPOT
  26. JE ESVH@B@JE ESVH@C@JE       

         w 4&-&$5 '30.LJOLJ@ESVHXIFSF ESVH@B@JEJO     w :FT ESVH@C@JEJT    CVUEVQMJDBUFE LJOLJ@ESVH $PNQMFYSFMBUJPOT
  27. "TTPDJBUFLFZXJUIWBMVF val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_companies_by_drugs_id”, DataLoader.newDataLoader<Long,

    Company> { companyIds, _ -> CompletableFuture.supplyAsync { // SELECT * FROM company where company_id in (1, 2, 3) drugService.getCompanyByIds(companyIds) }d }c )b )a return context w 0SEFSNBUUFS w DPNQBOZ*ET    <$PNQBOZ JE $PNQBOZ JE $PNQBOZ JE > w DPNQBOZ*ET    <$PNQBOZ JE OVMM $PNQBOZ JE >
  28. context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_kinki_drugs_by_drugs_id”, DataLoader.newMappedDataLoader<Long, Drug> { drugIds, _ -> }c

    )b )a
  29. context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_kinki_drugs_by_drugs_id”, DataLoader.newMappedDataLoader<Long, Drug> { drugIds, _ -> CompletableFuture.supplyAsync

    { }d }c )b )a
  30. context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_kinki_drugs_by_drugs_id”, DataLoader.newMappedDataLoader<Long, Drug> { drugIds, _ -> CompletableFuture.supplyAsync

    { // SELECT * FROM kinki_drug where drug_a_id in (1, 2, 3) val kinkis = kinkiDrugService.getKinkiDrugs(drugsIds) // SELECT * FROM drug where id in (4, 5, 6) val drugs = drugService.getDrugs(kinkis.map { it.drugBId }) }d }c )b )a
  31. context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_kinki_drugs_by_drugs_id”, DataLoader.newMappedDataLoader<Long, Drug> { drugIds, _ -> CompletableFuture.supplyAsync

    { // SELECT * FROM kinki_drug where drug_a_id in (1, 2, 3) val kinkis = kinkiDrugService.getKinkiDrugs(drugsIds) // SELECT * FROM drug where id in (4, 5, 6) val drugs = drugService.getDrugs(kinkis.map { it.drugBId }) // Create Map<KinkiDrug, List<Drug>> val kinkiMap = kinkis.associateWith { kinkiDrug -> drugs.filter { it.id == kinkiDrug.drugBId } }x }d }c )b )a
  32. context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_kinki_drugs_by_drugs_id”, DataLoader.newMappedDataLoader<Long, Drug> { drugIds, _ -> CompletableFuture.supplyAsync

    { // SELECT * FROM kinki_drug where drug_a_id in (1, 2, 3) val kinkis = kinkiDrugService.getKinkiDrugs(drugsIds) // SELECT * FROM drug where id in (4, 5, 6) val drugs = drugService.getDrugs(kinkis.map { it.drugBId }) // Create Map<KinkiDrug, List<Drug>> val kinkiMap = kinkis.associateWith { kinkiDrug -> drugs.filter { it.id == kinkiDrug.drugBId } }x // Create Map<Long, List<Drug>> drugIds.associateWith { drugId -> kinkiMap.filter { entry -> entry.key.drugAId == drugId }.values.flatten() } }d }c )b )a
  33. #FTU1SBDUJDF

  34. 0UIFS1SPCMFNT w .BOBHJOH4USJOH,FZ w /BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF w 5FTUJOHMPBEFSJNQMFNFOUBUJPO w $BDIJOH w

    /PEPDVNFOUBUJPO
  35. .BLFDVTUPN%BUB-PBEFS to be precise, BatchLoader not DataLoader

  36. context.setDataLoaderRegistry( DataLoaderRegistry().register( “key_kinki_drugs_by_drugs_id”, DataLoader.newMappedDataLoader<Long, Drug> { drugIds, _ -> CompletableFuture.supplyAsync

    { // do whatever you want }d }c )b )a .BLFDVTUPN#BUDI-PBEFS
  37. @Component class DrugDataLoader : MappedBatchLoader<Long, List<Drug>> { override fun load(ids:

    Set<Long>?): CompletionStage<Map<Long, List<Drug>>> { return CompletableFuture.supplyAsync { // do whatever you want }a }b }c .BLFDVTUPN#BUDI-PBEFS
  38. 4USJOH,FZ /BWJHBUJPO context.setDataLoaderRegistry( DataLoaderRegistry().register( // Use class name DrugDataLoader::class.qualifiedName, DataLoader.newMappedDataLoader(drugDataLoader)

    )b )a @Component class DrugDataLoader : MappedBatchLoader<Long, List<Drug>> { override fun load(ids: Set<Long>?): CompletionStage<Map<Long, List<Drug>>> { return CompletableFuture.supplyAsync { // do whatever you want }a }b }c @Suppress("unused") fun getKinkiDrugs(drug: Drug, environment: DataFetchingEnvironment): CompletableFuture<List<Drug>>? { return environment // Use class name .getDataLoader<Long, List<Drug>>(DrugDataLoader::class.qualifiedName) .load(drug.id) }b
  39. 0UIFS1SPCMFNT 㾎.BOBHJOH4USJOH,FZ 㾎/BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF w 5FTUJOHMPBEFSJNQMFNFOUBUJPO w $BDIJOH w /PEPDVNFOUBUJPO

  40. @Component class DrugDataLoader : MappedBatchLoader<Long, List<Drug>> { override fun load(ids:

    Set<Long>?): CompletionStage<Map<Long, List<Drug>>> { return CompletableFuture.supplyAsync { // do whatever you want }a }b }c .BLFDVTUPN#BUDI-PBEFS .PDLBOEUFTUMPBENFUIPE
  41. 0UIFS1SPCMFNT 㾎.BOBHJOH4USJOH,FZ 㾎/BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF 㾎5FTUJOHMPBEFSJNQMFNFOUBUJPO w $BDIJOH w /PEPDVNFOUBUJPO

  42. 4USJOH,FZ /BWJHBUJPO val context = GraphQLContext(httpServletRequest, httpServletResponse) context.setDataLoaderRegistry( DataLoaderRegistry().register( cDrugDataLoader::class.qualifiedName,

    // cache survive until drugDataLoader DataLoader.newMappedDataLoader(drugDataLoader) )b )a return context @Component class DrugDataLoader : MappedBatchLoader<Long, List<Drug>> { // keep cache here override fun load(ids: Set<Long>?): CompletionStage<Map<Long, List<Drug>>> { return CompletableFuture.supplyAsync { // do whatever you want }a }b }c
  43. 0UIFS1SPCMFNT 㾎.BOBHJOH4USJOH,FZ 㾎/BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF 㾎5FTUJOHMPBEFSJNQMFNFOUBUJPO 㾎$BDIJOH w /PEPDVNFOUBUJPO

  44. %PDVNFOUBUJPO

  45. 5IBOLZPV