Project Online: как интерпретировать распространенные ошибки рабочего процесса

При выполнении рабочих процессов в Project Online задействовано множество механизмов и настроек, которые могут повлиять на поток этих процессов или полностью «перекрыть» его. В этой статье я опробую некоторые из участвующих процессов и постараюсь помочь вам понять, почему рабочие процессы могут быть приостановлены и почему очень часто такая приостановка не имеет никакого отношения к сбоям в нашем сервисе. Но это вовсе не значит, что я попытаюсь переложить ответственность на клиентов, поскольку вам действительно сложно понять, что произошло или почему. Мы работаем над исправлением ситуации, но пока...

Если вы уже знакомы с рабочими процессами и хотите сразу перейти к сути, прокрутите страницу вниз на метр или около того (остановитесь сразу после скриншота Excel), чтобы добраться до того места, где я начинаю свое повествование об ошибках.

Сначала давайте узнаем, где можно отслеживать статус рабочих процессов. На главной странице рабочего процесса проекта отражаются текущая фаза, этап и статус, присвоенный вашему рабочему процессу. В данном случае я не показываю какой-либо определенный статус этапа, но очень рекомендую что-нибудь здесь писать, чтобы ваши пользователи знали, что происходит, и, возможно, чего еще ожидать. Разверните узел All Workflow Stages (Все этапы рабочего процесса), как сделал я, чтобы лучше понять, где вы находитесь. При наличии нужного уровня разрешений в правом нижнем углу вы увидите опцию, позволяющую вывести дополнительные сведения о рабочем процессе (Additional Workflow Data):

Щелкнув Additional Workflow Data (Дополнительные сведения о рабочем процессе), вы перейдете к следующему экрану и увидите информацию о статусе, записанную в историю рабочего процесса, а также внутренний статус. Если вы работаете над выявлением и устранением ошибок, то можете видеть, что параметр Internal Status (Внутренний статус) показывает, что процесс приостановлен. В моем случае этот рабочий процесс в конечном итоге будет приостановлен после определенного количества попыток; сам статус отображается под значком информации рядом со Started (Запущен) на следующем скриншоте.

Чтобы увидеть статус рабочего процесса, можно также перейти в раздел Site Settings (Настройки сайта), Site Workflows (Рабочие процессы сайта) и Show all workflows (Показать все рабочие процессы).

К сожалению, здесь показан просто список рабочих процессов, без привязки к проектам, тем не менее, вы поймете, какие процессы были приостановлены.

Кроме того, доступ к сведениям о рабочих процессах можно получить через OData. Для этого добавьте _api/ProjectData/ProjectWorkflowStageDataSet к вашему адресу PWA URL. Вот созданный мной лист Excel, он заполнен данными из этого канала на моем сервере. Я отфильтровал таблицу по столбцу StageStatus, чтобы видеть только ошибки и процессы в режиме ожидания, при этом некоторые из ожидающих процессов также в конечном счете будут прерваны с ошибкой, но я нетерпелив...

Итак, вы примерно поняли, где искать сведения о статусе, поэтому давайте перейдем к обсуждению некоторых распространенных ошибок, с которыми вы можете столкнуться. Их можно увидеть, когда рабочий процесс приостанавливается, или в переходный период, когда выполняется определенное количество повторных попыток, как показано выше. Но вы обязательно увидите что-то подобное, если решите узнать статус рабочего процесса. Все процессы в конечном итоге будут приостановлены после определенного числа повторных попыток, если только проблема не была устранена до завершения цикла повторений.

Каждая из этих ошибок возникла из-за проблемы с разрешениями учетной записи, выполняющей рабочий процесс. Я не знаю, какая ошибка отобразится в каждом конкретном сценарии, поскольку на самом деле причина представляет собой комбинацию из проблемы с разрешениями и действием, которое пытается выполнить ваш рабочий процесс. Но какая учетная запись пользователя столкнулась с проблемой и не смогла получить доступ?

