realm/realm-graphql-service

Subscriptions get shared between clients

Closed this issue · 8 comments

Subscribe filters don't work properly when 2 different clients subscribing to the same object but with different filters. For example Client A runs:

const query = gql`subscription {
  books(query: "author.name = 'Ernest Cline'") {
    lastReadDatetime
  }
}`
client.subscribe({ query }).subscribe({
  next: data => {
    console.log(data)
  },
  error: err => {
    console.log(err);
  }
})

Works great with only Client A running the code; however, when Client B runs

const query = gql`subscription {
  books(query: "author.name = 'Charles Dickens'") {
    lastReadDatetime
  }
}`
client.subscribe({ query }).subscribe({
  next: data => {
    console.log(data)
  },
  error: err => {
    console.log(err);
  }
})

Both clients subscribe to both filters, switching between them for every update.

I'd like to follow up on this issue

@LucidComplex , it doesn't look like you shared any kind of error message or the exact behavior you're seeing. I just see two code snippets

@mgeerling There is no error message. I believe I've clearly described the behavior that is occurring. The subscriptions are being shared. That is, when you subscribe to 2 different things, such as subscribing to 2 different authors, both observers receive update for both authors -- even updates not intended for them.

Are you using node.js for this? Could you give us a clear repro case? (i.e. your models, some data, your code, etc)

This would help us save time. Also, is there a reason you are using GraphQL rather than our javascript library?

We are creating an electron app, so it's javascript. We can't use the javascript library for this, because it's not node.js.

Currently I have this:

const query = gql`subscription{
    vehicles(query:"company.code='${this.props.user.company.code}'"){
        uuid
        plateNumber
        bodyNumber
        type
        company {
            code
        }
        latitude
        longitude
    }
}`;
const observable = this.props.client.subscribe({ query });
this.subscription = observable.subscribe({
    next: (data) => {
        if (data == null || data.data == null || data.data.vehicles == null) {
            return;
        }
        const areOwnVehicles = data.data.vehicles.some(vehicle => {
            return vehicle.company.code === this.props.user.company.code;
        });
        if (areOwnVehicles) {
            this.setState({ vehicles: data.data.vehicles });
        }
    },
    error: (err) => {
    }
});

The above subscribes me to updates of vehicles of a certain company. I have my own check with areOwnVehicles because having this code run at the same time but with different companies causes both to receive updates of both companies.

An easy repro would be this:
Realm model:

const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: {type: 'int', default: 0},
  }
};

Setup:

realm.create('Car', {make: 'Nissan', model: 'Almera'});
realm.create('Car', {make: 'Honda', model: 'City'});

Subscribe (via GraphQL):

const nissanQuery = gql`subscription{
  cars(query: "make = 'Nissan'") {
    model
    miles
  }
}`;
const nissanObservable = client.subscribe({ query: nissanQuery });
const nissanSubscription = nissanObservable.subscribe({
    next: (data) => {
        console.log('nissan', data);
    },
    error: (err) => {
    }
});
const hondaQuery = gql`subscription{
  cars(query: "make = 'Honda'") {
    model
    miles
  }
}`;
const hondaObservable = client.subscribe({ query: hondaQuery });
const hondaSubscription = hondaObservable.subscribe({
    next: (data) => {
        console.log('honda', data);
    },
    error: (err) => {
    }
});

Insert new Honda to demonstrate subscription:

realm.create('Car', {make: 'Honda', model: 'Civic'})

Both subscriptions above will run their next method.

I tried to reproduce this but was unable to do so. I'm uploading a simple node.js app that attempts to follow your instructions. Just update the serverUrl, username, and password values. Then run npm install and then npm start.

It assumes you have a user that has a Realm under /~/cars - if you don't, you can uncomment the setupRealm call to create the Realm and update its schema.

When you run testGraphQL, it should output

nissan triggered
honda triggered

the first time it is run. Then, you can use Studio to add new Car instances. When I was testing that, whenever I added a Car with a make "Honda", that would output honda triggered and similarly, when I added a "Nissan", I got nissan triggered. I was never able to get the wrong subscription to trigger in my tests.

While I realize that node.js is different from electron, I believe the results are still relevant because the client cannot alter the server behavior and it was much easier for me to prepare a node script to test this out.

graphql.zip

I tried what you said, and indeed the issue didn't occur. However, I created 2 clients by copying the directory and running both of them at the same time. I set one of the client to subscribe to Nissans, and the other to Hondas, and the issue occurred there.

Thanks. With the new instructions, I was able to reproduce it and we'll have a fix shortly.