https://kotlinlang.org logo
Join the conversationJoin Slack
Channels
100daysofcode
100daysofkotlin
100daysofkotlin-2021
advent-of-code
aem
ai
alexa
algeria
algolialibraries
amsterdam
android
android-architecture
android-databinding
android-studio
androidgithubprojects
androidthings
androidx
androidx-xprocessing
anime
anko
announcements
apollo-kotlin
appintro
arabic
argentina
arkenv
arksemdevteam
armenia
arrow
arrow-contributors
arrow-meta
ass
atlanta
atm17
atrium
austin
australia
austria
awesome-kotlin
ballast
bangladesh
barcelona
bayarea
bazel
beepiz-libraries
belgium
berlin
big-data
books
boston
brazil
brikk
budapest
build
build-tools
bulgaria
bydgoszcz
cambodia
canada
carrat
carrat-dev
carrat-feed
chicago
chile
china
chucker
cincinnati-user-group
cli
clikt
cloudfoundry
cn
cobalt
code-coverage
codeforces
codemash-precompiler
codereview
codingame
codingconventions
coimbatore
collaborations
colombia
colorado
communities
competitive-programming
competitivecoding
compiler
compose
compose-android
compose-desktop
compose-hiring
compose-ios
compose-mp
compose-ui-showcase
compose-wear
compose-web
connect-audit-events
corda
cork
coroutines
couchbase
coursera
croatia
cryptography
cscenter-course-2016
cucumber-bdd
cyprus
czech
dagger
data2viz
databinding
datascience
dckotlin
debugging
decompose
decouple
denmark
deprecated
detekt
detekt-hint
dev-core
dfw
docs-revamped
dokka
domain-driven-design
doodle
dsl
dublin
dutch
eap
eclipse
ecuador
edinburgh
education
effective-kotlin
effectivekotlin
emacs
embedded-kotlin
estatik
event21-community-content
events
exposed
failgood
fb-internal-demo
feed
firebase
flow
fluid-libraries
forkhandles
forum
fosdem
fp-in-kotlin
framework-elide
freenode
french
fritz2
fuchsia
functional
funktionale
gamedev
ge-kotlin
general-advice
georgia
geospatial
german-lang
getting-started
github-workflows-kt
glance
godot-kotlin
google-io
gradle
graphic
graphkool
graphql
graphql-kotlin
graviton-browser
greece
grpc
gsoc
gui
hackathons
hacktoberfest
hamburg
hamkrest
helios
helsinki
hexagon
hibernate
hikari-cp
hire-me
hiring
hongkong
hoplite
http4k
hungary
hyderabad
image-processing
india
indonesia
inkremental
intellij
intellij-plugins
intellij-tricks
internships
introduce-yourself
io
ios
iran
israel
istanbulcoders
italian
jackson-kotlin
jadx
japanese
jasync-sql
java-to-kotlin-refactoring
javadevelopers
javafx
javalin
javascript
jdbi
jhipster-kotlin
jobsworldwide
jpa
jshdq
juul-libraries
jvm-ir-backend-feedback
jxadapter
k2-early-adopters
kaal
kafka
kakao
kalasim
kapt
karachi
karg
karlsruhe
kash_shell
kaskade
kbuild
kdbc
kgen-doc-tools
kgraphql
kinta
klaxon
klock
kloudformation
kmdc
kmm-español
kmongo
knbt
knote
koalaql
koans
kobalt
kobweb
kodein
kodex
kohesive
koin
koin-dev
komapper
kondor-json
kong
kontent
kontributors
korau
korean
korge
korim
korio
korlibs
korte
kotest
kotest-contributors
kotless
kotlick
kotlin-asia
kotlin-beam
kotlin-by-example
kotlin-csv
kotlin-data-storage
kotlin-foundation
kotlin-fuel
kotlin-in-action
kotlin-inject
kotlin-latam
kotlin-logging
kotlin-multiplatform-contest
kotlin-mumbai
kotlin-native
kotlin-pakistan
kotlin-plugin
kotlin-pune
kotlin-roadmap
kotlin-samples
kotlin-sap
kotlin-serbia
kotlin-spark
kotlin-szeged
kotlin-website
kotlinacademy
kotlinbot
kotlinconf
kotlindl
kotlinforbeginners
kotlingforbeginners
kotlinlondon
kotlinmad
kotlinprogrammers
kotlinsu
kotlintest
kotlintest-devs
kotlintlv
kotlinultimatechallenge
kotlinx-datetime
kotlinx-files
kotlinx-html
kotrix
kotson
kovenant
kprompt
kraph
krawler
kroto-plus
ksp
ktcc
ktfmt
ktlint
ktor
ktp
kubed
kug-leads
kug-torino
kvision
kweb
lambdaworld_cadiz
lanark
language-evolution
language-proposals
latvia
leakcanary
leedskotlinusergroup
lets-have-fun
libgdx
libkgd
library-development
linkeddata
lithuania
london
losangeles
lottie
love
lychee
macedonia
machinelearningbawas
madrid
malaysia
mathematics
meetkotlin
memes
meta
metro-detroit
mexico
miami
micronaut
minnesota
minutest
mirror
mockk
moko
moldova
monsterpuzzle
montreal
moonbean
morocco
motionlayout
mpapt
mu
multiplatform
mumbai
munich
mvikotlin
mvrx
myndocs-oauth2-server
naming
navigation-architecture-component
nepal
new-mexico
new-zealand
newname
nigeria
nodejs
norway
npm-publish
nyc
oceania
ohio-kotlin-users
oldenburg
oolong
opensource
orbit-mvi
osgi
otpisani
package-search
pakistan
panamá
pattern-matching
pbandk
pdx
peru
philippines
phoenix
pinoy
pocketgitclient
polish
popkorn
portugal
practical-functional-programming
proguard
prozis-android-backup
pyhsikal
python
python-contributors
quasar
random
re
react
reaktive
realm
realworldkotlin
reductor
reduks
redux
redux-kotlin
refactoring-to-kotlin
reflect
refreshversions
reports
result
rethink
revolver
rhein-main
rocksdb
romania
room
rpi-pico
rsocket
russian
russian_feed
russian-kotlinasfirst
rx
rxjava
san-diego
science
scotland
scrcast
scrimage
script
scripting
seattle
serialization
server
sg-user-group
singapore
skia-wasm-interop-temp
skrape-it
slovak
snake
sofl-user-group
southafrica
spacemacs
spain
spanish
speaking
spek
spin
splitties
spotify-mobius
spring
spring-security
squarelibraries
stackoverflow
stacks
stayhungrystayfoolish
stdlib
stlouis
strife-discord-lib
strikt
students
stuttgart
sudan
swagger-gradle-codegen
swarm
sweden
swing
swiss-user-group
switzerland
talking-kotlin
tallinn
tampa
teamcity
tegal
tempe
tensorflow
terminal
test
testing
testtestest
texas
tgbotapi
thailand
tornadofx
touchlab-tools
training
tricity-kotlin-user-group
trójmiasto
truth
tunisia
turkey
turkiye
twitter-feed
uae
udacityindia
uk
ukrainian
uniflow
unkonf
uruguay
utah
uuid
vancouver
vankotlin
vertx
videos
vienna
vietnam
vim
vkug
vuejs
web-mpp
webassembly
webrtc
wimix_sentry
wwdc
zircon
Powered by Linen
apollo-kotlin
  • j

    Jaime

    04/26/2022, 2:10 AM
    Hi, I am trying to perform a query with fetchPolicy but in version 3.2.1 the method does not exist, in the documentation it is but when I do it in an example the method does not exist, could you please help me Client
    ApolloClient.Builder()
        .serverUrl(BuildConfig.HOST)
        .okHttpClient(okHttpClient.build())
        .autoPersistedQueries(
            httpMethodForHashedQueries = HttpMethod.Get,
            httpMethodForDocumentQueries = HttpMethod.Get,
        )
        .normalizedCache(cacheFactory)
        .addInterceptor(SessionInterceptor(context))
        .addInterceptor(LanguageInterceptor())
        .build()
    Query
    val queryResult = query(query).fetchPolicy(CacheFirst).execute()
    Methods normalizedCache and fetchPolicy not found in 3.2.1
    m
    s
    • 3
    • 7
  • j

    Jaime

    04/30/2022, 6:29 AM
    Hello everyone, I have a strange error
    StandaloneCoroutine was canceled
    it only happens to me when a specific api is consumed this is my apollo configuration
    val logging = HttpLoggingInterceptor()
    logging.level = HttpLoggingInterceptor.Level.NONE
    
    if (BuildConfig.DEBUG) {
        logging.level = HttpLoggingInterceptor.Level.BODY
    }
    
    val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(logging)
        .connectTimeout(8, TimeUnit.SECONDS)
        .readTimeout(8, TimeUnit.SECONDS)
        .writeTimeout(8, TimeUnit.SECONDS)
    
    apolloClient = ApolloClient.Builder()
        .serverUrl(
            BuildConfig.HOST
        )
        .okHttpClient(
            okHttpClient.build()
        )
        .normalizedCache(
            SqlNormalizedCacheFactory(context, "h_apollo.db"),
            writeToCacheAsynchronously = true
        )
        .addInterceptor(
            SessionInterceptor(context)
        )
        .addInterceptor(
            LanguageInterceptor()
        )
        .build()
    HttpLogginInterceptor print
    <-- HTTP FAILED: java.io.IOException: Canceled
    m
    a
    • 3
    • 7
  • j

    Jan Skrasek

    05/03/2022, 8:03 AM
    I have a build-convention plugin in kts where I depend on Apollo
    implementation("com.apollographql.apollo3:apollo-gradle-plugin:3.2.2")
    but I fail to use any type from Apollo plugin (ApolloExtension, etc.), no autocompletion, class is not "visible". Any idea why? I use convention plugin and configure other tools (like Detekt) and it works like a charm.
    b
    e
    • 3
    • 8
  • j

    John O'Reilly

    05/03/2022, 4:51 PM
    If not using subscriptions and instead either say polling or providing some kind of "pull to refresh" etc UI, what's best way of coordinating this when
    watch
    is being used to drive UI.....I think someone mentioned before that separately calling say
    execute
    on a query will cause cache to be updated and any UI observing the flow from
    watch
    will get the update?
    b
    • 2
    • 1
  • s

    Stylianos Gakis

    05/04/2022, 3:19 PM
    In 2.x.x, we were using some custom thing for testing graphQL responses, which let us map mutations/queries to responses without having to specify what parameters would go into them. We did that by using the QUERY_DOCUMENT and mapping that to a response and then some code which I can’t quite get to migrate to 3.x.x. So I was thinking, maybe it’s time to use the new
    MapTestNetworkTransport
    instead. So I was wondering, since I don’t think there’s a way to do that right now, would it make sense to introduce a way to map an Operation to a result using something similar to registerTestResponse but by accepting an operation no matter what the inputs? Otherwise the annoying part is that I have to in cases like the one I linked above, go inside the Activity and check what is being passed inside those queries and copy it inside the tests. But in the tests that I am doing right now I am mostly interested in how the UI reacts to a particular response, not that I am also sending the correct query. With all that said, I know that the queue approach also exists, which doesn’t care for what the query was, but simply responds with specific things in order, but in many of these tests I do want the mapping functionality of at least the Operation type to the response, just not interested in what the input to that Operation was. Does my idea make sense? Does it sound reasonable? Would it even be feasible? Should I reconsider the approach I’m taking maybe? If you have any sort of input I’d love to hear it.
    b
    • 2
    • 17
  • e

    ephemient

    05/07/2022, 10:31 AM
    I am trying to upgrade from Apollo Android 2.5 to Apollo Kotlin 3.3, but the Gradle convert and codegen tasks are failing with a method not found:
    void com.apollographql.relocated.vi.finallyStart(int)
    how should I debug this? I don't see an un-relocated plugin or a mapping to at least track down what this is
    m
    • 2
    • 69
  • j

    Jan Skrasek

    05/10/2022, 9:39 AM
    Hi guys, I was trying out testing builders and I have to admit that I got burned a bit. This is what I have to have:
    val data = UserDataQuery.Data {
      viewer = userViewer {
        userId = "userId"
        identity = identity {
          email = "<mailto:jon.snow@example.com|jon.snow@example.com>"
          name = name {
            first = "Jon"
            last = "Snow"
          }
        }
        preferredTrips = preferredTrips {
          edges = listOf(edge { node = node { kind = "MUSIC" } })
        }
      }
    }
    This was my first attempt:
    val data = UserDataQuery.Data {
      userViewer {
        userId = "userId"
        identity {
          email = "<mailto:jon.snow@example.com|jon.snow@example.com>"
          name {
            first = "Jon"
            last = "Snow"
          }
        }
        preferredTrips {
          edge { node { kind = "MUSIC" } }
        }
      }
    }
    Have you considered something like this?
    m
    s
    b
    • 4
    • 33
  • s

    Seb Jachec

    05/10/2022, 10:11 AM
    Hi! If I’m using
    MemoryCacheFactory
    &
    SqlNormalizedCacheFactory
    chained together, and
    doNotStore(true)
    is used on an Apollo (3.3.0) query with a
    CacheOnly
    fetch policy, would this prevent the memory cache being populated if only the SQL cache was read from?
    m
    • 2
    • 3
  • b

    bod

    05/17/2022, 9:55 AM
    👋 The Apollo Client Team is looking for product feedback from our community to help shape the future of our Clients. If you have some time and would like to contribute, please complete this survey by
    June 17th, 2022.
    It should take no longer then
    10 minutes
    and every response will be used to help us better understand your use of Apollo Client as well as what type of features and functionality you are looking for in the future. Thank you! 🙏
    The survey is anonymous and the data will only used for directional product decisions, feature prioritization and the long term planning of our Client roadmaps.
    🙌 3
    s
    • 2
    • 7
  • s

    Stylianos Gakis

    05/17/2022, 12:47 PM
    If I haven’t configured a httpCache in my ApolloClient, does that mean that any calls to httpFetchPolicy on any of my `ApolloCall`s have no effect? I just realized we haven’t got that setup at all, so I am trying to understand how all these calls have been hehaving. Usually we paired them with editing the Apollo cache policy at the same time as well, so I guess we’ve only been relying on the apollo cache so far but we didn’t know it 😅. Which also bring the next question. How does it work if you’ve got both of them configured at the same time? Would most normal cases only need the apollo cache? Basically I am in unfamiliar grounds with this topic and I haven’t found anything in the docs regarding how to make the decision of what we should use, so any help would be appreciated!
    b
    g
    • 3
    • 11
  • s

    Stylianos Gakis

    05/17/2022, 1:41 PM
    The 3.0 migration doc mentions: “Watchers now default to a
    CacheOnly
    refetchPolicy instead
    CACHE_FIRST
    . To keep behavior unchanged, set a
    refetchPolicy
    on your watchers:” But the snippet directly under it shows
    subscribe
    instead of
    watch()
    . Is this wrong in the sense that it should be
    .watch()
    instead, or should the title of the section be
    subscriptions
    ? I am kinda confused at that part of the document.
    m
    • 2
    • 18
  • s

    Stylianos Gakis

    05/18/2022, 3:47 PM
    Hey got a potential bug, don’t have a nice reproducible build atm to show you, but I can ask it to see if from the top of your head you know that something changed in that part of the code, if not I can try and get something to show you. Using testBuilders, I get generated code that is red. Specifically on a generated file, a testBuilder on a enum turned into a sealed class. Got an enum like
    enum CrossSellType {FOO \n BAR}
    . Apollo {} is configured with
    sealedClassesForEnumsMatching.set(listOf("CrossSellType", ...))
    and then the generated code looks like this
    "type" to resolve("type", CrossSellType.type.notNull(), knownValues().map { it.name }),
    . Problem is, knownValues() returns a list of <CrossSellType> which is in fact a sealed class now, not an enum, so
    .name
    isn’t resolving, it’s red. On Apollo 3.3.0 rn, can’t really test earlier versions as a lot of other things will be red.
    b
    • 2
    • 9
  • s

    Stylianos Gakis

    05/19/2022, 7:50 AM
    I am in a situation where I have
    Operation.Data
    and I would like to get their Json String representation. I have kind of by accident stumbled upon this function which is jvm only [1] and I think this almost is what I am looking for. However just had a question, there isn’t a
    toJsonString
    equivalent in there which is what I would like to get instead. And I can’t quite build my own version of it locally since I
    adapter()
    is private in that file and I don’t feel like copying that one into our project. I guess what I can do instead locally is imitate what the Adapter<D>.toJsonString is doing, with something like this right?
    fun Operation.Data.toJsonString(
        scalarTypeAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
        indent: String? = null,
    ): String {
        val buffer = Buffer()
        val jsonWriter = BufferedSinkJsonWriter(buffer, indent)
        toJson(jsonWriter, scalarTypeAdapters)
        return buffer.readUtf8()
    }
    But optimally, I’d like that function to be available to me from the library, where it can use the private
    adapter()
    function and not have to deal with buffers etc. in my local code. I am definitely not super confident with using them. So in short, do you think it’d fit to have a function with this body directly under this one, or should I just do the workaround I posted above (or something else if it is wrong of course)
    fun Operation.Data.toJsonString(customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, indent: String? = null): String {
      return adapter().toJsonString(this, customScalarAdapters, indent)
    }
    Random question: [1] Is this due to the
    getDeclaredField
    calls etc? Is that using reflection only available on the JVM or something?
    b
    m
    • 3
    • 33
  • s

    Stylianos Gakis

    05/19/2022, 9:39 AM
    Posting a new thread about the use-case for the above post to keep it more organized 🧵
    🙏 1
    m
    • 2
    • 6
  • s

    Stylianos Gakis

    05/23/2022, 2:51 PM
    When using test builders, what’s the story about populating a field which is of a type that I have a custom type mapper for? Specifically, I am building an object that at some point has a java.time.LocalDate in there, and I am using the JavaLocalDateAdapter specified in my apollo {} configuration in gradle. In the test builder however it’s expecting a String, and I assume it will then try to use that Adapter to map it to the object. So should I have to do something like this on these cases: (For an object that has a field called “activeFrom”)
    this.activeFrom = JavaLocalDateAdapter.toJsonString(
        LocalDate.of(2021, 4, 11), // I guess no need to pass anything for customScalarAdapters here
    )
    And then this line inside build should take are of it right?
    "activeFrom" to resolve("activeFrom", LocalDate.type, emptyList()),
    And a couple of thoughts if I assume that this is the way to do it. I’d of course optimally like to be able to just give it the object itself, but maybe this isn’t possible with the way the test builders are made. Maybe some convenience function could be generated that does this for us?
    m
    • 2
    • 22
  • f

    Filip Wiesner

    05/24/2022, 11:03 AM
    Possibly dumb question: Is there a way to share fragments between services? I have only one module but want to maintain "module like" behavior between folders in this module. For example I have
    feature/home/GetHome.graphql
    and
    shared/CategoryFragment.graphql
    . These files are each in it's own
    service
    so that I can specify a package name to the generated files. Now I want to access
    CategoryFragment
    from
    GetHome
    file but it errors out with `Cannot find fragment `Category``. Or maybe different question with the same outcome. Can I specify the package name of generated file more granularly so that the package names of different queries, mutations and fragments more resemble my defined project package structure?
    m
    • 2
    • 32
  • s

    Stylianos Gakis

    05/24/2022, 4:16 PM
    A follow-up on the original workaround to using the LocalDate in a testBuilder here When I do
    JavaLocalDateAdapter.toJsonString(LocalDate.of(2021, 4, 11))
    I get back the string
    ""2021-04-11""
    . Now if I do a test case like this:
    val localDate = LocalDate.of(2021, 4, 11)
    val jsonRepresentation = JavaLocalDateAdapter.toJsonString(localDate)
    val buffer = Buffer().write(jsonRepresentation.encodeUtf8())
    val jsonReader = BufferedSourceJsonReader(buffer)
    val parsedAgain = JavaLocalDateAdapter.fromJson(jsonReader, CustomScalarAdapters.Empty)
    assertThat(parsedAgain).isEqualTo(localDate)
    It passes fine as expected. However when I am actually using it on the builder, it crashes at the adapter, since it tries to then parse
    ""2021-04-11""
    directly, and LocalDate fails to do so. In this line in particular, the
    nextString!!
    returns the value with those double quotes and it fails. In the code snippet I put above, the encodeUtf8() part seems to turn it into a string with a single quote which makes this work, and maybe this isn’t happening in the test builders code? I am not quite sure what piece of the puzzle is going wrong, so I am posting this here in case you may have a better idea. Edit: For people who might read this at some future time and might be confused. I had simply misread that it was giving me back a String with double quoted strings, but it was just that in the debugger view in IntelliJ, to indicate a string it wraps it with quotations in the UI. The String I got back was in fact
    "2021-04-11"
    as expected and the workaround for the test builders was to strip the ‘“’ from the start and the end of the resulting string. This resulted in the String
    2021-04-11
    which was correctly parsed from the LocalDate.parse
    👀 1
    m
    • 2
    • 4
  • a

    agrosner

    05/24/2022, 6:00 PM
    Hey. There is information on multi module configuration for sharing schema. Is there any information about making a schema repo and using the same artifact in multiple projects? Is that not a recommended approach?
    m
    • 2
    • 8
  • s

    Stylianos Gakis

    05/25/2022, 8:05 AM
    On the 22nd of March, you had a talk at Android Makers. I was looking for it to re-watch something but I realize it’s not on their YouTube channel. Is it available somewhere?
    m
    • 2
    • 11
  • s

    Stylianos Gakis

    05/25/2022, 2:18 PM
    Another migration question (sorry 😅) Before we used to construct objects like this, for a query which looked like this where the return type of
    status
    is
    ContractStatus!
    which is a union with the three things you saw in the query among others. This worked fine somehow, since the __typename in the constructor put
    val __typename: String = "ContractStatus"
    and parsing it was no problem. In 3.x, we do not get that for free, and I need to go and put the correct __typename to each and every one of those constructors, and I am trying to avoid this if possible. I’ve put
    __typename = ""
    everywhere to make it compile, and it works for most cases, but not for one like here where even the test builder can’t automatically populate the __typename and I need to specify it myself, like here. So now due to this change, I would need to either adopt the testBuilders which handle this for me by forcing me to chose the typename when I need to, and even give me a comment above it with the possible correct options in the generated code (Nice work there, super convenient). Or I need to just go and hunt all the places where this problem happens and manually put in the correct __typename in all these places. Am I missing something in this migration story? I understand that the way we construct the Data objects in our tests might be a bit unorthodox, hence an easy solution may not exist, so just asking here for ideas 🤗
    m
    • 2
    • 12
  • v

    Vsevolod Kaganovych

    05/26/2022, 10:39 AM
    Hi everyone. I have a question: our scheme contains an enum with the
    type
    value. But apollo-kotlin generates the
    type
    field inside the companion object and inserts it in
    knownValues()
    . Is there a way to avoid/ignore it, cause the project doesn't compile in this case? Version - 3.3.0
    m
    b
    • 3
    • 12
  • v

    Vsevolod Kaganovych

    05/27/2022, 1:30 PM
    Hey again. I have another issue:
    HttpLoggingInterceptor
    breaks the multipart request. If I don't add it, the request works. The exception is the same as here https://github.com/apollographql/apollo-kotlin/issues/2912, but I don't add 2 of them. I have my custom interceptor for authorization and logging. Any ideas?
    b
    • 2
    • 15
  • v

    Vsevolod Kaganovych

    05/27/2022, 4:17 PM
    About the multipart: we have this kind of mutation
    mutation uploadFile($id: String!, $files: [Upload!]!)
    The request is built like this
    val upload = DefaultUpload.Builder()
                .content(file)
                .fileName(file.name)
                .build()
    
            val mutation = UploadFileMutation(id = recommendationId, files = listOf(upload))
    But the backend tells me, that the received
    files
    field is not an array and returns an error to me. Are there any modifications for the multipart request if there's only 1 item to be sent?
    b
    • 2
    • 1
  • s

    Stylianos Gakis

    05/27/2022, 11:25 PM
    Hey, while working with TestBuilders, the experience of the following could be improved a bit. When I am missing things that I haven’t specified, instead of getting a clear error of what it is that is missing, I get a random “__typename is not known at compile-time for this type. Please specify it explicitly” but this is in some seemingly random builder. Following the stacktrace I can see this comes from how the build() is specified, where there is the place where it says for example:
    "variables" to resolve("variables", some type, emptyList(), { ctors here })
    and the list of constructors is of all the possible things this could be. But some of those alternatives are builders that require the __typename to be specified, so it crashes when it tries to resolve them all. I then need to go back that stacktrace and find the last reference to my code to see which field it is that I need to define but isn’t there. Where this happens is places where there is a field which inside the generated builder has the type
    List<Map<String, Any?>>
    , so it’s not nullable and it needs to be specified. (The type inside the schema is [SomeType!]!. I am sure I’ve not explained this very well, but just wanted to type it out here for now and if it makes sense to you that’d be awesome, if not I will try to make a better explanation some other day 😅
    b
    m
    • 3
    • 13
  • s

    Stylianos Gakis

    06/02/2022, 1:58 PM
    Hey got some troubles using the apollo3.mockserver.MockServer for some tests.
    b
    w
    • 3
    • 14
  • s

    Stylianos Gakis

    06/03/2022, 9:03 AM
    Got a question about execute() on an ApolloCall. Before, there was one place where we did a simply fire and forget enqueue where I can see in the signature it only throws an exception when the call has already been executed, so we were pretty confident(right?) that one of the two callbacks would be called for sure. Now in 3.x we’ve changed it to execute() and then handle the same cases, but I was wondering, are we guaranteed for either of the two cases to be called now? For starters the coroutine could get cancelled inbetween so in that case I assume we won’t get any response, but even without cancellation, other things could go bad, like a parsing exception or whatever, and it would still not reach the
    also
    block right? In general I was thinking about this and I realize I can’t answer this question with much confidence, so I thought I’d ask here 😄
    b
    • 2
    • 4
  • n

    Nick

    06/09/2022, 12:26 AM
    I have some queries that return a lot of nested fields. I have to access some of them by going through about 5 different other fields. Is there anyway to shorten how i’d access the brand in the generated kotlin code? I don’t want to do hero.friends.person.home.kitchen.stove.brand every time I need the brand.
    {
      hero {
        friends {
          person {
              home {
                kitchen {
                    stove {
                        brand
                    }
                }  
              }
          }
        }
      }
    }
    m
    • 2
    • 3
  • s

    Stylianos Gakis

    06/13/2022, 8:25 AM
    Hey I was wondering if you have plans on when you’d like 3.3.1 to be merged back into main? Is there something particular that needs fixing before it is merged or something like that? We’re relying on some of the fixes that you helped me with recently (The swedish characters handling in the tests) that are on the Snapshot builds for now, and I’d simply like to know if there’s an ETA on the merge. We’re using the snapshot for now so that’s fine, I am not asking to rush you in any way, just to be better informed 🙌
    m
    b
    • 3
    • 3
  • s

    Stylianos Gakis

    06/13/2022, 3:15 PM
    In 2.x, we used to have subscriptionConnectionParams and subscriptionTransportFactory. In 3.x we don’t. I’ve tried constructing my ApolloClient without replacing it with anything, thinking maybe the necessary headers will be added from the OkHttp interceptor instead which didn’t work. The docs show a setup like this, but maybe this doesn’t work when there’s authentication etc on the play. Tried instead going full out by using subscriptionNetworkTransport on the builder but this also brings the same issue. I feel like I am missing some big part of the puzzle here somehow. The exception is all referencing methods that are internal to flow or the apollo library so they don’t give me a lot of ideas on where to look next 🤔 The message says:
    com.apollographql.apollo3.exception.SubscriptionOperationException: Operation error ChatMessage
    where ChatMessage is a subscription. And I don’t quite know where to go from there, hence here I am if you have any obvious thing I am missing.
    b
    • 2
    • 10
  • s

    Seb Jachec

    06/13/2022, 3:57 PM
    Hi, we’re seeing cache-only queries on both Android & iOS being unexpectedly very slow, but I’m not really sure where to go with debugging / optimising performance. Hoping someone has some pointers – not sure if we’re running into an Apollo limitation or just missing something basic around Kotlin flows! (sorry for the large message 😅) On iPhone 13 Pro w/ latest iOS, I measured ~500-600ms to perform a cache-only query for our items (just via naive
    print
    statements before/after
    query.execute()
    ) shortly after app launch. From inspecting the SQL DB, I’d guess this query reads ~2,000 SQL records worth of data. We’re using Apollo’s in-memory cache (10MB limit), chained with the SQL normalized disk cache, in a KMM app. Inspecting the cache seems easy on Android, from where I can see our disk cache DB file is ~1.1MB, with ~2150 records in total during an average flow through our app (launch app, query for account, query user’s paginated items). Each ‘item’ has ~15 immediate fields, most of which are nested fragments/objects. We’re creating a
    flow { .. }
    , and using
    withContext(Dispatchers.Main) { .. }
    inside this to execute the Apollo query for items and receive the response (we later perform some other operations within that flow, after emitting the queried cache-only items). Downstream shared view models collect via
    IO
    (Android)/`Default` Dispatcher (iOS), and further downstream, platform view models collect from shared view models on the main dispatcher/thread to present UI.
    b
    w
    m
    • 4
    • 49
Powered by Linen
Title
s

Seb Jachec

06/13/2022, 3:57 PM
Hi, we’re seeing cache-only queries on both Android & iOS being unexpectedly very slow, but I’m not really sure where to go with debugging / optimising performance. Hoping someone has some pointers – not sure if we’re running into an Apollo limitation or just missing something basic around Kotlin flows! (sorry for the large message 😅) On iPhone 13 Pro w/ latest iOS, I measured ~500-600ms to perform a cache-only query for our items (just via naive
print
statements before/after
query.execute()
) shortly after app launch. From inspecting the SQL DB, I’d guess this query reads ~2,000 SQL records worth of data. We’re using Apollo’s in-memory cache (10MB limit), chained with the SQL normalized disk cache, in a KMM app. Inspecting the cache seems easy on Android, from where I can see our disk cache DB file is ~1.1MB, with ~2150 records in total during an average flow through our app (launch app, query for account, query user’s paginated items). Each ‘item’ has ~15 immediate fields, most of which are nested fragments/objects. We’re creating a
flow { .. }
, and using
withContext(Dispatchers.Main) { .. }
inside this to execute the Apollo query for items and receive the response (we later perform some other operations within that flow, after emitting the queried cache-only items). Downstream shared view models collect via
IO
(Android)/`Default` Dispatcher (iOS), and further downstream, platform view models collect from shared view models on the main dispatcher/thread to present UI.
b

bod

06/13/2022, 4:05 PM
Hi! 👋 Is the ~500-600ms time with the chained cache, and is it consistent? If the same query is slow the first time but not the second time that would point to the SQL cache.
(also, not related to the performance issues, but there is a
ApolloCall.toFlow()
method that already returns a Flow - this may be simpler than calling
execute()
inside a
flow {...}
)
also, just to confirm, are you using 3.x?
s

Seb Jachec

06/13/2022, 4:14 PM
Hi! Yes, 500-600ms is with the chained caches, and it’s very much consistent for subsequent identical queries. We’re using Apollo 3.3.0
🙏 1
b

bod

06/13/2022, 4:16 PM
interesting
That seems very high to me indeed especially for the in-memory cache.
however it's kind of hard to gauge what's "normal" or not 🙂 Would you be able to create a repro project?
s

Seb Jachec

06/13/2022, 4:37 PM
It might be a bit tricky, but I’ll see what I can do tomorrow morning!
🙏 1
I thought I’d try using a free/open GraphQL API (AniList) in a new project setup as similarly to ours as possible, fetching a similar number of fragments/fields in the main paginated query. Our API is behind authentication and a bit hard to extract. The amount of data is much higher – on-disk cache DB file is ~4.3MB Android, ~16k records (so ~4x disk size, ~7x record count) – however much that’s actually relevant to the in-memory cache. But, the cache-only read time is only ~138ms 🤔
b

bod

06/14/2022, 10:14 AM
damn 😅 more data but faster 🤔
s

Seb Jachec

06/14/2022, 3:23 PM
Switched back to our actual app to try and narrow things down. Copied over Apollo’s internal `MemoryCacheFactory`/`MemoryCache`/`LruCache` implementations to add some logging, that seems to take ~30ms to load the records for the particular cache-only query. The
cacheInfo
on Apollo’s actual response still reports 500-600ms (from
cacheEndMillis - cacheStartMillis
), so my next guess is the conversion from cached records into actual objects is taking a while
b

bod

06/14/2022, 3:35 PM
The conversion involves instantiating Data classes and using the generated parsers to "populate" them with the data from the Maps in the cache.
👌 1
thank you for digging into this by the way! 🙏
s

Seb Jachec

06/14/2022, 4:20 PM
Do you know if there’s a way I could stop Apollo from re-generating the generated response adapters each time I build, so that I could make changes / add logging to them? I thought maybe just disabling the
generateServiceApolloSources
Gradle task might work, but it looks like that’s doing a bit more as my sync/build breaks entirely
b

bod

06/14/2022, 4:28 PM
good question! Wondering if you could just copy the generated files to your sources and temporarily comment out the Apollo plugin?
s

Seb Jachec

06/14/2022, 4:56 PM
Brilliant, that works thanks! Investigation to be continued.. 😅
:android-wave: 1
w

wasyl

06/14/2022, 8:06 PM
I wonder if you would see the same results on regular JVM, for example in a unit test. If yes, it’d be relatively easy to attach some profiling tool and actually see which methods take the most time
➕ 1
m

mbonnin

06/17/2022, 9:45 AM
@Seb Jachec did you end up finding anything? One thing that comes to mind is if you're using the
responseBased
codegen, it can generate a non-trivial amount of bytecode. Maybe simply loading these classes is what is taking the ~500ms? (although it should be faster on subsequent calls)
s

Seb Jachec

06/17/2022, 10:31 AM
Sorry for the radio silence! Wanted to come back with some more conclusive results 😅. I’ve finally been able to account for all of the time that Apollo reports in its
cacheInfo
(
cacheEndMillis - cacheStartMillis
) — ~75% of that 500-600ms in our app is being spent just loading records from the in-memory cache, ~15% is taken by the response adapters (mis-measured the time before). We’ve got custom
CacheKeyResolver
and
CacheKeyGenerator
implementations which I think might be detrimental to the specific query we’re trying to improve. We are indeed using
responseBased
codegen too though. Taken a brief break from this, will revisit next week!
🙏 1
m

mbonnin

06/17/2022, 10:40 AM
Thanks for the update! 500ms for in-memory is a loooong time. I'd be curious if using
operationBased
changes anything. We should certainly work on baseline benchmarks on our side too to have a comparison point
s

Seb Jachec

06/20/2022, 11:23 AM
operationBased
seems to be nearly the same as
responseBased
for our use-case (no significant difference). I’m looking at the just-added
@typePolicy(embeddedFields:)
now, as it could be potentially useful to cut down on the sheer number of cache records. With a few types extended, I’m so far running into
JsonDataException: Expected BEGIN_OBJECT but was NULL at path…
, although inspecting the normalised SQL DB on-disk it looks like a much tidier representation. Probably user error (and not something I can give helpful debug info for if not), so there’s more to work out yet!
b

bod

06/20/2022, 11:29 AM
Woops, this issue with
embeddedFields
was found Friday - the fix is on another branch that is not merged yet
s

Seb Jachec

06/20/2022, 11:30 AM
Ooh awesome, okay!
No urgency from me, but out of interest do you think it’s likely that branch would be merged into
main
in the next few days or weeks?
b

bod

06/20/2022, 11:39 AM
the plan is that this should go in version 3.4.0 so maybe not next few days but next few weeks yes 🙂 This release will focus on cache improvements.
m

mbonnin

06/20/2022, 12:02 PM
@Seb Jachec you can always build locally, it's usually a matter of
./gradlew publishToMavenLocal
and adding
mavenLocal()
to your repos
But we could as well very much merge this PR @bod?
b

bod

06/20/2022, 12:05 PM
well this one hasn't been reviewed 😅 But we could merge the fix independently 🙂
m

mbonnin

06/20/2022, 12:08 PM
Yeaaa, that's on me but I don't want to be the bottleneck there. Tests are ✅ and there are no API changes so it's all good for me
I just approved 🙂
b

bod

06/20/2022, 12:14 PM
all right it should be merged to main soon (waiting for CI to be sure 🙂)
:loading: 1
s

Seb Jachec

06/20/2022, 12:19 PM
Ah, thanks! 😄 I didn’t know about building locally @mbonnin – that’s good to know for the future
b

bod

06/20/2022, 1:12 PM
this is merged now! ✅
❤️ 1
✅ 1
m

mbonnin

06/20/2022, 1:13 PM
Will be in the SNAPSHOTS in ~40min :loading:
Sometimes I envy our JS colleagues 🙃
😅 2
s

Seb Jachec

06/21/2022, 1:34 PM
We’ve experimented with
@typePolicy(keyFields:,embeddedFields:)
in various configurations and found we basically have a trade-off between embedding fields (reducing cache record count, increasing read speed) and being able to query for individual objects later on. In the most extreme case, embedding our entire paginated query and all its objects/fields such that there’s a single cache record (instead of ~500) gives us a 110ms cache-only query, instead of ~350ms on Android, but obviously that would then lose the benefit of getting a cache hit when querying for a single item, and we might overwrite the entire cache record for any mutations. The schema we’re querying against looks something like this:
type PageInfo {
	# Omitted for brevity
}

type ItemConnection {
	edges: [ItemEdge!]!
	pageInfo: PageInfo
}

type ItemEdge {
	id: ID!
	node: Item!
}

type Item {
	id: ID!
	title: String!
	start: DateTimeInfo
	end: DateTimeInfo
	# ... ~10 other fields, mostly objects, omitted for brevity
}
Would you have any recommendations here as to how we could get the best of both worlds, i.e. storing cache keys for each edge, but also embedding for speedy paginated queries?
m

mbonnin

06/21/2022, 1:36 PM
This is all using only in-memory cache, right?
w

wasyl

06/21/2022, 1:37 PM
I’d hope it’s possible to do some optimizations in Apollo in the first place, before having to adjust queries. Perhaps there’s some place to trade off memory for speed in how the cache is accessed
:plus-one: 1
m

mbonnin

06/21/2022, 1:39 PM
To answer the question, I don't really have strong recommendations at this point. I'd love to get this as a benchmark in the repo though and use it as a starting point for future optimization work
s

Seb Jachec

06/21/2022, 1:41 PM
The numbers and benchmarking we’ve been doing has just been using in-memory cache alone, yep
m

mbonnin

06/21/2022, 1:48 PM
Any chance you can share the schema, typical query and response json? I can work on putting that in the repo, maybe even make in run in a device farm somewhere so that we can track the evolution of the numbers as we make changes
s

Seb Jachec

06/21/2022, 2:13 PM
Absolutely, happy to! I’ll just get hold of these and send them over
:thank-you: 1
I’ve zipped up our schema, relevant queries (and fragments), and an actual response JSON file (formatted). Let me know if there’s any more info I can give, feel free to simplify any of this if need be too
Calendar Items GraphQL.zip
🙏 1
m

mbonnin

06/21/2022, 4:19 PM
Thank you 💙! Is it ok to include this in the apollo-kotlin Github repository?
s

Seb Jachec

06/21/2022, 4:19 PM
Yes!
💙 1
m

mbonnin

06/21/2022, 7:10 PM
Early benchmark is there: https://github.com/apollographql/apollo-kotlin/blob/add-benchmarks/benchmark/src/androidTest/java/com/apollographql/apollo3/benchmark/Calendar.kt I still need to iron out everything and double check everything tomorrow but early numbers are ~74ms for reading from cache the 635KB json. Not really sure if it's good or bad but that's at least something
s

Seb Jachec

06/22/2022, 1:34 PM
Interesting! That’s similar to what we’re seeing for that amount of data on Android, but iOS is still 2x–3x the time. (Our interim app change has been to decrease the amount of data we’re fetching, to about the amount in the response I sent over, which reduced the time on both platforms)
m

mbonnin

06/22/2022, 1:56 PM
Double checking: iOS is Kotlin multiplatform too, right? Not
apollo-ios
?
s

Seb Jachec

06/22/2022, 1:58 PM
Yep! Kotlin multiplatform
👍 1
View count: 12