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 · 6 comments
Open

Losing subscription strangely #2516

ScreamZ opened this issue Jan 18, 2024 · 6 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);
    },
  },
});

@etrousset
Copy link
Member

Hey,
If you don't want Kuzzle nor the application tu detect a deconnection to fast when out of reach of network connection, you can setup a longer ping interval in both side.
On Kuzzle side, in kuzzlerc there is the websocket.idleTimeout parameter:

"idleTimeout": 60000,

On SDK side there a pingInterval setting : https://docs.kuzzle.io/sdk/js/7/protocols/websocket/constructor/#arguments

Let us know if this helps

@ScreamZ
Copy link
Author

ScreamZ commented Mar 5, 2025

Hi @etrousset, glad to see you're still at Kuzzle.

Thanks for late suggestion, i've been trying this always, not sucessfully.

I've only been able to solve it using the hacky proxy way specified above. And therefore within the 2024 year, without any answer from support, I've rebuild a full low level server based on Bun WS to handle my disconnection very specifically and achieve a good result with simpler subscription mechanism (I don't need the full stack of kuzzle in my case)

The issue was about subscription not being renew, not user not disconnected.
If you want to dig deeper, you can try with a react native application connected to a kuzzle server with some subscription, and try move out of range or your wifi, just at the limit and you might see that you're not subscribed anymore.

I also made an attempt to rewrite a full SDK, this might solve the issue, but with the new SDK and the proxy I didn't faced the issue anymore so I didn't dig deeper.

And therefore I moved away from the stack.

I've been using it privately, but the new SDK is more optimized for react application and usage with useEffect. It doesn't implement a full specification but for realtime, it's way more accurate https://github.com/ScreamZ/Kuzzle-RealTime-SDK, I let you with a link.

Have a nice day

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