Example #1
0
        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;
            }
        }