- Ya
- 27 Septemba 2026, 06:14 UTC
- Mwandishi
- Kamo
- Ahadi ya
- 279a389
Chat has been failing in two ways a member notices at once: opening a conversation showed "Couldn't load messages" until Retry was pressed, and a sent message sometimes never arrived. Both were MediaService answering 500 with YugabyteDB SQLState 40001, "Restart read required" — logged daily on both pods, and every 500 the web BFF recorded this week lines up with one. Yugabyte restarts a read for free only when it is the FIRST statement of its transaction. The history load checks access on its third statement by reading the caller's membership row — the row that opening the conversation marks read in the same instant. A send writes the message and then reads the roster and the org's domain in the same transaction. Either read meeting a row written a moment earlier failed the whole request; a moment later the write had settled, which is why Retry always worked. - The chat window's requests (send, history, mark-read, chat list, edit, faces, members, receipts) carry @RetryOnDbConflict, and their catch-alls let a conflict out **************** so the advisor can re-run it in a NEW transaction. Swallowed into a 500 it was invisible to the retry. Safe: everything a send announces is after-commit. - Mark-read no longer rewrites the membership row when the pointer is already there (ChatReadPointerRepository, a guarded UPDATE). It was called several times per open, and every rewrite was a chance for the member's own history load or send to fail. The read receipt still goes out as before. - A send may carry a clientMessageId (ChatSendLedger, Redis): a retry of a send that already committed is answered with that send's response, and one still in flight with 409 SEND_IN_PROGRESS, so the client can retry without ever posting a message twice. Fails open. A ratchet **************** holds every @Transactional chat handler to the retry, and the retry to a catch that lets the conflict out. MediaControllerDbConflictTest replays the production failure through a real proxy: the conflicted attempt rolls back, a second transaction commits once, and nothing is announced for the attempt that failed. 1384 tests pass.
