matrix-js-sdk icon indicating copy to clipboard operation
matrix-js-sdk copied to clipboard

events are processed in wrong order, cannot chat anymore

Open Ainias opened this issue 3 years ago • 4 comments

When somebody kicks me from a room, but invites me shortly after without my client making a sync in between (because the chat app was closed), the leave and invite event is processed in the wrong order.

The result of /sync looks fine, the origin_server_ts of the invite is later than the one of the leave-Event. But when listening to client.on('event', ...), the invite Event comes first and the leave-Event second. This leads to a wrong state in the (persistent) store. Because of this, the app thinks the user is not in the room anymore and won't receive any new messages. The new state is "invite", but the user cannot accept the invittation, since it is not displayed.

Here is the result of /sync

/sync result
{
  "next_batch": "s127655_2818331_4835_288127_209897_85_169_14999_0",
  "presence": {
    "events": [
      {
        "type": "m.presence",
        "sender": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
        "content": {
          "presence": "online",
          "last_active_ago": 17,
          "currently_active": true
        }
      }
    ]
  },
  "device_lists": {
    "left": [
      "@ctbot:chat.krz.tools"
    ]
  },
  "device_one_time_keys_count": {
    "signed_curve25519": 0
  },
  "org.matrix.msc2732.device_unused_fallback_key_types": [],
  "device_unused_fallback_key_types": [],
  "rooms": {
    "invite": {
      "!sQgeWUrcqXvxvzkUqw:chat.krz.tools": {
        "invite_state": {
          "events": [
            {
              "type": "m.room.avatar",
              "state_key": "",
              "content": {
                "url": ""
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.canonical_alias",
              "state_key": "",
              "content": {
                "alias": "#ctg_4b1a00f5-7753-469f-bf95-6bb3b7c1414d:chat.krz.tools"
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.create",
              "state_key": "",
              "content": {
                "room_version": "9",
                "creator": "@ctbot:chat.krz.tools"
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.join_rules",
              "state_key": "",
              "content": {
                "join_rule": "invite"
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.name",
              "state_key": "",
              "content": {
                "name": "Chattest"
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.topic",
              "state_key": "",
              "content": {
                "topic": "Gruppen-Chat einer Gruppe in testsilas.krz.tools"
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.member",
              "state_key": "@ctbot:chat.krz.tools",
              "content": {
                "membership": "join"
              },
              "sender": "@ctbot:chat.krz.tools"
            },
            {
              "type": "m.room.member",
              "sender": "@ctbot:chat.krz.tools",
              "content": {
                "membership": "invite",
                "displayname": "Admin Admin"
              },
              "state_key": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
              "origin_server_ts": 1671178716243,
              "unsigned": {
                "replaces_state": "$LbfQGkLijKPasqYizadgfKnvLW_OKYK4izrqDLegduU",
                "prev_content": {
                  "reason": null,
                  "membership": "leave"
                },
                "prev_sender": "@ctbot:chat.krz.tools",
                "age": 41096
              },
              "event_id": "$elHhtOnT4Xy9mdNaHeJzVu5nVhKnRbjDEiFpN8BxBrc"
            }
          ]
        }
      }
    },
    "leave": {
      "!sQgeWUrcqXvxvzkUqw:chat.krz.tools": {
        "timeline": {
          "events": [
            {
              "type": "m.room.member",
              "sender": "@ctbot:chat.krz.tools",
              "content": {
                "reason": null,
                "membership": "leave"
              },
              "state_key": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
              "origin_server_ts": 1671178710797,
              "unsigned": {
                "replaces_state": "$pTOIJv3ViDwR7QyzOCLQatCwUyv5fQpNYSOaDIHXcdI",
                "prev_content": {
                  "membership": "join",
                  "displayname": "Admin Admin"
                },
                "prev_sender": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
                "age": 46542
              },
              "event_id": "$LbfQGkLijKPasqYizadgfKnvLW_OKYK4izrqDLegduU"
            }
          ],
          "prev_batch": "s127652_2818310_4829_288126_209897_85_169_14999_0",
          "limited": false
        },
        "state": {
          "events": []
        },
        "account_data": {
          "events": []
        }
      }
    }
  }
}

And the Events from client.on('event', ...):

Invite Event
{
  "type": "m.room.member",
  "sender": "@ctbot:chat.krz.tools",
  "content": {
    "membership": "invite",
    "displayname": "Admin Admin"
  },
  "state_key": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
  "origin_server_ts": 1671178716243,
  "unsigned": {
    "replaces_state": "$LbfQGkLijKPasqYizadgfKnvLW_OKYK4izrqDLegduU",
    "prev_content": {
      "reason": null,
      "membership": "leave"
    },
    "prev_sender": "@ctbot:chat.krz.tools",
    "age": 41096
  },
  "event_id": "$elHhtOnT4Xy9mdNaHeJzVu5nVhKnRbjDEiFpN8BxBrc",
  "room_id": "!sQgeWUrcqXvxvzkUqw:chat.krz.tools"
}
Leave Event
{
  "type": "m.room.member",
  "sender": "@ctbot:chat.krz.tools",
  "content": {
    "reason": null,
    "membership": "leave",
    "displayname": "Admin Admin"
  },
  "state_key": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
  "origin_server_ts": 1671178710797,
  "unsigned": {
    "replaces_state": "$pTOIJv3ViDwR7QyzOCLQatCwUyv5fQpNYSOaDIHXcdI",
    "prev_content": {
      "membership": "join",
      "displayname": "Admin Admin"
    },
    "prev_sender": "@ct_a18c34c2-ea65-4359-956e-b4c237c96e47:chat.krz.tools",
    "age": 46542
  },
  "event_id": "$LbfQGkLijKPasqYizadgfKnvLW_OKYK4izrqDLegduU",
  "room_id": "!sQgeWUrcqXvxvzkUqw:chat.krz.tools"
}

Ainias avatar Dec 16 '22 08:12 Ainias

Clients can't rely on origin_server_ts for ordering given that it may come from different servers in the distributed system with out of sync clocks. The issue here is a spec one, in no definition on how to handle a room being in both invite and leave sections of the sync response, ideally the server would only include it in the latest "correct" section.

t3chguy avatar Dec 16 '22 09:12 t3chguy

@t3chguy thanks for the response. Is there a possibility to sync the member states for a given room? Then I could at least sync the states at the app start and have the right state again after a restart. At the moment because of persistent storage the app saves the wrong state

Ainias avatar Dec 16 '22 09:12 Ainias

I'd suggest

client.stopClient();
client.store.deleteAllData();

then restart your client - this will not clear any e2ee

t3chguy avatar Dec 16 '22 09:12 t3chguy

Is there a better way then to delete all data? If I delete all data on appstart, then I can use a memory store directly.

Ainias avatar Dec 16 '22 09:12 Ainias