private async Task RunUpdates(ICollection <UpdatePackage> packages, CancellationToken?cancellationToken) { // Lock the process to prevent concurrent updates var distributedLock = await _autoUpdateDistributedLockManager.LockAsync(); try { foreach (var package in packages) { if (IsCancelled(cancellationToken)) { break; } await ExecutePackage(package, cancellationToken); } } catch (Exception updateProcessException) { // Try and close the lock before we throw the exception try { await _autoUpdateDistributedLockManager.UnlockAsync(distributedLock); } catch (Exception unlockException) { _logger.LogError(unlockException, unlockException.Message); } throw; } await _autoUpdateDistributedLockManager.UnlockAsync(distributedLock); }
/// <summary> /// Updates an application and referenced modules by scanning for implementations /// of IUpdatePackageFactory and executing any packages found. /// </summary> /// <remarks> /// Async because sometimes UpdateCommands need to be async to save having to create /// sync versions of methods that would not normally require them. E.g. when calling into /// shared command handlers. I don't really think there's much benefit in making any other /// part async because nothing else useful should be happening while the db update is going on anyway. /// </remarks> public async Task UpdateAsync() { var previouslyAppliedVersions = await GetUpdateVersionHistoryAsync(); var filteredPackages = _updatePackageFactories .SelectMany(f => f.Create(previouslyAppliedVersions)) .ToList(); var packages = _updatePackageOrderer.Order(filteredPackages); if (!packages.Any()) { return; } if (await IsLockedAsync()) { throw new DatabaseLockedException(); } // Lock the process to prevent concurrent updates var distributedLock = await _autoUpdateDistributedLockManager.LockAsync(); try { foreach (var package in packages) { await ExecutePackage(package); } } finally { await _autoUpdateDistributedLockManager.UnlockAsync(distributedLock); } }
/// <summary> /// Updates an application and referenced modules by scanning for implementations /// of IUpdatePackageFactory and executing any packages found. /// </summary> public async Task UpdateAsync(CancellationToken?cancellationToken = null) { var previouslyAppliedVersions = await GetUpdateVersionHistoryAsync(); var filteredPackages = _updatePackageFactories .SelectMany(f => f.Create(previouslyAppliedVersions)) .ToList(); var packages = _updatePackageOrderer.Order(filteredPackages); if (!packages.Any()) { return; } var isLocked = await IsLockedAsync(); if (isLocked && !packages.Any(p => p.ContainsVersionUpdates())) { // if locked ignore always-update commands and only throw if there // are required version updates. return; } else if (isLocked) { throw new DatabaseLockedException(); } if (IsCancelled(cancellationToken)) { return; } // Lock the process to prevent concurrent updates var distributedLock = await _autoUpdateDistributedLockManager.LockAsync(); try { foreach (var package in packages) { if (IsCancelled(cancellationToken)) { break; } await ExecutePackage(package, cancellationToken); } } finally { await _autoUpdateDistributedLockManager.UnlockAsync(distributedLock); } }