При создании проекта запускается рабочий процесс с использованием токена, зарегистрированного в системе пользователя, и процесс выполняется в контексте этого пользователя до тех пор, пока не завершится или по какой-то причине не будет прерван. Контекст пользователя не будет изменен даже при смене владельца проекта! Вызвать сбой могут такие проблемы, как отсутствие необходимых разрешений для запуска, и последний пример иллюстрирует именно этот сценарий — PDP вызвал ошибку 403. Обнаружить это довольно просто, поскольку обычно очевидно, кто запустил рабочий процесс, если он прерывается не сразу. Но что произойдет, если пользователь, запустивший рабочий процесс, ушел из компании и стал неактивен? Это может привести к возникновению ошибок «Forbidden: Access Denied» и «InternalServerError: GeneralSecurityAccessDenied». Устранить эту проблему поможет перезапуск рабочих процессов. Еще один пример, с которым я столкнулся, — когда учетная запись, выполняющая рабочий процесс, утратила некоторые разрешения, например в результате переноса из группы Project Manager в группу Team Member либо после ограничения прав доступа (изменения разрешений или даже проектов!) для всей группы. Вы увидите те же два сообщения, в зависимости от действий, выполняемых в рабочем процессе.

Как насчет этого?

Здесь мы видим немного больше информации, чем раньше. Рабочий процесс пытается считать данные из поля DateTime и сталкивается с проблемой. Мы видим здесь ссылку на propertyid — 21af92c1-a389-e711-80c8-00155de44f0a — как правило, это ссылки на пользовательские поля, чаще всего на уровне проекта. Открою вам быстрый способ, который я обычно использую, чтобы установить принадлежность идентификатора GUID конкретному полю: просто нажмите View Source (Открыть исходный код) на странице Custom Field and Lookup Table (Пользовательское поле и таблица подстановки) и найдите этот GUID. Как видите, в моем случае поиск указывает на ProjDate.

В своем рабочем процессе я считывал эту дату и выставлял значение переменной, но поскольку дата не была задана, я получил исключение NullReferenceException, что привело к возникновению ошибки InternalServerError, которую вы видите в этом рабочем процессе. В данном случае во избежание проблем необходимо убедиться, что дате присвоено значение, прежде чем считывать его.

Приведенные примеры относятся к проблемам, которые в большинстве случаев лежат в зоне ответственности клиента, хотя, если вы не знали, что пользователь, который запустил рабочий процесс, больше не активен, то вы и не могли этого учесть. Но мы уже работаем над улучшениями в этой области, так что следите за новостями. Мы также стараемся понять, как эффективнее выявить истинную причину проблем и сообщить вам о ней, чтобы вы точно знали, что делать дальше. То есть вы должны четко понимать, достаточно ли просто перезапустить процесс или нужно проанализировать журнал вызовов, если проблема на нашей стороне. В качестве примера можно привести ошибку, которую вы, возможно, недавно видели:

RequestorId: 2d852b8a-41ad-5d86-0000-000000000000. Details: System.InvalidOperationException: Incomplete closure detected while loading subroutines for workflow 66149d57-9e52-4a69-970d-2324e6132ec1 in scope .

Я поясню, как исправить ситуацию, и это также поможет вам понять, почему иногда возникают ошибки, которые, как мы утверждали, были исправлены. Команда специалистов по рабочим процессам устранила эту проблему довольно давно, и у них есть автоматизированные процедуры для исправления работающего (хотя в большинстве случаев точнее будет сказать «ждущего» или «спящего») рабочего процесса, который может использовать старую копию этого кода. Но в самой автоматизированной процедуре также возникли некоторые сложности, поэтому клиенты достаточно долго продолжали сталкиваться с проблемой уже после ее исправления. Мы должны установить это исправление и, опять же, перезапуск процесса также позволяет достичь нужного результата — мы получим последнюю версию кода.

Надеемся, что это поможет вам разгадать некоторые из загадок приостановленных рабочих процессов и понять, можно ли исправить ситуацию и как это сделать. Я буду публиковать новые статьи по мере появления новостей о более информативных описаниях ошибок, о дальнейших действиях или о новых рекомендациях по использованию рабочих процессов.

