diff --git a/uSync.BackOffice/Boot/FirstBootMigration.cs b/uSync.BackOffice/Boot/FirstBootMigration.cs index ea582dbf3..a92097329 100644 --- a/uSync.BackOffice/Boot/FirstBootMigration.cs +++ b/uSync.BackOffice/Boot/FirstBootMigration.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Threading.Tasks; +using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Migrations; @@ -30,9 +31,10 @@ public FirstBootMigrationPlan() /// /// First boot Feature migration /// -public class FirstBootMigration : AsyncMigrationBase +public class FirstBootMigration : UnscopedAsyncMigrationBase { private readonly IUmbracoContextFactory _umbracoContextFactory; + private readonly IServerRoleAccessor _serverRoleAccessor; private readonly ISyncConfigService _uSyncConfig; private readonly ISyncService _uSyncService; private readonly ILogger _logger; @@ -43,19 +45,19 @@ public FirstBootMigration( IUmbracoContextFactory umbracoContextFactory, ISyncConfigService uSyncConfig, ISyncService uSyncService, - ILogger logger) : base(context) + ILogger logger, + IServerRoleAccessor serverRoleAccessor) : base(context) { _umbracoContextFactory = umbracoContextFactory; _uSyncConfig = uSyncConfig; _uSyncService = uSyncService; _logger = logger; + _serverRoleAccessor = serverRoleAccessor; } /// protected override async Task MigrateAsync() { - // TODO: doesn't work in the betas. might need a new migration to add it. - // return; // first boot migration. try @@ -63,6 +65,12 @@ protected override async Task MigrateAsync() if (!_uSyncConfig.Settings.ImportOnFirstBoot) return; + if (_serverRoleAccessor.CurrentServerRole == ServerRole.Subscriber) + { + _logger.LogInformation("This is a Subscriber server in a load balanced setup - uSync only runs on single or schedulingPublisher (main) servers"); + return; + } + var sw = Stopwatch.StartNew(); var changes = 0; @@ -72,8 +80,9 @@ protected override async Task MigrateAsync() // if config service is set to import on first boot then this // will let uSync do a first boot import - // not sure about context on migrations so will need to test - // or maybe we fire something into a notification (or use a static) + // this runs as a 'un-scoped' migration, so we need to manage the context here. + // as we are in the context and not just a scope all the publish stuff works + // first time, just like it does in ImportOnStartup using (var reference = _umbracoContextFactory.EnsureUmbracoContext()) { @@ -92,7 +101,12 @@ protected override async Task MigrateAsync() catch (Exception ex) { _logger.LogError(ex, "uSync First boot failed {message}", ex.Message); - throw; } + finally + { + // we always complete the context - even if we fail. + // we don't want to keep trying this migration every time. + Context.Complete(); + } } } diff --git a/uSync.BackOffice/Notifications/uSyncApplicationStartingHandler.cs b/uSync.BackOffice/Notifications/uSyncApplicationStartingHandler.cs index 71f1e56a8..cda58590d 100644 --- a/uSync.BackOffice/Notifications/uSyncApplicationStartingHandler.cs +++ b/uSync.BackOffice/Notifications/uSyncApplicationStartingHandler.cs @@ -120,8 +120,8 @@ private async Task InituSyncAsync() }; _logger.LogInformation("uSync: Running export at startup"); - - + + _uSyncService.StartupExportAsync(_uSyncConfig.GetWorkingFolder(), options).Wait(); } @@ -131,10 +131,10 @@ private async Task InituSyncAsync() if (!HasStopFile(_uSyncConfig.GetWorkingFolder())) { - _uSyncService.StartupImportAsync(_uSyncConfig.GetFolders(), false, new SyncHandlerOptions + await _uSyncService.StartupImportAsync(_uSyncConfig.GetFolders(), false, new SyncHandlerOptions { Group = _uSyncConfig.Settings.ImportAtStartup - }).Wait(); + }); await ProcessOnceFileAsync(_uSyncConfig.GetWorkingFolder()); } diff --git a/uSync.BackOffice/Services/SyncService.cs b/uSync.BackOffice/Services/SyncService.cs index 078b35ee5..0efa3a9d6 100644 --- a/uSync.BackOffice/Services/SyncService.cs +++ b/uSync.BackOffice/Services/SyncService.cs @@ -22,6 +22,7 @@ using uSync.BackOffice.SyncHandlers; using uSync.BackOffice.SyncHandlers.Models; using uSync.Core; +using uSync.Core.Extensions; using uSync.Core.Serialization; namespace uSync.BackOffice; @@ -113,9 +114,28 @@ public bool HasRootFiles(string[] folders) #region Importing static readonly SemaphoreSlim _importSemaphoreLock = new SemaphoreSlim(1, 1); + /// + /// hash of the options last used in a startup import + /// + /// + /// as both first boot and import at startup use this method, + /// and both can be triggered at startup, we use a hash of the + /// options and folders to make sure we are not running them + /// both if they are asking the same thing. + /// + static int? _lastStartupRun; + /// > public async Task> StartupImportAsync(string[] folders, bool force, SyncHandlerOptions handlerOptions, uSyncCallbacks? callbacks = null) { + var runHash = $"{string.Join(",", folders)}{force}{handlerOptions.SerializeJsonString(false)}".GetDeterministicHashCode(); + + if (_lastStartupRun.HasValue && _lastStartupRun.Value == runHash) + { + _logger.LogInformation("uSync: Skipping [duplicate] startup import has already ran with these parameters"); + return []; + } + handlerOptions ??= new SyncHandlerOptions(); handlerOptions.Action = HandlerActions.Import; var handlers = _handlerFactory.GetValidHandlers(handlerOptions); @@ -127,6 +147,8 @@ public async Task> StartupImportAsync(string[] folders, if (changes.Any(x => x.Change > ChangeType.NoChange && x.ItemType == "IContent")) _distributedCache.RefreshAllPublishedSnapshot(); + _lastStartupRun = runHash; + return changes; }