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

Losing subscription strangely #2516

Open
ScreamZ opened this issue Jan 18, 2024 · 4 comments
Open

Losing subscription strangely #2516

ScreamZ opened this issue Jan 18, 2024 · 4 comments
Labels

Comments

@ScreamZ
Copy link

ScreamZ commented Jan 18, 2024

Hi, I'm using Kuzzle SDK JS for a React Native mobile application and ESP32 devices (using WebSocket custom calls in C/C++ for them).

Both are using API Keys for authentication. So for C++, I'm just setting the jwt key in the pushed JSON.

I'm observing the same behavior on both devices. Or at least something very close.

Context (Environment)

  • Kuzzle version: 2.27.4
  • Node.js version: 16.20.2
  • SDK version: 7.10.7
  • Authenticated through: API KEY (k-apikey)

Observations

Everything works well for some time. Then at some point, I'm losing real-time subscriptions.

  • On JS SDK side it looks like a disconnect event is triggered.
  • On ESP32 no disconnect event is emitted from the socket client (https://github.com/Links2004/arduinoWebSockets)
  • When losing a subscription the device is still connected as I can publish new events from it. It has just not received new events from the subscription as it looks not subscribed anymore.

Possible cause

I've seen using an observer on the server side that strange things occur

1. Subscribe on the Server side to presence (click to unfold code)
 kuzzle.realtime.subscribe(
    index,
    "presence",
    {},
    (notification) => {
      if (notification.type !== "user" || !notification.volatile.device) return;

      console.log(
        `Presence change for device ${notification.volatile.device} to ${notification.user}`,
      );
    },
    { users: UserOption.all, scope: ScopeOption.none },
  );
}
  1. Boot the device and subscribe.
  2. Disconnect abruptly (like killing the app, or pressing the restart button on an ESP32)
  3. and therefore boot and subscribe again.

-> Everything works.

Until the server receives the timeout event of the first socket being closed abruptly.

Presence change for device ESP_1 to in
Presence change for device ESP_1 to in
Presence change for device ESP_1 to out
Presence change for device ESP_1 to out

-> It seems it clears all new subscriptions. ?

Possible cause

If confirmed, I'm pretty sure the issue is about hotelClerk thinking it's the same connection and, therefore, the same subscription and clear all ? But it seems the ws instance is generating a uuid v4 for connectionID, so I don't know how its possible.

Might it be related to API Keys usage ?

Issue happens on SDK JS too, so I'm not sure this is related to my implementation

Workaround

  • Regularly sending a subscribe request with the same payload as the subscription ID is idempotent ?
  • Rely on polling at regular intervals ?
@ScreamZ ScreamZ added the bug label Jan 18, 2024
@etrousset
Copy link
Member

etrousset commented Jan 18, 2024

This could possibly be due to the websocket layer sending a PING message and the device no responding with a PONG message? Can you check this?

https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#pings_and_pongs_the_heartbeat_of_websockets

@ScreamZ
Copy link
Author

ScreamZ commented Jan 18, 2024

@etrousset Thanks for helping me with that :)

While this could justify the issue with the disconnection, it seems it has been handled already by ArduinoWebsocket library:

https://github.com/Links2004/arduinoWebSockets/blob/93707d455fff3c667aedd92b485276101f5e6eba/src/WebSockets.h#L269

https://github.com/Links2004/arduinoWebSockets/blob/93707d455fff3c667aedd92b485276101f5e6eba/src/WebSockets.cpp#L490-L500

But what about losing the subscription in case of reconnecting with the JavaScript SDK?

@ScreamZ
Copy link
Author

ScreamZ commented Jan 30, 2024

After building my own minimal client with reconnection I can confirm that Kuzzle sdk JS seems not be the issue.

After losing a connection and reconnect I still receive notifications for a small duration then at some point I get nothing.

Now I need to check if it's related to react native or Kuzzle server itself.

@ScreamZ
Copy link
Author

ScreamZ commented Jan 30, 2024

I made a custom websocket proxy and i can confirm that now I don't lose my subscriptions anymore.

I can confirm that kuzzle works strangely under the scenario of a react native mobile application going out of range of a local area network and coming back. The websocket reconnect (connection 2) and connection 1 drop after the server timeout resulting in connection 2 losing subscriptions.

Here is the proxy server code.

You can just use bun to power a uWebSocket server.

import type { ServerWebSocket } from "bun";

const kuzzleSocket = new WebSocket("ws://192.168.1.140:7512");
let reactSocketSet = new Set<ServerWebSocket<unknown>>();
let heartBeatRef: NodeJS.Timer | null = null;

kuzzleSocket.addEventListener("open", () => {
  console.log("✅ Connected to Kuzzle");
  heartBeatRef = setInterval(() => kuzzleSocket.send(JSON.stringify({ p: 1 })), 5000);
});

kuzzleSocket.addEventListener("message", (event) => {
  if (event.data !== '{"p":2}') console.log(`🦝 Kuzzle Received -> forwarding to React`);
  reactSocketSet.forEach((reactSocket) => reactSocket.send(event.data));
});

kuzzleSocket.addEventListener("close", () => {
  heartBeatRef && clearInterval(heartBeatRef);
  console.log("❌ Kuzzle closed socket");
});

const server = Bun.serve({
  fetch(req, server) {
    const success = server.upgrade(req);
    if (success) {
      // Bun automatically returns a 101 Switching Protocols
      // if the upgrade succeeds
      return undefined;
    }

    return new Response("Not supported!");
  },
  websocket: {
    open(ws) {
      reactSocketSet.add(ws);
      console.log("✅ React opened socket. Socket set size :", reactSocketSet.size);
    },
    async message(ws, message) {
      if (message !== '{"p":1}') {
        console.log(`⚛️ React Received -> forwarding to Kuzzle`);
      }
      kuzzleSocket.send(message);
    },
    close(ws) {
      reactSocketSet.delete(ws);
      console.log("❌ React closed socket. Socket set size :", reactSocketSet.size);
    },
  },
});

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

2 participants