Специально для поисковых систем привожу текст из представленных выше скриншотов с ошибками:

Retrying last request. Next attempt scheduled in less than one minute. Details of last request: HTTP InternalServerError to https://brismithpjo.sharepoint.com/sites/pwa/\_api/ProjectServer/WorkflowActivities/ReadDateTimeProperty(projectId='ab3e700f-d789-e711-80d9-00155de43c08',propertyId='21af92c1-a389-e711-80c8-00155de44f0a') Correlation Id: a7da1060-9fbd-8fe0-82e6-678a0b0a2b6e Instance Id: 99d1d76b-daf2-45b2-9489-c9abdb7cce69

Retrying last request. Next attempt scheduled after 8/25/2017 4:11 PM. Details of last request: HTTP InternalServerError to https://brismithpjo.sharepoint.com/sites/pwa/\_api/ProjectServer/WorkflowActivities/ReadyToLeaveProjectStage(projectId='4c4532af-b289-e711-80ce-00155de4880e') Correlation Id: a7da1060-9fbd-8fe0-a513-0efff7b5357b Instance Id: fc8f01bd-cb49-4cdc-9ed1-f53dff2d640f
ProjectServerError(s) LastError=GeneralSecurityAccessDenied Instructions: Pass this into PSClientError constructor to access all error information

Retrying last request. Next attempt scheduled after 8/25/2017 6:10 PM. Details of last request: HTTP Forbidden to https://brismithpjo.sharepoint.com/sites/pwa/\_api/ProjectServer/WorkflowActivities/ReadyToLeaveProjectStage(projectId='1627a7c1-ad89-e711-80de-00155de42a0d') Correlation Id: a7da1060-9fbd-8fe0-8554-9c3f3931dc99 Instance Id: 7620f076-f706-4692-80d3-6d02a75f9fff
Access denied. You do not have permission to perform this action or access this resource.

RequestorId: 9e975a9a-2037-bbfa-0000-000000000000. Details: An unhandled exception occurred during the execution of the workflow instance. Exception details: System.ApplicationException: HTTP 403 {"Transfer-Encoding":["chunked"],"X-SharePointHealthScore":["0"],"X-MSDAVEXT_Error":["917656; Access+denied.+Before+opening+files+in+this+location%2c+you+must+first+browse+to+the+web+site+and+select+the+option+to+login+automatically."],"X-SP-SERVERSTATE":["ReadOnly=0"],"DATASERVICEVERSION":["3.0"],"SPClientServiceRequestDuration":["74"],"SPRequestDuration":["142"],"SPRequestGuid":["9e975a9a-2037-bbfa-95aa-aa690c69f8a2"],"request-id":["9e975a9a-2037-bbfa-95aa-aa690c69f8a2"],"Strict-Transport-Security":["max-age=31536000"],"X-FRAME-OPTIONS":["SAMEORIGIN"],"MicrosoftSharePointTeamServices":["16.0.0.6809"],"X-Content-Type-Options":["nosniff"],"X-MS-InvokeApp":["1; RequireReadOnly"],"Cache-Control":["max-age=0, private"],"Date":["Fri, 18 Aug 2017 09:33:41 GMT"],"P3P":["CP=\"ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI\""],"Server":["Microsoft-IIS\/8.5"],"X-AspNet-Version":["4.0.30319"],"X-Powered-By":["ASP.NET"]} at System.Activities.Statements.Throw.Execute(CodeActivityContext context) at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation) Exception from activity Throw If Sequence Sequence TryCatch Sequence Microsoft.SharePoint.WorkflowServices.Activities.RetryForDurationPolicy HttpPost Sequence Microsoft.Office.Project.Server.WorkflowActivities.EnterProjectStage Sequence Flowchart Sequence BSTest.WorkflowXaml_f9e81a98_8151_4254_a524_eab9f8e99079