Скорее всего, персональный чат ошибочно неактивен (обычно это происходит из-за некорректного логаута, например, путём удаления сервера с RTS). Потребуется ID этого чата. Получить его можно несколькими способами. Если чат между двумя серверами (cts1+cts2 или rts+cts), и на одном из них чат можно найти в админке — можно взять ID оттуда. Но чаще всего чат находится на одном сервере, и его не найти, так как неактивные чаты в админке скрыты. Тогда можно узнать ID чата в веб-/десктоп-приложении: 1. Откройте Настройки > О программе > много раз нажмите на логотип, пока не откроется инженерное меню. 2. Включите тумблер Инфо для разработчика в меню сообщения. Закройте инженерное меню. 3. В нужном чате щёлкните правой кнопкой мыши любое сообщение и выберите Инфо для разработчика. 4. В панели справа посмотрите значение groupChatId — это и есть идентификатор чата. Если чат пустой у всех участников чата, тогда не получится вызвать меню для сообщений в нём. Можно попробовать выполнить звонок в этом чате, скачать стейт со стороны любого участника чата и, ориентируясь на HUID участников, найти нужный groupChatId в этом стейте. Неактивные чаты в списке чатов в панели администратора не отображаются, но перейти к ним всё равно можно: 1. открываем в админке любой другой чат. 2. в адресной строке браузера меняем его ID на взятый ранее groupChatId. 3. должен открыться наш чат с Активен: False. После того как убедились, что в админке конкретного сервера участника чат неактивный, то, имея на руках ID чата, выполняем исправление одного чата. В результате чат активируется и появится в админке. Выполняем последовательно команды в консоли сервиса messaging следующие команды: 1. Заходим в консоль messaging: cd /opt/express && DPL_PULL_POLICY=never dpl --dc exec messaging ./bin/messaging remote_console или: docker exec -it {messaging_container} ./bin/messaging remote_console если у вас kubernetes, {cts-messaging} - заменить на имя пода kubectl exec -it {cts-messaging} -- ./bin/messaging remote_console 2. Копируем код, приведённый ниже, и нажимаем Enter: defmodule Messaging.Troubleshooting do defmodule Chats do alias CcsSdk.API.Messaging.V1, as: MessagingV1 alias Messaging.Chats.{ChatsContext, GroupChat} alias Messaging.Chats.RoutingContext alias Messaging.Chats.GroupChat.Changeset.ChatServersChangeset alias Messaging.Chats.GroupChat.Commands.ChatSyncPullCommand alias Messaging.Chats.GroupChat.Queries.GetChatsByHuidInMembersQuery alias Messaging.Events.Kafka.EventHandler.Decoders.ChatParamsDecoder alias Messaging.Events.{SystemEventsContext, ChatEventsContext} alias Messaging.Event alias Messaging.Repo require Logger def resync_chat_servers(chat_id) do {:ok, base} = ChatsContext.get_chat_base_from_cache(chat_id) RoutingContext.sync(base.members) {:ok, chat} = ChatsContext.get_chat(chat_id, preload: true) ChatsContext.activate_chat(chat) chat |> ChatServersChangeset.apply() |> Repo.update!(force: true) ChatsContext.clear_chat_cache(chat.id, "chat_sync") end def update_chat_servers(chat_id) do with {:ok, chat} <- ChatsContext.get_chat(chat_id, preload: true) do chat |> ChatServersChangeset.apply() |> Repo.update!(force: true) ChatsContext.clear_chat_cache(chat_id, "chat_sync") end end def refresh_chat(chat_id) do with {:ok, chat_data} <- MessagingV1.sync_data(chat_id), decoded_data = ChatParamsDecoder.decode(chat_data), {:ok, result} <- ChatsContext.sync_chat(decoded_data, []) do produce_chat_updated(result.group_chat) end end def sync_chat(chat_id, server_id) do ChatSyncPullCommand.execute(chat_id, server_id) end def repair(chat_id) do with :ok <- refetch_routing_info_for_chat(chat_id) do produce_chat_updated(chat_id) end end def repair_user(user_huid) do with {:ok, _} <- RoutingContext.sync([user_huid]), {:ok, chats} <- GetChatsByHuidInMembersQuery.execute(user_huid) do Enum.each(chats, fn(chat) -> clear_cache(chat.id) produce_chat_updated(chat) produce_routing_changed(chat, user_huid) end) end end def repair_user_with_chats(user_huid) do with {:ok, _} <- RoutingContext.sync([user_huid]), {:ok, chats} <- GetChatsByHuidInMembersQuery.execute(user_huid) do Enum.each(chats, fn(chat) -> refresh_chat(chat.id) clear_cache(chat.id) end) end end def produce_chat_updated(%GroupChat{} = chat) do with {:ok, chat_updated} <- SystemEventsContext.chat_updated(chat) do Event.emit(chat_updated, chat: chat) {:ok, [id: chat.id]} else error -> Logger.error("Troubleshooting.Chats produce chat updated error: #{chat.id} #{error}") {:error, [id: chat.id, reason: error]} end end def produce_chat_updated(chat_id) do with {:ok, chat} <- ChatsContext.get_chat(chat_id, preload: true) do produce_chat_updated(chat) end end def produce_routing_changed(%GroupChat{} = chat, user_huid) do backward = %GroupChat{chat | chat_member_routing_infos: []} routing_info = Enum.find(chat.chat_member_routing_infos, &(&1.user_huid == user_huid)) forward = %GroupChat{chat | chat_member_routing_infos: [routing_info]} with {:ok, routing_changed} <- ChatEventsContext.routing_changed(backward, forward) do Event.emit(routing_changed, chat: chat) {:ok, [id: chat.id]} else error -> Logger.error("Troubleshooting.Chats produce chat routing changed error: #{chat.id} #{error}") {:error, [id: chat.id, reason: error]} end end def activate_chat(chat_id) do with {:ok, chat} <- ChatsContext.get_chat(chat_id), {:ok, updated_chat} <- ChatsContext.activate_chat(chat) do clear_cache(chat_id) produce_chat_updated(updated_chat) end end def refetch_routing_info_for_chat(chat_id) do {:ok, chat} = ChatsContext.get_chat(chat_id) with {:ok, _} <- RoutingContext.sync(chat.members) do ChatsContext.clear_chat_cache(chat_id, "chat_sync") end end def refetch_routing_info(user_huid) do with {:ok, _} <- RoutingContext.sync([user_huid]), {:ok, chats} <- GetChatsByHuidInMembersQuery.execute(user_huid) do Enum.each(chats, &(ChatsContext.clear_chat_cache(&1.id, "chat_sync"))) end end def clear_cache(chat_id) do ChatsContext.clear_chat_cache(chat_id, "chat_sync") end def validate_chat(chat_id) do {:ok, local_chat} = ChatsContext.get_chat(chat_id, preload: true) {:ok, local_chat_from_cache} = ChatsContext.get_chat_from_cache(chat_id) check1 = validate_chat_member_routing_info( {:local, local_chat.chat_member_routing_infos}, {:local_cache, local_chat_from_cache.chat_member_routing_infos} ) check2 = validate_keys( {:local, ChatsContext.keys(local_chat)}, {:local_cache, local_chat_from_cache.keys} ) [check1, check2] end defp validate_chat_member_routing_info({tag1, cmr_info1}, {tag2, cmr_info2}) do i1 = cmr_info1 |> Enum.map(&map_routing_info/1) |> Enum.sort_by(&(&1.user_huid)) i2 = cmr_info2 |> Enum.map(&map_routing_info/1) |> Enum.sort_by(&(&1.user_huid)) validate_diff(:routing_info, {tag1, i1}, {tag2, i2}) end defp map_routing_info(routing_info) do routing_info |> Map.from_struct() |> Map.take([ :active, :cts_id, :cts_key_id, :ets_id, :rts_id, :rts_key_id, :user_huid, :user_kind ]) end defp validate_keys({tag1, keys1}, {tag2, keys2}) do validate_diff(:keys, {tag1, keys1}, {tag2, keys2}) end defp validate_diff(set_name, {tag1, set1}, {tag2, set2}) do missing1 = case set1 -- set2 do [] -> [] diff -> [{tag2, diff}] end missing2 = case set2 -- set1 do [] -> [] diff -> [{tag1, diff}] end case missing1 ++ missing2 do [] -> {set_name, :ok} missing -> {set_name, {:missing, missing}} end end end end Затем команду: Messaging.Troubleshooting.Chats.resync_chat_servers("ID_чата") Если решение выше не подошло, проверяем эти признаки: * В панели администратора ищем проблемный чат, в списке пользователей у пользователя видим неверное значение в столбце Conn Type (например, rts вместо cts); * В роутинге чата у других участников отображается неверная принадлежность этого пользователя к серверу (трафик от него отображается через RTS, а не через CTS). То есть пользователь корпоративного сервера (синий) в роутинге участников проблемного чата может ошибочно отображаться как зарегистрированный на региональном транспортном сервере (зелёный). * Открываем Chat JSON чата. Параллельно открываем ключи пользователя, ищем ключ cts самой последней версии и сверяем с ключом пользователя в Chat JSON. В чате будет неверный ключ cts пользователя, скорее всего, предыдущей версии. Если присутствует хотя бы один из вышеперечисленных признаков: 1. Открываем консоль сервиса messaging: cd /opt/express && DPL_PULL_POLICY=never dpl --dc exec messaging ./bin/messaging remote_console если deployka отдельно, то: - до бэкенда 3.13: docker exec -ti cts_messaging_1 bin/messaging remote_console - бэкенд 3.13 и выше: docker exec -ti cts-messaging-1 bin/messaging remote_console если у вас kubernetes, {cts-messaging} - заменить на имя пода kubectl exec -it {cts-messaging} -- ./bin/messaging remote_console 2. Обновляем роутинг у проблемного пользователя и затем очищаем кэш его проблемного чата следующей командой: Messaging.Chats.Routing.ChatMemberRoutingInfoFetcher.get_and_save!(["HUID ПОЛЬЗОВАТЕЛЯ"]) Messaging.Chats.GroupChatCache.clear("ID ЧАТА")