Content Deployment – The complete Guide – Part 9 – Job and Timerjob

Relationship between Job and Timerjob

How many Timerjobs per Job?

Each content deployment job (class ContentDeploymentJob) has one or two timer jobs (class ContentDeploymentJobDefinition) associated. One timer job is created together with the content deployment job – that is the one responsible to do the scheduled deployment if enabled. A second (one-time) timer job is created if requested by a user by chosing the “Run Now” option in the UI. That allows to persist the scheduling even if a user decides to start a content deployment job manually.

The ID of the timer job responsible for scheduled deployment is maintained in the TimerJobId field of the list item related to the specific content deployment job. The ID of the timer job created through “Run Now” is maintained in the “LastAsyncRunJobId” field of the list item. After the content deployment job is completed the LastAsyncRunJobId is changed to an empty GUID = {00000000-0000-0000-0000-000000000000}.

With other words: a quick check to see if a running deployment is started through “Run Now” or through scheduling you can check the LastAsyncRunJobId field. If it contains a not empty GUID the job has been stated through Run Now.

Timerjob Naming Conventions

Depending on the type of the job (export or import) and how the associated timer job was created (run now vs. scheduled) there are different naming conventions.

As you might remember, we have one content deployment job on the source system and one “shadow” content deployment job on the target system responsible to perform the import (see part 2 and part 4 for details about the shadow job).

For both of these jobs you may have up to two timer jobs.

Let’s assume the following Scenario:

  • Title of ContentDeploymentJob on source system: “deploy 4444
  • ItemId of ContentDeploymentJob on source system: “be315bb7-e7be-4af4-a8bc-1cd369eb5220
  • Title of “shadow” ContentDeploymentJob on target system: “Remote import job for job with sourceID = be315bb7-e7be-4af4-a8bc-1cd369eb5220
  • ItemId of “shadow” ContentDeploymentJob on target system: “14c0deb5-f0a2-45b4-a899-09843edfc181

Timerjob title on Source system

On the source system you can find the following content deployment timer jobs:

For scheduled Deployment:

  • Content Deployment Timer Job for ContentDeploymentJobDefinition: <title-of –contentdeploymentjob-on-source-system>

    E.g. “Content Deployment Timer Job for ContentDeploymentJobDefinition: deploy 4444

For “Run Now” Deployment:

  • Content Deployment Timer Job for ContentDeploymentJobDefinition: <GUID-from-ItemId-field-of-contentdeploymentjob-listitem> <random-guid>

    E.g.: “Content Deployment Timer Job for ContentDeploymentJobDefinition: be315bb7-e7be-4af4-a8bc-1cd369eb5220 e4d80472-1122-451b-b23d-e8efdce0fd82

    where be315bb7-e7be-4af4-a8bc-1cd369eb5220 is the ID (ItemId field) of the Content Deployment Job on the source system.

For Quick Deployment:

  • ContentDeploymentJobDefinition: Quick Deploy job for path <title-of-content-deployment-path>

    E.g.: “ContentDeploymentJobDefinition: Quick Deploy job for path ‘4444 – 5555’

Timerjob title on the Target system

On the target system you will find the following two content deployment timer jobs:

  • Content Deployment Timer Job for ContentDeploymentJobDefinition: Remote import job for job with sourceID = <GUID-from-ItemId-field-of-contentdeploymentjob-listitem-of-source-system>

    E.g.: “Content Deployment Timer Job for ContentDeploymentJobDefinition: Remote import job for job with sourceID = be315bb7-e7be-4af4-a8bc-1cd369eb5220

    where be315bb7-e7be-4af4-a8bc-1cd369eb5220 is the ID (ItemId field) of the Content Deployment Job on the source system.

    This job is never executed! It is automatically created when the content deployment job is created – similar to the scheduled job on the export side – but the import operation is always done using a dedicated one-time timer job which is described below.

  • Content Deployment Timer Job for ContentDeploymentJobDefinition: <GUID-from-ItemId-field-of-shadow-contentdeploymentjob-listitem> <random-guid>

    E.g: “Content Deployment Timer Job for ContentDeploymentJobDefinition: 14c0deb5-f0a2-45b4-a899-09843edfc181 67e2d452-5d54-403b-a2f0-d2732a950f6f

    where 14c0deb5-f0a2-45b4-a899-09843edfc181 is the ID (ItemId field) of the shadow Content Deployment Job on the target system.

    This is the job that performs the actual import!

