How I Add Custom Sitecore Publishing Service Targets

At this point, I think I’ve installed, configured, or customized the new Sitecore Publishing Service at least a dozen times for various projects. Sometimes it’s on PaaS, sometimes on IaaS . . . I’ve used a variety of different versions depending on the compatibility matrix (see below as of Dec 16, 2018):

PubSvcVisual

I’m going to skip all the preamble about how the new Sitecore Publishing Service works, about .Net core being the new hotness, why this component can be a great addition to many distributed Sitecore implementations, etc — smart people have written a lot about this already. For example, check out Stephen Pope’s no-holds-barred look at the Publishing Service at http://www.stephenpope.co.uk/publishing or Jonathan Robbins has a nice overview piece at https://jonathanrobbins.co.uk/2016/09/02/setting-up-sitecore-publishing-service/.

I’ve learned a good bit from all the iterations of working with the component and I think consistently the most error-prone part of the setup is aligning any additional custom Sitecore publishing targets one is using in an implementation. This write-up from Geykel Moreno at AlphaSolutions has all the good information, but it’s not as easy to follow because it doesn’t post a comprehensive sc.publishing.xml file — it took a bit of trial and error for me, so to simplify for posterity I’m going to share a reference sample Gist at https://gist.github.com/grant-killian/d2fe8d3e89c5d7b15f47464dd1809d62 that includes 2 additional custom publishing targets. I’ve inserted XML comments for the 3 locations one must update in the config\sitecore\publishing\sc.publishing.xml file:

  1. You need to add your ConnectionString entry for each database to the Publishing/ConnectionStrings XML
  2. You need to add your Services/DefaultConnectionFactory/Options/Connections XML definition for each custom target
  3. You need to add entries for each target to the StoreFactory/Options/Stores/Targets XML that will include the GUID of the Sitecore item that defines each publishing target, along with the Name of the item and additional details

Here’s the gist with the full XML for reference:

