Bonjour,

Aujourd'hui, je veux vous faire part d'un problème qui apparait de temps en temps et qui peut être résolu par l'intermédiaire d'un plan de maintenance SQL Server. D'une manière plus générale, je peux même dire que l'exécution régulière un plan de maintenance SQL Server peut éviter de nombreux autres problèmes d'accès aux vues dans Project Web Access. C'est donc un élément à vérifier impérativement dès lors que vous rencontrez ce type d'erreur.

 

Des utilisateurs se plaignent que lorsqu'ils veulent afficher le détails de certains projets à partir du centre de Projets de PWA, l'erreur suivante apparait après quelques secondes :

  "Project Center cannot access the project(s) you're trying to view"

 

Si l'on regarde de plus près le contenu des logs ULS, on se rend compte que la cause de l'erreur est un Timeout lorsque Project Server essayes de récupérer les données à afficher dans la vue de détail :

PSI: PWA.ProjectGetProjectDetailsForGrid  SqlException occurred in DAL:

<Error>

<Class>11</Class>

<LineNumber>0</LineNumber>

<Number>-2</Number>

<Procedure></Procedure>

<Message>  System.Data.SqlClient.SqlError: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.  </Message>

 

Si l'on effectue une trace SQL (avec Profiler) au même moment, une reqête semble prendre beaucoup de temps pour s'exécuter :

declare @ResUid uniqueidentifier
declare @PermUid uniqueidentifier
declare @EntUids nvarchar(36)
declare @ViewUid uniqueidentifier
declare @P0 int
declare @P1 uniqueidentifier
declare @P2 uniqueidentifier
set @ResUid='205D0380-7946-4096-8D4C-BBDE95C0E3BD'
set @PermUid='E98573C8-7EA6-41AB-8EA4-FF8DA9730D0A'
set @EntUids=N'b82e62f4-023b-4705-9d99-e456eadbb437'
set @ViewUid='1BF11948-C8FB-4070-9FB0-BEA12AFD740F'
set @P0=1
set @P1='B82E62F4-023B-4705-9D99-E456EADBB437'
set @P2='999CE165-C52D-42E1-9A09-03390B5C1FFA'

SELECT DISTINCT tab.TASK_UID,tab.PROJ_UID INTO #T0 FROM (SELECT
                        T.TASK_UID,
                        T.PROJ_UID,
                        T.TASK_EXT_PROJ_UID,
                        CASE WHEN T.TASK_EXT_PROJ_UID IS NOT NULL AND T.TASK_IS_SUBPROJ=1 THEN 7 ELSE CASE WHEN T.TASK_OUTLINE_LEVEL=0 AND T.TASK_IS_SUMMARY=1 THEN P1.PROJ_TYPE ELSE NULL END END AS PROJ_TYPE,
                        WO.WOBJ_ISSUE_REF_CNT,
                        WO.WOBJ_DOC_REF_CNT,
                        WO.WOBJ_OTHER_REF_CNT,
                        WO.WOBJ_RISK_REF_CNT,
                        dbo.MSP_FN_HYPERLINK_HREF(T.TASK_HYPERLINK_ADDRESS, T.TASK_HYPERLINK_SUB_ADDRESS) AS TASK_HYPERLINK_HREF,
                        CASE WHEN T.TASK_BCWP=0 THEN NULL ELSE (T.TASK_CV/T.TASK_BCWP)*100 END AS TaskCVP,
                        CASE WHEN T.TASK_BCWS=0 THEN NULL ELSE (T.TASK_SV/T.TASK_BCWS)*100 END AS TaskSVP,
                        dbo.MSP_FN_DUR_IS_ELAPSED(T.TASK_DUR_FMT) AS TASK_DURATION_IS_ELAPSED