Timerjob definition and status information

Central Admin

Existing Content deployment job definitions can be listed using the central admin website. On the Timer Job Definitions page ensure to select the View “Service” and choose the service “Central Administration”:

In the picture above you can see all the jobs I discussed earlier for an ongoing content deployment operation. You will see the export and import jobs as the deployment in my test was done between different web application on the same farm. You can also see that the “deploy 4444” timer job is disabled as I did not configure scheduling on my system. The same applies to the relevant Quick Deploy job.

In the Timer Job Status page you will see the following information:

Here you can see the executing timer jobs responsible for the export and import operation. Usually time stamp in the “Started” column would be different if the export operation takes longer than a couple of seconds as the import timer job will be created after the export operation is completed. See Part 4 for more details about this.

For executing content deployment timer jobs the Status column is always “Initialized” and Progress will always be “0%” as the content deployment timer jobs do not update the status till the operation is completed.

Custom Code

To list the existing timer jobs using custom code you can use the following code piece: 

// get a reference to the web application hosting the central admin site
SPAdministrationWebApplication adminWebApp =
         SPAdministrationWebApplication.Local;
// get a reference of the WSS service responsible for the central admin site
SPWebService adminWssService = (SPWebService)adminWebApp.Parent;

// enumerate all timerjobs associated with the service of the 
// central admin site which hosts all content deployment jobs
foreach (SPJobDefinition jobDef in adminWssService.JobDefinitions)
{
    // dump name and type in case that other jobs are also registered 
    // to the same service
    Console.WriteLine(“Name: “+jobDef.Name+” (“+jobDef.TypeName+“)”);
}

 

Content Deployment Jobs without Timerjob?

It is indeed possible to execute a content deployment job outside the context of a Timerjob and in a different process than OWSTIMER.EXE. At least the export side. The import side will always be executed in OWSTIMER.EXE as this part of the operation is controlled through the web service communication as discussed in Part 4 of this series.

A very simple sample to execute a Content Deployment Job inside a Console application can be implemented using the following code: 

static void Main(string[] args)
{
    ContentDeploymentJob job = ContentDeploymentJob.GetAllJobs()[“jobname”];
    job.Run();
}

If you execute the code above the export operation will be performed inside the console application and not in OWSTIMER.EXE. The interesting thing is that you can still monitor the process of the export and import operation from the Content Deployment Paths and Jobs page of the Central Admin.

You can also get additional information inside your custom code by implementing a couple event handlers: 

public static void OnExporting(object sender, ContentDeploymentEventArgs e)
{
    Console.WriteLine(“+++++ On Exporting fired for Job with Name: “ +
            e.Job.Name);
}

public static void OnImporting(object sender, ContentDeploymentEventArgs e)
{
    Console.WriteLine(“+++++ On Importing fired for Job with Name: “ +
            e.Job.Name);
}

public static void OnTransferring(object sender, ContentDeploymentEventArgs e)
{
    Console.WriteLine(“+++++ On Transferring fired for Job with Name: “ +
            e.Job.Name);
}

static void Main(string[] args)
{
    ContentDeploymentJob job = ContentDeploymentJob.GetAllJobs()[“jobname”];
    job.Exporting += new ContentDeploymentEventHandler(OnExporting);
    job.Transferring += new ContentDeploymentEventHandler(OnTransferring);
    job.Importing += new ContentDeploymentEventHandler(OnImporting);
    job.Run();
}

But it is also possible to execute the Content Deployment Job as Timerjob inside OWSTIMER in case you would like to control when exactly to start the deployment.

This can be achieved by passing the value “true” to the Run method of the Content Deployment Job: 

static void Main(string[] args)
{
    ContentDeploymentJob job = ContentDeploymentJob.GetAllJobs()[“jobname”];
    job.Run(true);
}

The value “true” indicates that the deployment should be performed asynchronously – which means it will be executed as Timerjob in OWSTIMER.EXE

There is one additional interesting flavor of the Run method which allows to incrementally deploy changes since a specific time in the past. E.g. the following code will deploy everything that has changed in the last 24 hours:

static void Main(string[] args)
{
    ContentDeploymentJob job = ContentDeploymentJob.GetAllJobs()[“jobname”];
    DateTime datetime = DateTime.Now.AddDays(-1);
    job.Run(truedateTime);
}

This method can be helpful in case that the target database had to be restored to a point in time before the last successful content deployment was done.

In such a situation the changes between the backup and the content deployment would be lost. Using the method above you would be able to deploy all changes since the last successful content deployment before the backup. You can retrieve the required DateTime value from the job history of the content deployment job. Ensure to use the start time value of the job rather than the end time to avoid missing changes that have happened while the deployment job was running.

Be aware that you can also deploy incrementally using a DateTime value outside of a TimerJob by changing the first parameter of the Run method to false:

job.Run(false, dateTime); 

 

How to deal with stuck Timerjobs

In some situations the content deployment timer job responsible to update the status can finish without updating the status in the content deployment job list item.

Two possible reasons:

  1. The timerjob on the exporting server exited during export unexpectedly.
  2. The timerjob on the exporting server exited due to a timeout waiting for the remote import job to update the status.

 

How to identify such an issue

An indication that we have run into such an issue is that the Deployment Time inside the job has not changed for a long time. If you compare the start time and the Deployment Time, the sum of both should be near the current time. A couple of minutes difference are ok, but you should not see a huge difference here.

To be sure, you need to check if there is a content deployment timer job running related to content deployment job listed in the timer job status page (see earlier in this chapter).

For example, here you will see a running scheduled timer job for the job listed above:

In the event that the job was a “Run Now” job, the name would not show up as friendly as above but would be replaced with the ItemId field of the content deployment job list item plus a random guid.

If you don’t find a running timer job related to your content deployment job listed here, then you have successfully identified the issue.

 

How to resolve such an issue

The recommended solution here is different depending on whether the job is stuck in the exporting or importing phase.

If it is stuck in exporting, then we can simply set the status to “Failure” to allow restarting the job again.

In case that it is stuck in the importing phase, we should change the status back to “Timed Out” to allow another check of the status on the target as the import might have succeeded and we would not want to redo the same deployment again.

As outlined in the earlier chapters the status of the job is logged in the list item associated with the content deployment job in the following list:

http://url-to-central-admin/lists/content%20deployment%20jobs

To change the status you need to edit the relevant list item.

Attention! Direct modification of the values in the list item should only be done if really required! Also ensure not to change any other fields than the one listed below. In case that incorrect values are written back it might be required to delete and recreate the affected content deployment job!

Here is a translation of the status codes:

Status

Int Value

Not Available

0

Success

1

Failure

2

Cancelled

3

Export in progress

4

Transfer in progress

5

Import in progress

6

Test Success

7

Test Failure

8

Test Exporting

9

Preparing

10

Cancel in progress

11

Import preparing

12

Import timed out

13

If the problem has occurred during export, you now need to change LastStatus to 2 which means “Failure”. This allows the job to be restarted in the UI.

If the problem has occurred during import, you should change LastStatus to 13 which means “Import timed out” to re-enable the “Check Status” option in the UI to see if the Import has failed or succeeded.

 

How to deal with orphan Timerjobs

The last step of a content deployment job is to delete it’s timerjob definition if it is a one-time timer job. That means all import timer jobs will be deleted automatically and also all export timer jobs which were created using “Run Now”.

In some situations it can happen that this cleanup operation is not performed. These jobs would have to be deleted manually. I have seen systems where a customer ended up with several thousand old one-time timer jobs visible on the Timer Job Status page. Manual cleanup would take hours in such a situation.

To automate such a cleanup you can use the code below:

using System;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Publishing.Administration;