<?xml version="1.0" encoding="UTF-8"?>
<Settings>
<Sitecore>
<Publishing>
<InstanceName>${SITECORE_InstanceName}</InstanceName>
<ConnectionStrings>
<Service>${Sitecore:Publishing:ConnectionStrings:Master}</Service>
<!– Add any additional publishing targets you may use (first location for changes to this file) –>
<previewweb>Data Source=Server-012345;Initial Catalog=PrevWeb;Integrated Security=True;MultipleActiveResultSets=True;ConnectRetryCount=15;ConnectRetryInterval=1</previewweb>
<liveweb>Data Source=Server-012345;Initial Catalog=LiveWeb;Integrated Security=True;MultipleActiveResultSets=True;ConnectRetryCount=15;ConnectRetryInterval=1</liveweb>
<!– end first location for changes –>
</ConnectionStrings>
<Services>
<DefaultConnectionFactory>
<Options>
<Connections>
<Links>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.SqlDatabaseConnection, Sitecore.Framework.Publishing.Data</Type>
<LifeTime>Transient</LifeTime>
<Options>
<ConnectionString>${Sitecore:Publishing:ConnectionStrings:Core}</ConnectionString>
<DefaultCommandTimeout>120</DefaultCommandTimeout>
<Behaviours>
<backend>sql-backend-default</backend>
<api>sql-api-default</api>
</Behaviours>
</Options>
</Links>
<Service>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.SqlDatabaseConnection, Sitecore.Framework.Publishing.Data</Type>
<LifeTime>Transient</LifeTime>
<Options>
<ConnectionString>${Sitecore:Publishing:ConnectionStrings:Service}</ConnectionString>
<DefaultCommandTimeout>120</DefaultCommandTimeout>
<Behaviours>
<backend>sql-backend-default</backend>
<api>sql-api-default</api>
</Behaviours>
</Options>
</Service>
<Master>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.SqlDatabaseConnection, Sitecore.Framework.Publishing.Data</Type>
<LifeTime>Transient</LifeTime>
<Options>
<ConnectionString>${Sitecore:Publishing:ConnectionStrings:Master}</ConnectionString>
<DefaultCommandTimeout>120</DefaultCommandTimeout>
<Behaviours>
<backend>sql-backend-default</backend>
<api>sql-api-default</api>
</Behaviours>
</Options>
</Master>
<Internet>
<!– Should match the name of the publishing target configured in SC. –>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.SqlDatabaseConnection, Sitecore.Framework.Publishing.Data</Type>
<LifeTime>Transient</LifeTime>
<Options>
<ConnectionString>${Sitecore:Publishing:ConnectionStrings:Web}</ConnectionString>
<DefaultCommandTimeout>120</DefaultCommandTimeout>
<Behaviours>
<backend>sql-backend-default</backend>
<api>sql-api-default</api>
</Behaviours>
</Options>
</Internet>
<!– start custom publishing target additions (2nd location) –>
<Preview>
<!– Should match the name of the publishing target configured in Sitecore –>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.SqlDatabaseConnection, Sitecore.Framework.Publishing.Data</Type>
<LifeTime>Transient</LifeTime>
<Options>
<ConnectionString>${Sitecore:Publishing:ConnectionStrings:previewweb}</ConnectionString>
<DefaultCommandTimeout>120</DefaultCommandTimeout>
<Behaviours>
<backend>sql-backend-default</backend>
<api>sql-api-default</api>
</Behaviours>
</Options>
</Preview>
<Live>
<!– Should match the name of the publishing target configured in Sitecore –>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.SqlDatabaseConnection, Sitecore.Framework.Publishing.Data</Type>
<LifeTime>Transient</LifeTime>
<Options>
<ConnectionString>${Sitecore:Publishing:ConnectionStrings:liveweb}</ConnectionString>
<DefaultCommandTimeout>120</DefaultCommandTimeout>
<Behaviours>
<backend>sql-backend-default</backend>
<api>sql-api-default</api>
</Behaviours>
</Options>
</Live>
<!– end custom publishing target additions (2nd location) –>
</Connections>
</Options>
</DefaultConnectionFactory>
<DbConnectionBehaviours>
<Options>
<Entries>
<sql-backend-default>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.NoRetryConnectionBehaviour, Sitecore.Framework.Publishing.Data</Type>
<Options>
<Name>Default Backend No Retry behaviour</Name>
<CommandTimeout>120</CommandTimeout>
</Options>
</sql-backend-default>
<sql-api-default>
<Type>Sitecore.Framework.Publishing.Data.AdoNet.NoRetryConnectionBehaviour, Sitecore.Framework.Publishing.Data</Type>
<Options>
<Name>Default Api No Retry behaviour</Name>
<CommandTimeout>10</CommandTimeout>
</Options>
</sql-api-default>
</Entries>
</Options>
</DbConnectionBehaviours>
<StoreFactory>
<Options>
<Stores>
<Service>
<Type>Sitecore.Framework.Publishing.Data.ServiceStore, Sitecore.Framework.Publishing.Data</Type>
<ConnectionName>Service</ConnectionName>
<FeaturesListName>ServiceStoreFeatures</FeaturesListName>
</Service>
<Sources>
<Master>
<Type>Sitecore.Framework.Publishing.Data.SourceStore, Sitecore.Framework.Publishing.Data</Type>
<ConnectionNames>
<master>Master</master>
</ConnectionNames>
<FeaturesListName>SourceStoreFeatures</FeaturesListName>
<!– The name of the Database entity in Sitecore. –>
<ScDatabase>master</ScDatabase>
</Master>
</Sources>
<Targets>
<!–Additional targets can be configured here–>
<Internet>
<Type>Sitecore.Framework.Publishing.Data.TargetStore, Sitecore.Framework.Publishing.Data</Type>
<ConnectionName>Internet</ConnectionName>
<FeaturesListName>TargetStoreFeatures</FeaturesListName>
<!– The id of the target item definition in Sitecore. –>
<Id>8E080626-DDC3-4EF4-A1D1-F0BE4A200254</Id>
<!– The name of the Database entity in Sitecore. –>
<ScDatabase>web</ScDatabase>
</Internet>
<!– start custom publishing target additions (third location) –>
<!– this XML node should be named the same as the item in Sitecore (not the "Display Name", but the Item name) –>
<Preview>
<Type>Sitecore.Framework.Publishing.Data.TargetStore, Sitecore.Framework.Publishing.Data</Type>
<ConnectionName>Preview</ConnectionName>
<FeaturesListName>TargetStoreFeatures</FeaturesListName>
<!– make sure the GUID below matches the GUID stored in Sitecore for the Publishing Target –>
<Id>8D1249E6-9413-4C2D-8C72-06561CE1D026</Id>
<ScDatabase>preveiwweb</ScDatabase>
</Preview>
<!– this XML node should be named the same as the item in Sitecore (not the "Display Name", but the Item name) –>
<Live>
<Type>Sitecore.Framework.Publishing.Data.TargetStore, Sitecore.Framework.Publishing.Data</Type>
<ConnectionName>Live</ConnectionName>
<FeaturesListName>TargetStoreFeatures</FeaturesListName>
<!– make sure the GUID below matches the GUID stored in Sitecore for the Publishing Target –>
<Id>0EA57D57-7837-4B51-A72C-E8B3F1322C07</Id>
<ScDatabase>liveweb</ScDatabase>
</Live>
<!– end custom publishing target additions (third location) –>
</Targets>
<ItemsRelationship>
<Type>Sitecore.Framework.Publishing.Data.ItemsRelationshipStore, Sitecore.Framework.Publishing.Data</Type>
<ConnectionName>Links</ConnectionName>
<FeaturesListName>ItemsRelationshipStoreFeatures</FeaturesListName>
</ItemsRelationship>
</Stores>
</Options>
</StoreFactory>
<StoreFeaturesLists>
<Options>
<FeatureLists>
<!–Source Store Features–>
<SourceStoreFeatures>
<ItemReadRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.CompositeItemReadRepository, Sitecore.Framework.Publishing.Data</Type>
</ItemReadRepositoryFeature>
<TestableContentRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.CompositeTestableContentRepository, Sitecore.Framework.Publishing.Data</Type>
</TestableContentRepositoryFeature>
<WorkflowStateRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.CompositeWorkflowStateRepository, Sitecore.Framework.Publishing.Data</Type>
</WorkflowStateRepositoryFeature>
<EventQueueRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.CompositeEventQueueRepository, Sitecore.Framework.Publishing.Data</Type>
<options>
<ConnectionName>master</ConnectionName>
</options>
</EventQueueRepositoryFeature>
<SourceIndexFeature>
<Type>Sitecore.Framework.Publishing.ItemIndex.SourceIndexWrapper, Sitecore.Framework.Publishing</Type>
</SourceIndexFeature>
</SourceStoreFeatures>
<!–Service Store Features–>
<ServiceStoreFeatures>
<ManifestRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Manifest.ManifestRepository, Sitecore.Framework.Publishing</Type>
</ManifestRepositoryFeature>
<PublisherOperationRepositoryFeature>
<Type>Sitecore.Framework.Publishing.PublisherOperations.PublisherOperationRepository, Sitecore.Framework.Publishing</Type>
</PublisherOperationRepositoryFeature>
<PublishJobQueueRepositoryFeature>
<Type>Sitecore.Framework.Publishing.PublishJobQueue.PublishJobQueueRepository, Sitecore.Framework.Publishing</Type>
</PublishJobQueueRepositoryFeature>
<TargetSyncStateRepositoryFeature>
<Type>Sitecore.Framework.Publishing.TargetSyncState.TargetSyncStateRepository, Sitecore.Framework.Publishing</Type>
</TargetSyncStateRepositoryFeature>
<ActivationLockRepositoryFeature>
<Type>Sitecore.Framework.Publishing.InstanceActivation.ActivationLockRepository, Sitecore.Framework.Publishing</Type>
</ActivationLockRepositoryFeature>
</ServiceStoreFeatures>
<!–Target Store Features–>
<TargetStoreFeatures>
<IndexableItemRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.Classic.ClassicIndexableItemRepository, Sitecore.Framework.Publishing.Data.Classic</Type>
</IndexableItemRepositoryFeature>
<ItemWriteRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.Classic.ClassicItemRepository, Sitecore.Framework.Publishing.Data.Classic</Type>
</ItemWriteRepositoryFeature>
<MediaRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.Classic.Repositories.ClassicMediaRepository, Sitecore.Framework.Publishing.Data.Classic</Type>
</MediaRepositoryFeature>
<TargetIndexFeature>
<Type>Sitecore.Framework.Publishing.ItemIndex.TargetIndexWrapper, Sitecore.Framework.Publishing</Type>
</TargetIndexFeature>
</TargetStoreFeatures>
<!–ItemsRelationship Store Features–>
<ItemsRelationshipStoreFeatures>
<DatabaseItemRelationshipRepositoryFeature>
<Type>Sitecore.Framework.Publishing.Data.Classic.ClassicItemRelationshipRepository, Sitecore.Framework.Publishing.Data.Classic</Type>
</DatabaseItemRelationshipRepositoryFeature>
</ItemsRelationshipStoreFeatures>
</FeatureLists>
</Options>
</StoreFeaturesLists>
</Services>
</Publishing>
</Sitecore>
</Settings>

