Runbook: Exchange sync по пользователю (ошибки папки/доступа/почтового ящика)¶
1. Когда использовать¶
Признаки инцидента:
- Ошибки
ErrorFolderNotFound/ErrorInvalidFolderId/ErrorNonExistentMailbox. - Синхронизация пользователя «самопроизвольно» отключилась.
- Растёт счётчик неудачных попыток Exchange.
- Встречи не появляются/не обновляются у конкретного пользователя.
2. Как это работает (по коду)¶
2.1 Режимы пользователя¶
Режим определяется парой флагов пользователя:
Disabled->DoSyncWithExchange = falseиCalendarEwsDirectSync = false.Sync->DoSyncWithExchange = trueиCalendarEwsDirectSync = false.Direct->DoSyncWithExchange = falseиCalendarEwsDirectSync = true.
Admin API для управления:
POST /api/admin/users/{userId}/exchange/reset-failsPOST /api/admin/users/{userId}/exchange/update-sync-modeGET /api/admin/users/{userId}/service/settings(для контроля текущего режима/счётчиков)
2.2 Fail-counter и автоотключение¶
- Ошибки Exchange увеличивают
SyncWithExchangeFailedAttempts. - Лимит берётся из
CalendarExchangeRetryLimit. - Если лимит
!= 0и попытки> limit, режим пользователя переводится вDisabled. reset-failsсбрасывает счётчик в0, но сам режим при необходимости надо выставить отдельно.
2.3 Обработка типовых ошибок Exchange¶
ExchangeExceptionsProcessor обрабатывает два класса ошибок:
AccessDenied:- очищаются
EwsFolders/EwsFolderPermissionsдля текущего пользователя, - инкрементируется fail-counter.
- Ошибки «плохой пользователь/ящик»:
ErrorNonExistentMailbox,ErrorFolderNotFound,ErrorInvalidFolderId,ErrorInvalidSmtpAddress,ErrorMailRecipientNotFound,ErrorItemNotFound,- при наличии
SmtpAddressв деталях могут отключаться пользователи с этим email, - иначе инкремент fail-counter по user.
2.4 Дополнительный восстановительный контур¶
Есть job EnableDoSyncWithExchangeJob, который может повторно включить sync пользователям, отключённым именно «по ошибке Exchange» (через служебный флаг Sync.Exchange.DisabledByError).
3. Что смотреть при разборе (чек-лист)¶
- Зафиксировать
userId, время ошибки, текстServiceError. - Проверить сервисные настройки пользователя (
service/settings): ewsMode,syncWithExchangeFailedAttempts,calendarExchangeRetryLimit,sid.- Если попытки выше лимита:
- сбросить fails,
- вернуть нужный режим sync вручную.
- Проверить, какой класс ошибки:
AccessDenied-> проверка доступа/impersonation/кеша папок,- mailbox/folder ошибки -> проверка email/папки/calendar mailbox.
- Для
AccessDeniedубедиться, что после очистки кэша папок и повторной попытки состояние изменилось. - Если проблема массовая (несколько пользователей) — проверять Exchange service settings и инфраструктурную доступность EWS.
4. Симптом -> вероятная причина -> проверка¶
| Симптом | Вероятная причина | Что проверить первым |
|---|---|---|
У пользователя ewsMode = Disabled после серии ошибок |
превышен retry limit | syncWithExchangeFailedAttempts vs calendarExchangeRetryLimit |
ErrorAccessDenied |
права/impersonation/доступ к папке | записи EwsFolders/EwsFolderPermissions, параметры impersonation |
ErrorNonExistentMailbox/ErrorInvalidSmtpAddress |
некорректный email/mailbox | Users.Email, детали SmtpAddress в логе |
ErrorFolderNotFound/ErrorInvalidFolderId |
невалидный folder link/изменение папки в Exchange | повторная инициализация папок, проверки кэша папок |
После reset-fails ситуация не изменилась |
сбросили только счётчик, но не режим | текущий ewsMode и update-sync-mode |
Событие создано в Outlook, комментарий в задачу не приходит, нет записей в AutomationScriptsLog (Type=10) |
Settings.DefaultEwsServiceId = NULL и нет AD-профилей, привязанных к EWS → GetAllExchangeServices() вернул пустой список → подписки не стартовали (тихий отказ без лога) |
SELECT DefaultEwsServiceId FROM Settings; проверить EwsServiceSettings; подробнее — provider-ews.md раздел «Инициализация» |
5. SQL для быстрой диагностики¶
declare @user_id int = 0;
-- Состояние пользователя по Exchange
select
u.UserID,
u.Email,
u.DoSyncWithExchange,
u.CalendarEwsDirectSync,
u.SyncWithExchangeFailedAttempts,
u.SID
from dbo.Users u
where u.UserID = @user_id;
-- Служебные флаги Exchange по пользователю
select
uiev.UserID,
uie.SysName,
uiev.Value,
uiev.IntValue
from dbo.UserInfoExtValues uiev
join dbo.UserInfoExt uie
on uie.ID = uiev.InfoExtID
where uiev.UserID = @user_id and
lower(uie.SysName) in (
'sync.exchange.disablederror',
'sync.exchange.itemsstate'
)
order by
uie.SysName;
-- Кеш папок Exchange для пользователя
select top (200)
ef.UserID,
ef.ServiceId,
ef.ChangeDate,
ef.CalendarFolderValue,
ef.InboxFolderValue
from dbo.EwsFolders ef
where ef.UserID = @user_id
order by
ef.ChangeDate desc;
-- Кеш прав на папки Exchange
select top (200)
ep.UserID,
ep.FolderOwnerId,
ep.ServiceId,
ep.ChangeDate,
ep.FolderValue
from dbo.EwsFolderPermissions ep
where ep.UserID = @user_id
order by
ep.ChangeDate desc;
-- Настройки сервиса Exchange (без пароля)
select
ess.ServiceId,
ess.EwsUrl,
ess.EwsLogin,
ess.EwsDomain,
ess.UseImpersonalization,
ess.UseSIDForImpersonalization
from dbo.EwsServiceSettings ess
order by
ess.ServiceId;
6. Что приложить в задачу разработчикам¶
userId, время ошибки, точныйServiceError.- Снимок
GET /api/admin/users/{userId}/service/settings. - Результаты SQL из блока выше.
- Что уже делали:
reset-fails, смена режима (update-sync-mode), повтор синка. - Масштаб: один пользователь или массово.
Связанные документы¶
docs/domains/calendar/data-flow.mddocs/domains/calendar/backend.mddocs/domains/integrations/exchange-integration-overview.mddocs/domains/integrations/backend.mdadmin.md