Domanda

I'm at a bit of crossroads here and I wanted to ask the opinion of the community.

I am creating a chat application for our company. Now, like every chat application out there, the messages are saved on a server that allows the client, at a later point, to access older messages.

What I am lost at is using HTTP and WebSocket together in a specific situation. I am already using them, perfectly fine and everything is running smoothly. My current design choice is this:

  1. Client sends a new message event over a WebSocket to the server with a message payload
  2. The server receives this new message event
  3. The server than makes an async call out to a mongodb server that saves the message with the required message
  4. When it makes the async call to the DB, it also sends a socket event out the other clients with the provided message

The problem I see is that what if the async call to the DB fails and the DB does not actually save the message? The client may get the message, but when they log out and log back in, the message will cease to exist in the conversation because it was never saved in the DB.

Luckily, this has not happened in testing, but we all know that we can do all the testing in the world, and somehow something will break. While thinking about this problem, I came up with a new solution to the problem:

  1. Client creates a message and sends a POST request to the server
  2. The server receives this POST request
  3. Makes an async call with await on it to the DB
  4. Using promises, either the operation succeeds or it fails
  5. If it succeeds, then a socket event is sent out to the other clients and the client who sent the message receives a response from the server that it succeeded
  6. If it fails, then the client is notified, and the socket event is not sent

I know that keeping a persistent connection has less overhead as messages are sent back and forth compared to making a HTTP call on every message, but the HTTP calls will allow the client to make sure the message is actually sent and saved.

Now, my original idea can be transformed into my new idea by using async/await with it and if it fails, then don't send the message at all. I can make a new socket event that emits as an error and the client displays it accordingly.

I can't decide which would be better, and wanted to ask if one of these ways is better, or if there are even better options out there for this particular application design.

Thanks!

È stato utile?

Soluzione

Combine both, i.e. your third option. Keep the websocket messages but sequence the order of operations by awaiting the database result before notifying any clients.

Pseudocode:

function on_new_message(sender, msg):
  is_duplicate = await save_message_in_db_async(msg)
  // If this fails, catch the exception in some error handler.
  // The client could also retry
  // if they don't receive a reply within some timeout.
  // The DB can tell you whether this is a duplicate message.

  // At this point the message is saved.
  // new clients that join can load it from the DB

  sender_notification_future = notify_client_async(sender, {
      "type": "message_acknowledged",
      "msg_id": msg.id,
  })

  client_notification_futures = []
  if !is_duplicate:
    for client in connected_clients:
      if client != sender:
        client_notification_futures.push(
            notify_client_async(client, msg))

  // The order of notifications is not important,
  // so await them together
  await all(sender_notification_future, client_notification_futures)

Alternatively your second option (using a POST request to send the message) isn't a problem either, assuming that sending messages is infrequent (at most one every few seconds) or that you are using HTTP/2. This might be easier because a POST request gets a clear response, whereas websockets do not impose a request/response lifecycle. The code basically looks the same, except that you respond to the sender instead of sending them a notification.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top