https://gist.github.com/grant-killian/d2fe8d3e89c5d7b15f47464dd1809d62.js

Sitecore artifact table patch config

I’ve patched EventQueues several times through the years, so I saved this Gist to make it easier for future opportunities.  There’s not much new to share in terms of introducing why one does this, refer to this blog about Sitecore artifact tables (or the old reliable Sitecore CMS Tuning Guide). You also have to take care around the order of configuration file processing, so zzzzzArtifactTableRetention.config this sucker if you really must 🙂

Here’s the Gist – https://gist.github.com/grant-killian/ffa1e84770b10a90e2454e241986b911  and here’s the expanded XML:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<sitecore>
<scheduling>
<agent type="Sitecore.Tasks.CleanupEventQueue, Sitecore.Kernel">
<patch:delete />
</agent>
<agent type="Sitecore.Tasks.CleanupEventQueue, Sitecore.Kernel" method="Run" interval="01:00:00">
<IntervalToKeep>06:00:00</IntervalToKeep>
</agent>
<agent type="Sitecore.Tasks.CleanupPublishQueue, Sitecore.Kernel">
<patch:delete />
</agent>
<agent type="Sitecore.Tasks.CleanupPublishQueue, Sitecore.Kernel" method="Run" interval="04:00:00">
<DaysToKeep>7</DaysToKeep>
</agent>
</scheduling>
<databases>
<database id="master">
<Engines.HistoryEngine.Storage>
<patch:delete />
</Engines.HistoryEngine.Storage>
<Engines.HistoryEngine.Storage>
<obj type="Sitecore.Data.SqlServer.SqlServerHistoryStorage, Sitecore.Kernel">
<param connectionStringName="$(id)" />
<EntryLifeTime>7.00:00:00</EntryLifeTime>
</obj>
</Engines.HistoryEngine.Storage>
</database>
<database id="web">
<Engines.HistoryEngine.Storage>
<patch:delete />
</Engines.HistoryEngine.Storage>
<Engines.HistoryEngine.Storage>
<obj type="Sitecore.Data.SqlServer.SqlServerHistoryStorage, Sitecore.Kernel">
<param connectionStringName="$(id)" />
<EntryLifeTime>7.00:00:00</EntryLifeTime>
</obj>
</Engines.HistoryEngine.Storage>
</database>
</databases>
</sitecore>
</configuration>