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

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

Shiraji
October 09, 2019

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

Shiraji

October 09, 2019
Tweet

More Decks by Shiraji

Other Decks in Programming

Transcript

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

    View full-size slide

  2. 8IPBN*
    w ,PUMJO(SBQI2-ZFBS
    w 0QFO4PVSDFLPUMJOHSBQIRMTBNQMF
    w -PWF,PUMJO
    w 8PSLTGPS6CJF *OD
    @shiraji @shiraj_i

    View full-size slide

  3. ,PUMJO'FTU
    IUUQTTQFBLFSEFDLDPNTIJSBKJTFSWFSTJEFLPUMJOQMVTHSBQIRM

    View full-size slide

  4. /1SPCMFN
    w 0OFDPNQBOZIBTNBOZESVHT
    w 0OFESVHIBTPOFDPNQBOZ

    View full-size slide

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

    View full-size slide

  6. w NVMUJQMFESVHTBUPODF
    /1SPCMFN

    View full-size slide

  7. w 4&-&$5'30.ESVHXIFSFJEJO

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

    View full-size slide

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

    w 4&-&$5'30.DPNQBOZXIFSFJEJO

    w
    /1SPCMFN

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  15. /XJUIHSBQIRMKBWBTUBSUFS
    val context = GraphQLContext(httpServletRequest, httpServletResponse)
    context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_companies_by_drugs_id”,
    DataLoader.newDataLoader { companyIds, _ ->
    CompletableFuture.supplyAsync {
    // SELECT * FROM company where company_id in (1, 2, 3)
    drugService.getCompanyByIds(companyIds)
    }d
    }c
    )b
    )a
    return context

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  22. /XJUIHSBQIRMKBWBTUBSUFS
    @Component
    class DrugResolver : GraphQLResolver {
    @Suppress("unused")
    fun getCompanies(drug: Drug, env: DataFetchingEnvironment): CompletableFuture>? {a
    return env
    .getDataLoader>(“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 { companyIds, _ ->
    CompletableFuture.supplyAsync {
    // SELECT * FROM company where company_id in (1, 2, 3)
    drugService.getCompanyByIds(companyIds)
    }d
    }c
    )b
    )a
    return context

    View full-size slide

  23. "TTPDJBUFLFZXJUIWBMVF
    val context = GraphQLContext(httpServletRequest, httpServletResponse)
    context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_companies_by_drugs_id”,
    DataLoader.newDataLoader { 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
    >

    View full-size slide

  24. $PNQMFYSFMBUJPOT
    type Drug {
    id: ID!
    company: Company!
    kinkiDrugs: [Drug!]
    }
    w 0OFESVHIBTNVMUJQMF,JOLJESVHT
    w %SVHTNJHIUIBWFTBNF,JOLJESVHT

    View full-size slide

  25. w 4&-&$5'30.ESVHXIFSFJEJO

    w 4&-&$5'30.LJOLJ@ESVHXIFSFESVH@B@JEJO

    w 4&-&$5'30.ESVHXIFSFJEJO

    $PNQMFYSFMBUJPOT

    View full-size slide

  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

    View full-size slide

  27. "TTPDJBUFLFZXJUIWBMVF
    val context = GraphQLContext(httpServletRequest, httpServletResponse)
    context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_companies_by_drugs_id”,
    DataLoader.newDataLoader { 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
    >

    View full-size slide

  28. context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_kinki_drugs_by_drugs_id”,
    DataLoader.newMappedDataLoader { drugIds, _ ->
    }c
    )b
    )a

    View full-size slide

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

    View full-size slide

  30. context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_kinki_drugs_by_drugs_id”,
    DataLoader.newMappedDataLoader { 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

    View full-size slide

  31. context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_kinki_drugs_by_drugs_id”,
    DataLoader.newMappedDataLoader { 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>
    val kinkiMap = kinkis.associateWith { kinkiDrug ->
    drugs.filter { it.id == kinkiDrug.drugBId }
    }x
    }d
    }c
    )b
    )a

    View full-size slide

  32. context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_kinki_drugs_by_drugs_id”,
    DataLoader.newMappedDataLoader { 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>
    val kinkiMap = kinkis.associateWith { kinkiDrug ->
    drugs.filter { it.id == kinkiDrug.drugBId }
    }x
    // Create Map>
    drugIds.associateWith { drugId ->
    kinkiMap.filter { entry ->
    entry.key.drugAId == drugId
    }.values.flatten()
    }
    }d
    }c
    )b
    )a

    View full-size slide

  33. #FTU1SBDUJDF

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    “key_kinki_drugs_by_drugs_id”,
    DataLoader.newMappedDataLoader { drugIds, _ ->
    CompletableFuture.supplyAsync {
    // do whatever you want
    }d
    }c
    )b
    )a
    .BLFDVTUPN#BUDI-PBEFS

    View full-size slide

  37. @Component
    class DrugDataLoader : MappedBatchLoader> {
    override fun load(ids: Set?): CompletionStage>> {
    return CompletableFuture.supplyAsync {
    // do whatever you want
    }a
    }b
    }c
    .BLFDVTUPN#BUDI-PBEFS

    View full-size slide

  38. 4USJOH,FZ/BWJHBUJPO
    context.setDataLoaderRegistry(
    DataLoaderRegistry().register(
    // Use class name
    DrugDataLoader::class.qualifiedName,
    DataLoader.newMappedDataLoader(drugDataLoader)
    )b
    )a
    @Component
    class DrugDataLoader : MappedBatchLoader> {
    override fun load(ids: Set?): CompletionStage>> {
    return CompletableFuture.supplyAsync {
    // do whatever you want
    }a
    }b
    }c
    @Suppress("unused")
    fun getKinkiDrugs(drug: Drug, environment: DataFetchingEnvironment): CompletableFuture>? {
    return environment
    // Use class name
    .getDataLoader>(DrugDataLoader::class.qualifiedName)
    .load(drug.id)
    }b

    View full-size slide

  39. 0UIFS1SPCMFNT
    㾎.BOBHJOH4USJOH,FZ
    㾎/BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF
    w 5FTUJOHMPBEFSJNQMFNFOUBUJPO
    w $BDIJOH
    w /PEPDVNFOUBUJPO

    View full-size slide

  40. @Component
    class DrugDataLoader : MappedBatchLoader> {
    override fun load(ids: Set?): CompletionStage>> {
    return CompletableFuture.supplyAsync {
    // do whatever you want
    }a
    }b
    }c
    .BLFDVTUPN#BUDI-PBEFS
    .PDLBOEUFTUMPBENFUIPE

    View full-size slide

  41. 0UIFS1SPCMFNT
    㾎.BOBHJOH4USJOH,FZ
    㾎/BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF
    㾎5FTUJOHMPBEFSJNQMFNFOUBUJPO
    w $BDIJOH
    w /PEPDVNFOUBUJPO

    View full-size slide

  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> {
    // keep cache here
    override fun load(ids: Set?): CompletionStage>> {
    return CompletableFuture.supplyAsync {
    // do whatever you want
    }a
    }b
    }c

    View full-size slide

  43. 0UIFS1SPCMFNT
    㾎.BOBHJOH4USJOH,FZ
    㾎/BWJHBUJOHUPMPBEFSJNQMFNFOUBUJPOBOEJUTDBMMFF
    㾎5FTUJOHMPBEFSJNQMFNFOUBUJPO
    㾎$BDIJOH
    w /PEPDVNFOUBUJPO

    View full-size slide

  44. %PDVNFOUBUJPO

    View full-size slide