,CASE WHEN T.TASK_UID=T.TASK_PARENT_UID THEN 1 ELSE 0 END AS TASK_IS_PROJECT_SUMMARY
FROM dbo.MSP_TASKS T
                       INNER JOIN MSP_PROJECTS P1 ON T.PROJ_UID=P1.PROJ_UID
                       LEFT JOIN MSP_WEB_ADMIN_PUBLISHED_VIEW WAPV ON 0=0
                       LEFT JOIN dbo.MSP_WEB_OBJECTS WO ON (T.PROJ_UID = WO.WOBJ_PROJ_UID AND WO.WOBJ_TYPE = 2 AND T.TASK_UID=WO.WOBJ_TASK_UID)
WHERE T.TASK_TYPE<>1000 AND (T.TASK_PARENT_UID IS NOT NULL)) as tab
 INNER JOIN [MSP_WEB_FN_SEC_GetAllProjectsResCanViewByViewID](@ResUid, @PermUid, @ViewUid, 3) AS perm ON tab.PROJ_UID = perm.PROJ_UID
 INNER JOIN MSP_FN_Utility_ConvertGuidListToTable(@EntUids) AS LT ON perm.PROJ_UID=LT.TokenVal
WHERE (((tab.TASK_IS_PROJECT_SUMMARY <> @P0 OR tab.TASK_IS_PROJECT_SUMMARY IS NULL)) OR ((tab.PROJ_UID <> @P1 OR tab.PROJ_UID IS NULL))) AND  (tab.TASK_EXT_PROJ_UID IS NULL OR NOT tab.TASK_EXT_PROJ_UID IN (SELECT TokenVal FROM MSP_FN_Utility_ConvertGuidListToTable(@EntUids)))
SET NOCOUNT OFF
SELECT  Dependency.LINK_UID,Dependency.PROJ_UID,Dependency.LINK_TYPE,Dependency.LINK_SUCC_UID,Dependency.LINK_PRED_UID FROM  Dependency INNER JOIN Task ON Task.TASK_UID = Dependency.LINK_PRED_UID INNER JOIN #T0 ON #T0.TASK_UID = Task.TASK_UID AND #T0.PROJ_UID = Task.PROJ_UID;
SELECT  Task.TASK_UID,Task.PROJ_UID,Task.WOBJ_OTHER_REF_CNT,Task.WOBJ_RISK_REF_CNT,Task.WOBJ_ISSUE_REF_CNT,Task.WOBJ_DOC_REF_CNT,Task.PROJ_OPT_MINUTES_PER_DAY,Task.TASK_IS_SUBPROJ,Task.TASK_HYPERLINK_HREF,Task.PROJ_PUBLISH_STATUS,Task.TASK_PCT_COMP,Task.TASK_DURATION_IS_ELAPSED,Task.TASK_IS_CRITICAL,Task.TASK_IS_EXTERNAL,Task.TaskCritical,Task.TASK_IS_MILESTONE,Task.TASK_EXT_PROJ_UID,Task.PROJ_OPT_CURRENCY_DIGITS,Task.PROJ_OPT_CURRENCY_POSITION,Task.PROJ_OPT_CURRENCY_SYMBOL,Task.PROJ_TYPE,Task.TASK_OUTLINE_LEVEL,Task.TASK_PARENT_UID,Task.TaskSummary,Task.TASK_IS_PROJECT_SUMMARY,Task.TASK_FINISH_DATE,Task.TASK_START_DATE,Task.TASK_COST_VAR,Task.TASK_COST,Task.TB_BASE_COST_0,Task.TASK_NAME,Task.TASK_ID FROM (SELECT
                        T.TASK_UID,
                        T.PROJ_UID,
                        T.TASK_EXT_PROJ_UID,
                        CASE WHEN T.TASK_EXT_PROJ_UID IS NOT NULL AND T.TASK_IS_SUBPROJ=1 THEN 7 ELSE CASE WHEN T.TASK_OUTLINE_LEVEL=0 AND T.TASK_IS_SUMMARY=1 THEN P1.PROJ_TYPE ELSE NULL END END AS PROJ_TYPE,
                        WO.WOBJ_ISSUE_REF_CNT,
                        WO.WOBJ_DOC_REF_CNT,
                        WO.WOBJ_OTHER_REF_CNT,
                        WO.WOBJ_RISK_REF_CNT,
                        dbo.MSP_FN_HYPERLINK_HREF(T.TASK_HYPERLINK_ADDRESS, T.TASK_HYPERLINK_SUB_ADDRESS) AS TASK_HYPERLINK_HREF,
                        CASE WHEN T.TASK_BCWP=0 THEN NULL ELSE (T.TASK_CV/T.TASK_BCWP)*100 END AS TaskCVP,
                        CASE WHEN T.TASK_BCWS=0 THEN NULL ELSE (T.TASK_SV/T.TASK_BCWS)*100 END AS TaskSVP,
                        dbo.MSP_FN_DUR_IS_ELAPSED(T.TASK_DUR_FMT) AS TASK_DURATION_IS_ELAPSED
,P1.PROJ_OPT_MINUTES_PER_DAY
,T.TASK_IS_SUBPROJ
,P1.PROJ_PUBLISH_STATUS
,T.TASK_PCT_COMP
,T.TASK_IS_CRITICAL
,T.TASK_IS_EXTERNAL
,T.TASK_IS_CRITICAL AS TaskCritical
,T.TASK_IS_MILESTONE
,P1.PROJ_OPT_CURRENCY_DIGITS
,P1.PROJ_OPT_CURRENCY_POSITION
,P1.PROJ_OPT_CURRENCY_SYMBOL
,CASE WHEN T2.TASK_EXT_TASK_UID IS NULL THEN T.TASK_OUTLINE_LEVEL ELSE T3.TASK_OUTLINE_LEVEL END AS TASK_OUTLINE_LEVEL
,CASE WHEN T2.TASK_EXT_PROJ_UID IS NULL THEN T.TASK_PARENT_UID ELSE T2.TASK_PARENT_UID END AS TASK_PARENT_UID
,T.TASK_IS_SUMMARY as TaskSummary
,CASE WHEN T.TASK_UID=T.TASK_PARENT_UID THEN 1 ELSE 0 END AS TASK_IS_PROJECT_SUMMARY
,T.TASK_FINISH_DATE
,T.TASK_START_DATE
,T.TASK_COST_VAR
,T.TASK_COST
,TB0.TB_BASE_COST as TB_BASE_COST_0
,T.TASK_NAME
,CASE WHEN T2.TASK_EXT_PROJ_UID IS NULL THEN T.TASK_ID ELSE T2.TASK_ID END AS TASK_ID
FROM dbo.MSP_TASKS T
                       INNER JOIN MSP_PROJECTS P1 ON T.PROJ_UID=P1.PROJ_UID
                       LEFT JOIN MSP_WEB_ADMIN_PUBLISHED_VIEW WAPV ON 0=0
                       LEFT JOIN dbo.MSP_WEB_OBJECTS WO ON (T.PROJ_UID = WO.WOBJ_PROJ_UID AND WO.WOBJ_TYPE = 2 AND T.TASK_UID=WO.WOBJ_TASK_UID)
LEFT JOIN MSP_TASKS T2 ON (T.TASK_OUTLINE_LEVEL=0 and T.PROJ_UID=T2.TASK_EXT_PROJ_UID and T2.TASK_IS_SUBPROJ=1)
LEFT JOIN MSP_TASKS T3 ON (T.TASK_OUTLINE_LEVEL=0 and T3.TASK_EXT_TASK_UID=T.TASK_UID)
LEFT JOIN MSP_TASK_BASELINES TB0 ON (T.TASK_UID=TB0.TASK_UID AND TB0.TB_BASE_NUM = 0)
WHERE T.TASK_TYPE<>1000 AND (T.TASK_PARENT_UID IS NOT NULL)) as  Task INNER JOIN #T0 ON #T0.TASK_UID = Task.TASK_UID AND #T0.PROJ_UID = Task.PROJ_UID;
SELECT DISTINCT TaskCustomFields.CUSTOM_FIELD_UID,TaskCustomFields.PROJ_UID,TaskCustomFields.TASK_UID,TaskCustomFields.MD_PROP_UID,TaskCustomFields.MD_PROP_ID,TaskCustomFields.FIELD_TYPE_ENUM,TaskCustomFields.FLAG_VALUE,TaskCustomFields.TEXT_VALUE,TaskCustomFields.DATE_VALUE,TaskCustomFields.CODE_VALUE,TaskCustomFields.DUR_VALUE,TaskCustomFields.NUM_VALUE,TaskCustomFields.DUR_FMT,TaskCustomFields.INDICATOR_VALUE FROM  TaskCustomFields INNER JOIN Task ON Task.TASK_UID = TaskCustomFields.TASK_UID INNER JOIN #T0 ON #T0.TASK_UID = Task.TASK_UID AND #T0.PROJ_UID = Task.PROJ_UID
WHERE TaskCustomFields.MD_PROP_UID IN (
  SELECT MD_PROP_UID FROM dbo.MSP_CUSTOM_FIELDS WHERE MD_PROP_UID IN (@P2)
  UNION
  SELECT MD_PROP_UID FROM dbo.MSP_CUSTOM_FIELDS WHERE MD_PROP_UID_SECONDARY IN (@P2)
  UNION
  SELECT MD_PROP_UID_SECONDARY AS MD_PROP_UID FROM dbo.MSP_CUSTOM_FIELDS WHERE MD_PROP_UID IN (@P2)
  UNION
  SELECT MD_PROP_UID_SECONDARY AS MD_PROP_UID FROM dbo.MSP_CUSTOM_FIELDS WHERE MD_PROP_UID_SECONDARY IN (@P2)
  UNION
  SELECT WFIELD_UID as MD_PROP_UID FROM dbo.MSP_WEB_VIEW_FIELDS WHERE WFIELD_UID IN (@P2) and WFIELD_IS_CUSTOM_FIELD = 2);