namespace StefanG.SupportTools
{
    class DeleteOrphanCDJobs
    {
        static void Main(string[] args)
        {
            // Content Deployment Timer Jobs are hosted by the Administration Service
            foreach (SPRunningJob runningJob in SPWebService.AdministrationService.RunningJobs)
            {
                Console.WriteLine(runningJob.JobDefinitionTitle + ” – “ + runningJob.Status);

                // ensure that the timerjob is indeed a content deployment timer job
                // ContentDeploymentJobDefinition is an internal type so we cannot use “is” comparion
                if (runningJob.JobDefinition.GetType().ToString().EndsWith(“.ContentDeploymentJobDefinition”))
                {
                    // process only complated jobs (Succeeded, Failed and Aborted)
                    if ((runningJob.Status == SPRunningJobStatus.Succeeded) ||
                        (runningJob.Status == SPRunningJobStatus.Failed) ||
                        (runningJob.Status == SPRunningJobStatus.Aborted))
                    {
                        // we will only process one-time content deployment timer jobs and not scheduled jobs
                        if (runningJob.JobDefinition.Schedule is Microsoft.SharePoint.SPOneTimeSchedule)
                        {
                            // ok, we found a completed one-time content deployment timer job. Now lets delete it.
                            Console.WriteLine(“Deleting orphaned TimerJob: “ + runningJob.JobDefinition.Title+ ” – Status: “+runningJob.Status);
                            runningJob.JobDefinition.Delete();
                        }
                    }
                }
            }

        }
    }
}

 

