Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normalised Cache Not Using Data from Different Query #3345

Open
reubn opened this issue Feb 25, 2024 · 1 comment
Open

Normalised Cache Not Using Data from Different Query #3345

reubn opened this issue Feb 25, 2024 · 1 comment
Labels

Comments

@reubn
Copy link

reubn commented Feb 25, 2024

Summary

I have a very simple use case that doesn't seem to be handled by the normalised cache properly.

I have a query that returns a list of Countrys using a CountryInfo fragment.

query AllCountriesQuery {
  countries {
    ...CountryInfo
  }
}
fragment CountryInfo on Country {
  code
  name
  emoji
}

I then have a second query that queries for a specific Country, returning the same fragment.

query CountryDetailQuery($code: ID!) {
  country(code: $code) {
    ...CountryInfo
  }
}

The data returned for the first query populates the cache, but is not used for the second query. Apollo returns the following error.

GraphQLExecutionError(path: country, underlying: ApolloAPI.JSONDecodingError.missingValue)

I have set up custom cache keys, but Apollo seems to only correctly return cached data for the same query, despite the 2 pieces of data in the cache having the same cache key. This is unexpected.

By introspecting the SQLiteNormalizedCache database I can see that the data from the first query is stored and keyed correctly, but this not appear to be read in the second. The test case behaves identically with the InMemoryNormalizedCache.

I can see that a similar issue was reported #842, and if this behaviour is intentional then the question is: how can I implement cache redirects to normalise identical objects across queries?

Thanks!

Minimal test case: https://github.com/reubn/apolloiOSBugTestCase

Artboard

Version

1.9.0

Steps to reproduce the behavior

Minimal test case: https://github.com/reubn/apolloiOSBugTestCase

Logs

No response

Anything else?

No response

@reubn reubn added bug Generally incorrect behavior needs investigation labels Feb 25, 2024
@Iron-Ham
Copy link
Contributor

Iron-Ham commented Feb 26, 2024

Hi @reubn

I took a quick look at your minimal test case, and I think that there's a bit of a mis-understanding of how the cache functions. Within your CountryDetailView, you're attempting to create an object of type CountryDetailQuery.Data.Country by fetching a CountryDetailQuery via the cache. However, as you've never fetched CountryDetailQuery before, you don't have a cache entry for that query.

As you said, you're really looking for the country field – which is being supplied by a shared fragment CountryInfo. You can, instead of storing CountryDetailQuery.Data.Country, store CountryInfo. And instead of fetching CountryDetailQuery, you can do a read operation on the cache for a CountryInfo.

Your fetch data function becomes something like this:

  func fetchData() {
    GraphQL.shared.apollo.store.withinReadTransaction { transaction in
      let countryInfo = try transaction.readObject(ofType: CountryInfo.self, withKey: "Country:\(code)")
      self.country = countryInfo
    }
  }

More important that having a fix (which may or may not work for your use-case) is understanding why what you're attempting to do fails.

Imagine, for a moment, that your CountryDataQuery was much more complex:

query CountryDetailQuery($code: ID!) {
  country(code: $code) {
    ...CountryInfo
    population
    officialLanguage
    currentLeader
    governmentType
    # etc
  }
}

At this point, there's a very clear divergence between the AllCountriesQuery.Country and CountryDataQuery.Country data types. Further, when we ask the cache to fetch a whole query, we must have executed that query before in the past. That's because of how queries are keyed.

For example, a CountryDetailQuery with code AD would be keyed as QUERY_ROOT.CountryDetailQuery(code: "AD"), with selections you have keyed similarly (QUERY_ROOT.CountryDetailQuery(code: "AD").country(code: "AD"). When you ask the cache to read for a query, there is no entry in the cache that resolves to that query, as we've never executed it. At its core, returnCacheDataDontFetch will resolve to code that's quite similar to the code I've written above for fetchData but will instead tell the transaction to read for the CountryDetailQuery, not the CountryInfo fragment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants