private async Task UpgradeSchemaAsync(int version, string script, CancellationToken cancellationToken) { // check if the record for given version exists in failed status await _schemaManagerDataStore.DeleteSchemaVersionAsync(version, SchemaVersionStatus.Failed.ToString(), cancellationToken); await _schemaManagerDataStore.ExecuteScriptAndCompleteSchemaVersionAsync(script, version, cancellationToken); _logger.LogInformation(string.Format(Resources.SchemaMigrationSuccessMessage, version)); }
/// <inheritdoc /> public virtual async Task ApplySchema(string connectionString, Uri server, MutuallyExclusiveType type, CancellationToken token = default) { EnsureArg.IsNotNullOrEmpty(connectionString, nameof(connectionString)); EnsureArg.IsNotNull(server, nameof(server)); EnsureArg.IsNotNull(type, nameof(type)); _schemaClient.SetUri(server); try { _sqlServerDataStoreConfiguration.ConnectionString = connectionString; // Base schema is required to run the schema migration tool. // This method also initializes the database if not initialized yet. await _baseSchemaRunner.EnsureBaseSchemaExistsAsync(token).ConfigureAwait(false); // If InstanceSchema table is just created(as part of baseSchema), it takes a while to insert a version record // since the Schema job polls and upserts at the specified interval in the service. await _baseSchemaRunner.EnsureInstanceSchemaRecordExistsAsync(token).ConfigureAwait(false); var availableVersions = (await GetAvailableSchema(server, token).ConfigureAwait(false)).ToList(); // If the user hits apply command multiple times in a row, then the service schema job might not poll the updated available versions // so there are retries to give it a fair amount of time. int attemptCount = 1; availableVersions = await Policy.Handle <SchemaManagerException>() .WaitAndRetryAsync( retryCount: RetryAttempts, sleepDurationProvider: (retryCount) => _retrySleepDuration, onRetry: (exception, retryCount) => { _logger.LogError(exception, string.Format(CultureInfo.InvariantCulture, Resources.RetryCurrentSchemaVersion, attemptCount++, RetryAttempts)); }) .ExecuteAsync(token => FetchUpdatedAvailableVersionsAsync(token), token) .ConfigureAwait(false); if (availableVersions.Count == 1) { _logger.LogError(Resources.AvailableVersionsDefaultErrorMessage); return; } // Removes the current version since the first available version is always the current version which is already applied. availableVersions.RemoveAt(0); var targetVersion = type.Next == true?availableVersions.First().Id: type.Latest == true?availableVersions.Last().Id: type.Version; availableVersions = availableVersions.Where(availableVersion => availableVersion.Id <= targetVersion) .ToList(); // Checking the specified version is not out of range of available versions if (availableVersions.Count < 1 || targetVersion < availableVersions.First().Id || targetVersion > availableVersions.Last().Id) { throw new SchemaManagerException(string.Format(CultureInfo.InvariantCulture, Resources.SpecifiedVersionNotAvailable, targetVersion)); } await ValidateVersionCompatibility(availableVersions.Last().Id, token).ConfigureAwait(false); if (availableVersions.First().Id == 1) { // Upgrade schema directly to the latest schema version _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, Resources.SchemaMigrationStartedMessage, availableVersions.Last().Id)); string script = await GetScriptAsync(1, availableVersions.Last().ScriptUri, token).ConfigureAwait(false); await InitializeSchemaAsync(availableVersions.Last().Id, script, token).ConfigureAwait(false); return; } foreach (AvailableVersion availableVersion in availableVersions) { int executingVersion = availableVersion.Id; _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, Resources.SchemaMigrationStartedMessage, executingVersion)); attemptCount = 1; await Policy.Handle <SchemaManagerException>() .WaitAndRetryAsync( retryCount: RetryAttempts, sleepDurationProvider: (retryCount) => _retrySleepDuration, onRetry: (exception, retryCount) => { _logger.LogError(exception, string.Format(CultureInfo.InvariantCulture, Resources.RetryCurrentVersions, attemptCount++, RetryAttempts)); }) .ExecuteAsync(token => ValidateInstancesVersionAsync(executingVersion, token), token) .ConfigureAwait(false); string script = await GetScriptAsync(executingVersion, availableVersion.ScriptUri, token, availableVersion.DiffUri).ConfigureAwait(false); // check if the record for given version exists in failed status await _schemaManagerDataStore.DeleteSchemaVersionAsync(executingVersion, SchemaVersionStatus.Failed.ToString(), token).ConfigureAwait(false); await _schemaManagerDataStore.ExecuteScriptAndCompleteSchemaVersionAsync(script, executingVersion, token).ConfigureAwait(false); _logger.LogInformation(string.Format(CultureInfo.InvariantCulture, Resources.SchemaMigrationSuccessMessage, executingVersion)); } } catch (Exception ex) when(ex is SchemaManagerException || ex is InvalidOperationException) { _logger.LogError(ex, ex.Message); throw; } catch (HttpRequestException ex) { _logger.LogError(ex, string.Format(CultureInfo.InvariantCulture, Resources.RequestFailedMessage, server)); throw; } catch (Exception ex) { if (ex is SqlException || ex is ExecutionFailureException) { _logger.LogError(ex, string.Format(CultureInfo.InvariantCulture, Resources.QueryExecutionErrorMessage, ex.Message)); return; } throw; } }