DROP TABLE #T0

 

Le plan d'exécution de cette requête fait apparaitre des % Couts très importants sur des Seek ou des jointures qui s'exécutent sur des tables contenant des Clustered Indexes.

Il est donc fort probable que le taux de fragmentation des indexes soit très élevé sur ces tables, ce qui a pour but de ralentir considérablement les recherches de type SELECT.

 

Une solution rapide consiste à défragmenter les indexes de ces tables. Pour ce faire, on peut utiliser une procédure stockée définie dans cet article : http://support.microsoft.com/kb/943345/.

Pour le cas présent, il est intéressant de le faire dans la base Published.

Une fois la défragmentation faite sur tous les indexes des tables de la base Published, on obtient, lors de l'exécution de la requête qui pose problème, les résultats suivants :

 

CPU

READS

DURATION (ms)

BEFORE DEFRAG

27 980

2 494 200

30 275

AFTER DEFRAG

380

29 103

432

 

 Si l'on retourne dans le Centre de Projet et que l'on essaye d'afficher le détails des projets, cela fonctionne correctement.

Le but de cet article est de vous informer sur une cause possible d'erreur s'affichant dans Project Web Access.

La méthode de troubleshooting est toujours la même :

  1. lire les fichiers de log ULS
  2. faire une trace SQL
  3. extraire la ou les requêtes consommatrices de temps
  4.  utiliser le plan d'exécution pour déterminer où se trouve les parties les plus longues

 

Comme d'habitude, n'hésitez pas à commenter cet article en nous faisant part de votre propre expérience.

 

Bonne journée,

Marc Biarnès