• Published
  • 5 min

Automatic synchronization of content between Episerver environments

With a continuous delivery process with frequent releases, it’s important to be able to work with up to date content so that your different environments are as like each other as they can be. At Epinova we’ve seen how this – when not being automated – can lead to a lot of wasted time as well as bugs not being caught early in the process (when they have low effect and cost).

Perhaps you recognize some of the following scenarios:

  • You have just gone live with a new release when you find a bug that you didn’t see while testing due to differences of content in your live and test environments.
  • You have been asked to do a minor change on a site – but find yourself spending much more time to be able to verify how it works due to a very slow manual process to get and test with the latest content.
  • You have a process for synchronizing the latest production content to your other environments – but you do this very rarely due to the amount of manual work required to do this.
  • You spend time investigating something that previously worked on your test environment just to find out (after several hours) that something was changed when doing a content synchronization, the last time.
  • You’re not allowed, or you’re hindered to move production data to other environments due to the fact that they contain sensitive data.

I’ve seen all the above and often feel the pain points that comes with not having up to date content to be able to develop and test against. To remedy this, we’ve spent some time creating automation tools to help with this that are built in a general way and that can then be reused for many customers. In this blog post I’ll describe how we tackle this on a conceptual level, as well as digging into code samples for some of this.

Episerver DXP vs on-prem/bespoke cloud hosting

The concept that I’ll describe are general and can be applied to both an Episerver DXP hosting with a fixed set of named environments as well on-prem or bespoke cloud hosting (usually using MS Azure). However, Episerver DXP comes with a very nice set of web APIs that makes it very simple to move content from one environment to another. Epinova has also created an extension to Azure DevOps that makes it possible to easily integrate these tools in your automated pipelines when using Azure DevOps (though it is of course possible to call these from any CI/CD Tool).

How content flows through the system

If we look at Episervers description of the DXP service, we can see that code/functionality flows in one direction, while content goes the other way. In practice this means that production content is created in the production environment. As with code updates, content is not moved automatically (downstream) but requires someone to initiate the content copy downstream. If you are using Episerver DXP you would require content to be copied from for instance production to preproduction. This then triggers a backup/restore of the database and the BLOB container (files) for the solution.

EpiserverDxpSync.jpg

Once this is done, this means that the database and BLOB files are basically copies of each other between the source and target environments.

If your using Azure DevOps and want to know more how to set up content synchronization between environments, my college Ove Lartelius has written a good blog post:

https://www.epinova.no/folg-med/blogg/2020/epinova-dxp-deployment-part-1-introduction/

Caveats when copying the database

Though copying the entire database between environments is a rather smooth and quick operation (at least for most sites) it might include challenges since you might have content that should differ between environments. To name a few examples:

  • You want to have different site configurations per environment.
  • You want to enable/disable scheduled jobs between environments.
  • You want to prevent sensitive data from being available in other environments except for production (or in database backups from these environments).
  • You have settings/configuration that you want to be editable in the production environment but where you want different environments to have different settings.

On top of this, there might also be one-time jobs that you want to trigger the first time the site starts up with a copied database. Making sure that you reindex a search solution like Episerver Find is a good example of such a case.

Introducing Addon.Episerver.EnvironmentSynchronizer

To handle the above we’ve created a new open source module named Addon.Episerver.EnvironmentSynchronizer (https://github.com/Epinova/Addon.Episerver.EnvironmentSynchronizer). Right now, the module is not available on any public feed, but we hope to add it to the Episerver feed soon. This module adds a lightweight infrastructure to be able to trigger something that should happen when the database has been moved. This is done by implementing one ore many IEnvironmentSynchronizer – that each should be responsible for a single piece of post synchronization work. Let’s look on a simple handler that’s responsible for triggering the Episerver Find reindex job:

using Addon.Episerver.EnvironmentSynchronizer;
using EPiServer.DataAbstraction;
using EPiServer.ServiceLocation;
using System;

namespace Somecustomer.Site.Infrastructure.Environments
{
    /// <summary>
    /// This environment synchronizer makes sure that the Episerver Find re-index job is run whenever an environment sync is done.
    /// </summary>
    [ServiceConfiguration(typeof(IEnvironmentSynchronizer))]
    public class EpiserverFindReindexTriggerSynchronizer : IEnvironmentSynchronizer
    {
        private static readonly Guid _findReindexScheduledJobGuid = new Guid("EC45E62B-9B9A-4D27-8731-35EC17F4A219");
        private readonly IScheduledJobRepository _scheduledJobRepository;

        public EpiserverFindReindexTriggerSynchronizer(IScheduledJobRepository scheduledJobRepository)
        {
            _scheduledJobRepository = scheduledJobRepository;
        }

        public string Synchronize(string environmentName)
        {
            var job = _scheduledJobRepository.Get(_findReindexScheduledJobGuid);
            job.NextExecution = DateTime.Now.AddSeconds(10);
            scheduledJobRepository.Save(job);

            return "Scheduled Episerver Find re-indexing";
        }
    }
}

In this example we will use the name of the current environment to take different decisions (in this example only changing the URL of the link we’re updating) of what to do.

How does the system keep track of what environment you are on?

Since the package is built to handle any environment, you need to implement the IEnvironmentNameSource interface to be able to inform the system what you call the current environment (there’s a default built in handler that works for Episerver DXP). A simple solution could be to just fetch a value that you have in your configuration:

using Addon.Episerver.EnvironmentSynchronizer;
using EPiServer.ServiceLocation;
using System.Configuration;

namespace Episervercom.Site.Infrastructure.Environments
{
    [ServiceConfiguration(typeof(IEnvironmentNameSource))]
    public class SiteEnvironmentNameSource : IEnvironmentNameSource
    {
        public string GetCurrentEnvironmentName()
        {
            return ConfigurationManager.AppSettings["EnvironmentName"];
        }
    }
}

Next step for you?

We have some of Sweden's most skilled developers working at Epinova. We work with large solutions, integrations, multilingual sites and have a learing culture where we help out each other. Check out what our developers think and if we can be a next step for you! 😃

Check out our Job Opportunities
Two developers up for a fika