15 Comments


  1. How to continue the process if destination server ImportLog_xxxxx_xxxx.log indicated as "Import Completed" but the source server content deployment job status as "timed out"?

    Reply

  2. Hi Wesley,

    hover over the job and choose the "Check Status" option.

    Cheers,

    Stefan

    Reply

  3. On this topic,

    E.g.: "Content Deployment Timer Job for ContentDeploymentJobDefinition: Remote import job for job with sourceID = be315bb7-e7be-4af4-a8bc-1cd369eb5220"

    where be315bb7-e7be-4af4-a8bc-1cd369eb5220 is the ID (ItemId field) of the Content Deployment Job on the source system.

    This job is never executed! It is automatically created when the content deployment job is created – similar to the scheduled job on the export side – but the import operation is always done using a dedicated one-time timer job which is described below.

    Content Deployment Timer Job for ContentDeploymentJobDefinition: <GUID-from-ItemId-field-of-shadow-contentdeploymentjob-listitem> <random-guid>

    E.g: "Content Deployment Timer Job for ContentDeploymentJobDefinition: 14c0deb5-f0a2-45b4-a899-09843edfc181 67e2d452-5d54-403b-a2f0-d2732a950f6f"

    where 14c0deb5-f0a2-45b4-a899-09843edfc181 is the ID (ItemId field) of the shadow Content Deployment Job on the target system.

    This is the job that performs the actual import!

    Should this job normally go away? Do they stay around if the deployment job fails?

    Reply

  4. Hi Marc,

    yes usually this job should go away – but sometimes it remains if the import terminates unexpectedly. You need to cleanup them manually in this case.

    Cheers,

    Stefan

    Reply

  5. Thank you for the quick response.

    In our MOSS 2007 environment, we notice that a great deal of errors are generated when the Sunday morning scheduled reboots occur on the SQL cluster. Is there any work-around to create an exception to those hourly jobs for the maintenance window? Perhaps to schedule the timer service to go down before the maintenance and back up afterwards would be a solution?

    Reply

  6. Hi Marc,

    unfortunatelly there is no simple solution for this. Best would be to schedule the reboot on SQL to a time when no content deployment job is scheduled.

    Cheers,

    Stefan

    Reply

  7. Hi,

    After a couple of years working with Content Deployment (in a public facing context) I came to a point where I wonder what are the real benefits of using it!

    Can you help me understand what would be the drawbacks of only using Publication Scheduling (only extending your web app to create an "authoring" site)?

    Reply

  8. Hi Fred,

    if scheduling fulfills your needs you should go for it!

    Content Deployment is not a replacement for content scheduling.

    It is intended for situations where you need a staging environment to consolidate content from different authors and do a consistent content approval or for situations where authoring and production environment have to be separated in two different farms.

    Another possible reason for using it is to deploy it to multiple farms all over the globe to make content available locally to subsidaries.

    Cheers,

    Stefan

    Reply

  9. Dear Stefan,

    what will happens if i delete an already created job and replace it with new job ,  does the new created job will continue do the incremental moving the modified content to the production or it will be full ?

    Thank you

    Reply

  10. Hi Mohammad,

    if the job has the same items selected and if the path has not been recreated then yes – it will do correct incrementals as the change token info is stored in the path.

    Cheers,

    Stefan

    Reply

  11. Dear Stefan,
    Not sure if you are still monitoring this thread, I’m trying my luck anyways
    I’m trying to perform CD from Source to Destination Farm on SP2010 Farm which is on build 14.0.7166.5000
    The CD was always failing at the Export Stage, thereby what I decided was that I try to see if just the Export can work on the source Farm, and did the Export of the Site Collection via Central Admin GUI
    The Size of the Site collection is close to 550GB, I dont know if there is upper limit on the size of the site collection
    Going back to export, the export runs for 12+hours and then fails with the below error:
    [1/17/2017 10:11:06 PM] [File] [AEO-19510_Rev_01.xml31.pdf] Progress: Exporting File /MATMT/AEO Workcard Temp PDF Library/AEO-19510_Rev_01.xml31.pdf.
    [1/17/2017 10:11:07 PM] FatalError: This constraint cannot be enabled as not all values have corresponding parent values.
    [1/17/2017 10:11:07 PM] Debug: at System.Data.ConstraintCollection.AddForeignKeyConstraint(ForeignKeyConstraint constraint)
    at System.Data.ConstraintCollection.Add(Constraint constraint, Boolean addUniqueWhenAddingForeign)
    at System.Data.DataRelationCollection.DataSetRelationCollection.AddCore(DataRelation relation)
    at System.Data.DataRelationCollection.Add(DataRelation relation)
    at System.Data.DataRelationCollection.Add(String name, DataColumn parentColumn, DataColumn childColumn)
    at Microsoft.SharePoint.Deployment.FileObjectHelper.GetNextBatch()
    at Microsoft.SharePoint.Deployment.ObjectHelper.RetrieveDataFromDatabase(ExportObject exportObject)
    at Microsoft.SharePoint.Deployment.FileObjectHelper.RetrieveData(ExportObject exportObject)
    at Microsoft.SharePoint.Deployment.ExportObjectManager.GetObjectData(ExportObject exportObject)
    at Microsoft.SharePoint.Deployment.ExportObjectManager.MoveNext()
    at Microsoft.SharePoint.Deployment.SPExport.SerializeObjects()
    at Microsoft.SharePoint.Deployment.SPExport.Run()
    [1/17/2017 10:11:56 PM] Progress: Export did not complete.
    I cannot figure out how to clear the Orphan Item, any help is highly appreciated!
    Thank you!

    Reply

    1. Hi Syed,
      this looks either like a problem in our code or like a database inconsistency in the database.
      I would suggest to ensure that the latest CU is installed and verify if the issue still occurs. If it still occurs you might want to verify (if available) if the issue also occurs on the latest CU on a SharePoint 2015 test farm (upgrade a DB backup using db attach). If the issue is not repro on SP2013 it is most likely a problem in our code and the only solution would be to upgrade to SP2013 as there are no further fixes for 2010 except for security issues.
      If the issue still occurs it is most likely a database inconsistency. then you might want to either use a backup of the DB from before the issue occurred or a delete the affected files / libraries and reupload the content afterwards.
      Cheers,
      Stefan

      Reply

      1. Hi Stefan,
        First of all I’m amazed at the speed with which you have responded, for the fact I know your current role in MS and how busy you must be.
        All I have for you Sir is Respect!!!
        Thank You for your guidance. Our Client have no plans to upgrade to SP2013, at least for now.
        Lastly the plan I had was, to take Content DB backup –> Run through a DB consistency check (DBCC CHECKDB) –> Detach & Re-attach the Content DB –> Try the Export again and see if this works.
        If I’m successful I shall surely let you know
        Thank you very much!
        -Syed

        Reply

        1. Hi Syed,
          DBCC CHECKDB only allows to check for physical database corruptions – not for logical database inconsistencies.
          I don’t think that this is caused by a database corruption – so this check will most likely not report anything.
          Cheers,
          Stefan

          Reply

          1. Dear Sir,
            WOW, you’re too efficient (so many things to learn from you)
            Thanks again, DBCC CHECKDB plan has been dropped
            I’m discussing with my Client on how they want to proceed
            God Bless you Stefan!
            -Syed

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.