Skip to content

Potential alias collision for subscriptions #2032

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

Open
vicary opened this issue Oct 29, 2024 · 4 comments
Open

Potential alias collision for subscriptions #2032

vicary opened this issue Oct 29, 2024 · 4 comments

Comments

@vicary
Copy link
Member

vicary commented Oct 29, 2024

When the exact same arguments are used across multiple subscriptions with different selections, GQty will generate the same query alias for them, leading to an incorrect cache update.

Consider the following case in React:

const sub1 = useSubscription();
sub1.foo({ bar: "baz" }).field1;

const sub2 = useSubscription();
sub2.foo({ bar: "baz" }).field2;

While sub1 and sub2 subscribes with a different ID in the WebSocket layer, they are stored to the cache under the same alias because of .foo({ bar: "baz" }), essentially gluing "next" message payloads for both sub1 and sub2.

The solution is to generate a unique alias for each subscription.


Thanks @jsjs_dev for reporting in Discord.

@vicary vicary mentioned this issue Oct 29, 2024
8 tasks
@brummetj
Copy link

brummetj commented Apr 10, 2025

Hey @vicary, do you have any updates on this collision issue?

Our caching is causing collisions on our queries, not using subs. Is there a way to update the alias internally so it generates IDs instead of just hashing the arguments?

this is running on NextJS server components, btw, using the resolve

@vicary
Copy link
Member Author

vicary commented Apr 11, 2025

@brummetj custom alias is not possible at the moment because the design of the core does not support it, but it's definitely a good escape hatch to have.

I believe this issue is shared by all normalized GraphQL cache available out there, that's where GraphQL fragments become useful. You may achieve the same by creating a query hook that pre-selects all fields used in your apps.

import { useQuery } from "~/gqty";

export const useQueryFoobar = () => {
  const query = useQuery();

  const {
    foo, // used by component A
    bar, // used by component B
  } = query;

  return query;
};

// ~/components/A.tsx
export default function A() {
  const query = useQueryFoobar();

  return <>{query.foo}</>;
}

// ~/components/B.tsx
export default function B() {
  const query = useQueryFoobar();

  return <>{query.bar}</>;
}

@brummetj
Copy link

brummetj commented Apr 14, 2025

@vicary ahh interesting. will this help with collision on nextJS?

maybe a little more info.. The issue we are seeing when two users hit the browser at the same time, the cache will reuse another users cache and they will see each other's data, which can contain PII. we were able to reproduce it by having two users login at the same time.

I was able to work around it by adding unique session id arguments to the queries/mutations due to your comment on this issue about using exact same arguments for queries.

any thoughts on how we can avoid the collisions without using ID fields in the arguments?

example

type Query {
  foo(id: ID): bar -> won't cause collision
  foo: bar -> will cause data collisions and users can see another user data
}

@vicary
Copy link
Member Author

vicary commented Apr 14, 2025

@brummetj Interesting use case for the same app to support two concurrent user sessions, if that's what you mean by "at the same time".

For most apps, calling cache.clear(); when a user logs out is sufficient.

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

No branches or pull requests

2 participants