Exemplo n.º 1
0
        public async Task <TaskInfo> KeepAliveAsync(string taskId, string runId, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    try
                    {
                        VLatest.TaskKeepAlive.PopulateCommand(sqlCommandWrapper, taskId, runId);
                        SqlDataReader sqlDataReader = await sqlCommandWrapper.ExecuteReaderAsync(cancellationToken);

                        if (!sqlDataReader.Read())
                        {
                            return(null);
                        }

                        var taskInfoTable = VLatest.TaskInfo;
                        _ = sqlDataReader.Read(taskInfoTable.TaskId, 0);
                        string   queueId           = sqlDataReader.Read(taskInfoTable.QueueId, 1);
                        short    status            = sqlDataReader.Read(taskInfoTable.Status, 2);
                        short    taskTypeId        = sqlDataReader.Read(taskInfoTable.TaskTypeId, 3);
                        string   taskRunId         = sqlDataReader.Read(taskInfoTable.RunId, 4);
                        bool     isCanceled        = sqlDataReader.Read(taskInfoTable.IsCanceled, 5);
                        short    retryCount        = sqlDataReader.Read(taskInfoTable.RetryCount, 6);
                        short    maxRetryCount     = sqlDataReader.Read(taskInfoTable.MaxRetryCount, 7);
                        DateTime?heartbeatDateTime = sqlDataReader.Read(taskInfoTable.HeartbeatDateTime, 8);
                        string   inputData         = sqlDataReader.Read(taskInfoTable.InputData, 9);
                        string   taskContext       = sqlDataReader.Read(taskInfoTable.TaskContext, 10);
                        string   result            = sqlDataReader.Read(taskInfoTable.Result, 11);

                        return(new TaskInfo()
                        {
                            TaskId = taskId,
                            QueueId = queueId,
                            Status = (TaskStatus)status,
                            TaskTypeId = taskTypeId,
                            RunId = taskRunId,
                            IsCanceled = isCanceled,
                            RetryCount = retryCount,
                            MaxRetryCount = maxRetryCount,
                            HeartbeatDateTime = heartbeatDateTime,
                            InputData = inputData,
                            Context = taskContext,
                            Result = result,
                        });
                    }
                    catch (SqlException sqlEx)
                    {
                        if (sqlEx.Number == SqlErrorCodes.NotFound)
                        {
                            throw new TaskNotExistException(sqlEx.Message);
                        }

                        throw;
                    }
                }
        }
        public async Task HardDeleteAsync(ResourceKey key, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.HardDeleteResource.PopulateCommand(sqlCommandWrapper, resourceTypeId: _model.GetResourceTypeId(key.ResourceType), resourceId: key.Id);

                    await sqlCommandWrapper.ExecuteNonQueryAsync(cancellationToken);
                }
        }
Exemplo n.º 3
0
        public async Task <ResourceWrapper> UpdateSearchParameterIndicesAsync(ResourceWrapper resource, WeakETag weakETag, CancellationToken cancellationToken)
        {
            int?eTag = weakETag == null
                ? null
                : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    if (_schemaInformation.Current >= SchemaVersionConstants.AddMinMaxForDateAndStringSearchParamVersion)
                    {
                        VLatest.ReindexResource.PopulateCommand(
                            sqlCommandWrapper,
                            resourceTypeId: _model.GetResourceTypeId(resource.ResourceTypeName),
                            resourceId: resource.ResourceId,
                            eTag,
                            searchParamHash: resource.SearchParameterHash,
                            tableValuedParameters: _reindexResourceTvpGeneratorVLatest.Generate(new List <ResourceWrapper> {
                            resource
                        }));
                    }
                    else
                    {
                        V17.ReindexResource.PopulateCommand(
                            sqlCommandWrapper,
                            resourceTypeId: _model.GetResourceTypeId(resource.ResourceTypeName),
                            resourceId: resource.ResourceId,
                            eTag,
                            searchParamHash: resource.SearchParameterHash,
                            tableValuedParameters: _reindexResourceTvpGeneratorV17.Generate(new List <ResourceWrapper> {
                            resource
                        }));
                    }

                    try
                    {
                        await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                        return(resource);
                    }
                    catch (SqlException e)
                    {
                        switch (e.Number)
                        {
                        case SqlErrorCodes.PreconditionFailed:
                            _logger.LogError(string.Format(Core.Resources.ResourceVersionConflict, weakETag));
                            throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag));

                        default:
                            _logger.LogError(e, "Error from SQL database on reindex.");
                            throw;
                        }
                    }
                }
        }
Exemplo n.º 4
0
        public async Task HardDeleteAsync(ResourceKey key, CancellationToken cancellationToken)
        {
            await _model.EnsureInitialized();

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
            {
                using (var command = sqlConnectionWrapper.CreateSqlCommand())
                {
                    V1.HardDeleteResource.PopulateCommand(command, resourceTypeId: _model.GetResourceTypeId(key.ResourceType), resourceId: key.Id);

                    await command.ExecuteNonQueryAsync(cancellationToken);
                }
            }
        }
Exemplo n.º 5
0
        public async Task <IReadOnlyCollection <TaskInfo> > GetNextMessagesAsync(short count, int taskHeartbeatTimeoutThresholdInSeconds, CancellationToken cancellationToken)
        {
            List <TaskInfo> output = new List <TaskInfo>();

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.GetNextTask.PopulateCommand(sqlCommandWrapper, _taskHostingConfiguration.QueueId, count, taskHeartbeatTimeoutThresholdInSeconds);
                    SqlDataReader sqlDataReader = await sqlCommandWrapper.ExecuteReaderAsync(cancellationToken);

                    var taskInfoTable = VLatest.TaskInfo;
                    while (sqlDataReader.Read())
                    {
                        string   id                = sqlDataReader.Read(taskInfoTable.TaskId, 0);
                        string   queueId           = sqlDataReader.Read(taskInfoTable.QueueId, 1);
                        short    status            = sqlDataReader.Read(taskInfoTable.Status, 2);
                        short    taskTypeId        = sqlDataReader.Read(taskInfoTable.TaskTypeId, 3);
                        string   taskRunId         = sqlDataReader.Read(taskInfoTable.RunId, 4);
                        bool     isCanceled        = sqlDataReader.Read(taskInfoTable.IsCanceled, 5);
                        short    retryCount        = sqlDataReader.Read(taskInfoTable.RetryCount, 6);
                        short    maxRetryCount     = sqlDataReader.Read(taskInfoTable.MaxRetryCount, 7);
                        DateTime?heartbeatDateTime = sqlDataReader.Read(taskInfoTable.HeartbeatDateTime, 8);
                        string   inputData         = sqlDataReader.Read(taskInfoTable.InputData, 9);
                        string   taskContext       = sqlDataReader.Read(taskInfoTable.TaskContext, 10);
                        string   result            = sqlDataReader.Read(taskInfoTable.Result, 11);

                        TaskInfo taskInfo = new TaskInfo()
                        {
                            TaskId            = id,
                            QueueId           = queueId,
                            Status            = (TaskStatus)status,
                            TaskTypeId        = taskTypeId,
                            RunId             = taskRunId,
                            IsCanceled        = isCanceled,
                            RetryCount        = retryCount,
                            MaxRetryCount     = maxRetryCount,
                            HeartbeatDateTime = heartbeatDateTime,
                            InputData         = inputData,
                            Context           = taskContext,
                            Result            = result,
                        };

                        output.Add(taskInfo);
                    }
                }

            return(output);
        }
        public async Task <ExportJobOutcome> UpdateExportJobAsync(ExportJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            byte[] rowVersionAsBytes = GetRowVersionAsBytes(eTag);

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommand sqlCommand = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.UpdateExportJob.PopulateCommand(
                        sqlCommand,
                        jobRecord.Id,
                        jobRecord.Status.ToString(),
                        JsonConvert.SerializeObject(jobRecord, _jsonSerializerSettings),
                        rowVersionAsBytes);

                    try
                    {
                        var rowVersion = (byte[])await sqlCommand.ExecuteScalarAsync(cancellationToken);

                        if (rowVersion.NullIfEmpty() == null)
                        {
                            throw new OperationFailedException(string.Format(Core.Resources.OperationFailed, OperationsConstants.Export, "Failed to create export job because no row version was returned."), HttpStatusCode.InternalServerError);
                        }

                        return(new ExportJobOutcome(jobRecord, GetRowVersionAsEtag(rowVersion)));
                    }
                    catch (SqlException e)
                    {
                        if (e.Number == SqlErrorCodes.PreconditionFailed)
                        {
                            throw new JobConflictException();
                        }
                        else if (e.Number == SqlErrorCodes.NotFound)
                        {
                            throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                        }
                        else
                        {
                            _logger.LogError(e, "Error from SQL database on export job update.");
                            throw;
                        }
                    }
                }
        }
        public async Task <TaskInfo> GetTaskAsync(string taskId, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.GetTaskDetails.PopulateCommand(sqlCommandWrapper, taskId);
                    SqlDataReader sqlDataReader = await sqlCommandWrapper.ExecuteReaderAsync(cancellationToken);

                    if (!sqlDataReader.Read())
                    {
                        return(null);
                    }

                    var taskInfoTable = VLatest.TaskInfo;

                    string   id                = sqlDataReader.Read(taskInfoTable.TaskId, 0);
                    string   queueId           = sqlDataReader.Read(taskInfoTable.QueueId, 1);
                    short    status            = sqlDataReader.Read(taskInfoTable.Status, 2);
                    short    taskTypeId        = sqlDataReader.Read(taskInfoTable.TaskTypeId, 3);
                    string   taskRunId         = sqlDataReader.Read(taskInfoTable.RunId, 4);
                    bool     isCanceled        = sqlDataReader.Read(taskInfoTable.IsCanceled, 5);
                    short    retryCount        = sqlDataReader.Read(taskInfoTable.RetryCount, 6);
                    short    maxRetryCount     = sqlDataReader.Read(taskInfoTable.MaxRetryCount, 7);
                    DateTime?heartbeatDateTime = sqlDataReader.Read(taskInfoTable.HeartbeatDateTime, 8);
                    string   inputData         = sqlDataReader.Read(taskInfoTable.InputData, 9);
                    string   taskContext       = sqlDataReader.Read(taskInfoTable.TaskContext, 10);
                    string   result            = sqlDataReader.Read(taskInfoTable.Result, 11);

                    return(new TaskInfo()
                    {
                        TaskId = id,
                        QueueId = queueId,
                        Status = (TaskStatus)status,
                        TaskTypeId = taskTypeId,
                        RunId = taskRunId,
                        IsCanceled = isCanceled,
                        RetryCount = retryCount,
                        MaxRetryCount = maxRetryCount,
                        HeartbeatDateTime = heartbeatDateTime,
                        InputData = inputData,
                        Context = taskContext,
                        Result = result,
                    });
                }
        }
Exemplo n.º 8
0
        public async Task HardDeleteAsync(ResourceKey key, bool keepCurrentVersion, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    if (_schemaInformation.Current >= SchemaVersionConstants.PurgeHistoryVersion)
                    {
                        VLatest.HardDeleteResource.PopulateCommand(sqlCommandWrapper, resourceTypeId: _model.GetResourceTypeId(key.ResourceType), resourceId: key.Id, Convert.ToInt16(keepCurrentVersion));
                    }
                    else if (!keepCurrentVersion)
                    {
                        V12.HardDeleteResource.PopulateCommand(sqlCommandWrapper, resourceTypeId: _model.GetResourceTypeId(key.ResourceType), resourceId: key.Id);
                    }
                    else
                    {
                        throw new BadRequestException(Resources.SchemaVersionNeedsToBeUpgraded);
                    }

                    await sqlCommandWrapper.ExecuteNonQueryAsync(cancellationToken);
                }
        }
Exemplo n.º 9
0
        public async Task <ReindexJobWrapper> CreateReindexJobAsync(ReindexJobRecord jobRecord, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.CreateReindexJob.PopulateCommand(
                        sqlCommandWrapper,
                        jobRecord.Id,
                        jobRecord.Status.ToString(),
                        JsonConvert.SerializeObject(jobRecord, _jsonSerializerSettings));

                    var rowVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                    if (rowVersion == null)
                    {
                        throw new OperationFailedException(string.Format(Core.Resources.OperationFailed, OperationsConstants.Reindex, "Failed to create reindex job because no row version was returned."), HttpStatusCode.InternalServerError);
                    }

                    return(new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId(rowVersion.ToString())));
                }
        }
        public async Task <ExportJobOutcome> GetExportJobByHashAsync(string hash, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNullOrWhiteSpace(hash, nameof(hash));

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommand sqlCommand = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.GetExportJobByHash.PopulateCommand(sqlCommand, hash);

                    using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        if (!sqlDataReader.Read())
                        {
                            return(null);
                        }

                        (string rawJobRecord, byte[] rowVersion) = sqlDataReader.ReadRow(VLatest.ExportJob.RawJobRecord, VLatest.ExportJob.JobVersion);

                        return(CreateExportJobOutcome(rawJobRecord, rowVersion));
                    }
                }
        }
        public async Task <ExportJobOutcome> GetExportJobByIdAsync(string id, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNullOrWhiteSpace(id, nameof(id));

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommand sqlCommand = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.GetExportJobById.PopulateCommand(sqlCommand, id);

                    using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        if (!sqlDataReader.Read())
                        {
                            throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, id));
                        }

                        (string rawJobRecord, byte[] rowVersion) = sqlDataReader.ReadRow(VLatest.ExportJob.RawJobRecord, VLatest.ExportJob.JobVersion);

                        return(CreateExportJobOutcome(rawJobRecord, rowVersion));
                    }
                }
        }
        public async Task UpdateContextAsync(string context, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    try
                    {
                        VLatest.UpdateTaskContext.PopulateCommand(sqlCommandWrapper, _taskId, context, _runId);
                        await sqlCommandWrapper.ExecuteNonQueryAsync(cancellationToken);
                    }
                    catch (SqlException sqlEx)
                    {
                        _logger.LogInformation(sqlEx, "Failed to update context.");

                        if (sqlEx.Number == SqlErrorCodes.NotFound)
                        {
                            throw new TaskNotExistException(sqlEx.Message);
                        }

                        throw;
                    }
                }
        }
Exemplo n.º 13
0
        public async Task <(bool found, string id)> CheckActiveReindexJobsAsync(CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.CheckActiveReindexJobs.PopulateCommand(sqlCommandWrapper);

                    var activeJobs = new List <string>();

                    using (SqlDataReader sqlDataReader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        while (await sqlDataReader.ReadAsync(cancellationToken))
                        {
                            string id = sqlDataReader.ReadRow(VLatest.ReindexJob.Id);

                            activeJobs.Add(id);
                        }
                    }

                    // Currently, there can only be one active reindex job at a time.
                    return(activeJobs.Count > 0, activeJobs.Count > 0 ? activeJobs.FirstOrDefault() : string.Empty);
                }
        }
        public async Task <UpsertOutcome> UpsertAsync(ResourceWrapper resource, WeakETag weakETag, bool allowCreate, bool keepHistory, CancellationToken cancellationToken)
        {
            int etag = 0;

            if (weakETag != null && !int.TryParse(weakETag.VersionId, out etag))
            {
                // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.
                etag = -1;
            }

            var resourceMetadata = new ResourceMetadata(
                resource.CompartmentIndices,
                resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)),
                resource.LastModifiedClaims);

            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                    using (var stream = new RecyclableMemoryStream(_memoryStreamManager))
                        using (var gzipStream = new GZipStream(stream, CompressionMode.Compress))
                            using (var writer = new StreamWriter(gzipStream, ResourceEncoding))
                            {
                                writer.Write(resource.RawResource.Data);
                                writer.Flush();

                                stream.Seek(0, 0);

                                VLatest.UpsertResource.PopulateCommand(
                                    sqlCommandWrapper,
                                    baseResourceSurrogateId: ResourceSurrogateIdHelper.LastUpdatedToResourceSurrogateId(resource.LastModified.UtcDateTime),
                                    resourceTypeId: _model.GetResourceTypeId(resource.ResourceTypeName),
                                    resourceId: resource.ResourceId,
                                    eTag: weakETag == null ? null : (int?)etag,
                                    allowCreate: allowCreate,
                                    isDeleted: resource.IsDeleted,
                                    keepHistory: keepHistory,
                                    requestMethod: resource.Request.Method,
                                    rawResource: stream,
                                    tableValuedParameters: _upsertResourceTvpGeneratorVLatest.Generate(resourceMetadata));

                                try
                                {
                                    var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                                    if (newVersion == null)
                                    {
                                        // indicates a redundant delete
                                        return(null);
                                    }

                                    resource.Version = newVersion.ToString();

                                    return(new UpsertOutcome(resource, newVersion == 1 ? SaveOutcomeType.Created : SaveOutcomeType.Updated));
                                }
                                catch (SqlException e)
                                {
                                    switch (e.Number)
                                    {
                                    case SqlErrorCodes.PreconditionFailed:
                                        throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag?.VersionId));

                                    case SqlErrorCodes.NotFound:
                                        if (weakETag != null)
                                        {
                                            throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId));
                                        }

                                        goto default;

                                    case SqlErrorCodes.MethodNotAllowed:
                                        throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed);

                                    default:
                                        _logger.LogError(e, "Error from SQL database on upsert");
                                        throw;
                                    }
                                }
                            }
        }
Exemplo n.º 15
0
        public async Task <UpsertOutcome> UpsertAsync(
            ResourceWrapper resource,
            WeakETag weakETag,
            bool allowCreate,
            bool keepHistory,
            CancellationToken cancellationToken,
            bool requireETagOnUpdate = false)
        {
            int?eTag = weakETag == null
                ? null
                : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.

            var resourceMetadata = new ResourceMetadata(
                resource.CompartmentIndices,
                resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)),
                resource.LastModifiedClaims);

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                    using (var stream = new RecyclableMemoryStream(_memoryStreamManager))
                    {
                        _compressedRawResourceConverter.WriteCompressedRawResource(stream, resource.RawResource.Data);

                        stream.Seek(0, 0);

                        PopulateUpsertResourceCommand(sqlCommandWrapper, resource, resourceMetadata, allowCreate, keepHistory, requireETagOnUpdate, eTag, stream, _coreFeatures.SupportsResourceChangeCapture);

                        try
                        {
                            var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                            if (newVersion == null)
                            {
                                // indicates a redundant delete
                                return(null);
                            }

                            resource.Version = newVersion.ToString();

                            SaveOutcomeType saveOutcomeType;
                            if (newVersion == 1)
                            {
                                saveOutcomeType = SaveOutcomeType.Created;
                            }
                            else
                            {
                                saveOutcomeType = SaveOutcomeType.Updated;
                                resource.RawResource.IsMetaSet = false;
                            }

                            return(new UpsertOutcome(resource, saveOutcomeType));
                        }
                        catch (SqlException e)
                        {
                            switch (e.Number)
                            {
                            case SqlErrorCodes.PreconditionFailed:
                                if (weakETag != null)
                                {
                                    // The backwards compatibility behavior of Stu3 is to return 409 Conflict instead of a 412 Precondition Failed
                                    if (_modelInfoProvider.Version == FhirSpecification.Stu3)
                                    {
                                        throw new ResourceConflictException(weakETag);
                                    }

                                    throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag.VersionId));
                                }

                                goto default;

                            case SqlErrorCodes.NotFound:
                                if (weakETag != null)
                                {
                                    throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId));
                                }

                                goto default;

                            case SqlErrorCodes.MethodNotAllowed:
                                throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed);

                            case SqlErrorCodes.TimeoutExpired:
                                throw new RequestTimeoutException(Resources.ExecutionTimeoutExpired);

                            case 50400: // TODO: Add this to SQL error codes in AB#88286
                                // The backwards compatibility behavior of Stu3 is to return 412 Precondition Failed instead of a 400 Bad Request
                                if (_modelInfoProvider.Version == FhirSpecification.Stu3)
                                {
                                    throw new PreconditionFailedException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.ResourceTypeName));
                                }

                                throw new BadRequestException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.ResourceTypeName));

                            default:
                                _logger.LogError(e, "Error from SQL database on upsert");
                                throw;
                            }
                        }
                    }
        }
        public async Task <ResourceWrapper> GetAsync(ResourceKey key, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapper(true))
            {
                int?requestedVersion = null;
                if (!string.IsNullOrEmpty(key.VersionId))
                {
                    if (!int.TryParse(key.VersionId, out var parsedVersion))
                    {
                        return(null);
                    }

                    requestedVersion = parsedVersion;
                }

                using (SqlCommandWrapper commandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.ReadResource.PopulateCommand(
                        commandWrapper,
                        resourceTypeId: _model.GetResourceTypeId(key.ResourceType),
                        resourceId: key.Id,
                        version: requestedVersion);

                    using (SqlDataReader sqlDataReader = await commandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken))
                    {
                        if (!sqlDataReader.Read())
                        {
                            return(null);
                        }

                        var resourceTable = VLatest.Resource;

                        (long resourceSurrogateId, int version, bool isDeleted, bool isHistory, Stream rawResourceStream) = sqlDataReader.ReadRow(
                            resourceTable.ResourceSurrogateId,
                            resourceTable.Version,
                            resourceTable.IsDeleted,
                            resourceTable.IsHistory,
                            resourceTable.RawResource);

                        string rawResource;

                        using (rawResourceStream)
                            using (var gzipStream = new GZipStream(rawResourceStream, CompressionMode.Decompress))
                                using (var reader = new StreamReader(gzipStream, ResourceEncoding))
                                {
                                    rawResource = await reader.ReadToEndAsync();
                                }

                        bool isRawResourceMetaSet = false;

                        if (_schemaInformation.Current >= 4)
                        {
                            isRawResourceMetaSet = sqlDataReader.Read(resourceTable.IsRawResourceMetaSet, 5);
                        }

                        return(new ResourceWrapper(
                                   key.Id,
                                   version.ToString(CultureInfo.InvariantCulture),
                                   key.ResourceType,
                                   new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: isRawResourceMetaSet),
                                   null,
                                   new DateTimeOffset(ResourceSurrogateIdHelper.ResourceSurrogateIdToLastUpdated(resourceSurrogateId), TimeSpan.Zero),
                                   isDeleted,
                                   searchIndices: null,
                                   compartmentIndices: null,
                                   lastModifiedClaims: null)
                        {
                            IsHistory = isHistory,
                        });
                    }
                }
            }
        }
Exemplo n.º 17
0
        public async Task BulkUpdateSearchParameterIndicesAsync(IReadOnlyCollection <ResourceWrapper> resources, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    if (_schemaInformation.Current >= SchemaVersionConstants.AddMinMaxForDateAndStringSearchParamVersion)
                    {
                        VLatest.BulkReindexResources.PopulateCommand(
                            sqlCommandWrapper,
                            _bulkReindexResourcesTvpGeneratorVLatest.Generate(resources.ToList()));
                    }
                    else
                    {
                        V17.BulkReindexResources.PopulateCommand(
                            sqlCommandWrapper,
                            _bulkReindexResourcesTvpGeneratorV17.Generate(resources.ToList()));
                    }

                    if (_schemaInformation.Current >= SchemaVersionConstants.BulkReindexReturnsFailuresVersion)
                    {
                        // We will reindex the rest of the batch if one resource has a versioning conflict
                        int?failedResourceCount;
                        try
                        {
                            failedResourceCount = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);
                        }
                        catch (SqlException e)
                        {
                            _logger.LogError(e, "Error from SQL database on reindex.");
                            throw;
                        }

                        if (failedResourceCount != 0)
                        {
                            string message    = string.Format(Core.Resources.ReindexingResourceVersionConflictWithCount, failedResourceCount);
                            string userAction = Core.Resources.ReindexingUserAction;

                            _logger.LogError(message);
                            throw new PreconditionFailedException(message + " " + userAction);
                        }
                    }
                    else
                    {
                        try
                        {
                            // We are running an earlier schema version where we will fail the whole batch if there is a conflict
                            await sqlCommandWrapper.ExecuteNonQueryAsync(cancellationToken);
                        }
                        catch (SqlException e)
                        {
                            switch (e.Number)
                            {
                            case SqlErrorCodes.PreconditionFailed:
                                string message    = Core.Resources.ReindexingResourceVersionConflict;
                                string userAction = Core.Resources.ReindexingUserAction;

                                _logger.LogError(message);
                                throw new PreconditionFailedException(message + " " + userAction);

                            default:
                                _logger.LogError(e, "Error from SQL database on reindex.");
                                throw;
                            }
                        }
                    }
                }
        }
Exemplo n.º 18
0
        public async Task <UpsertOutcome> UpsertAsync(ResourceWrapper resource, WeakETag weakETag, bool allowCreate, bool keepHistory, CancellationToken cancellationToken)
        {
            int?eTag = weakETag == null
                ? null
                : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources.

            var resourceMetadata = new ResourceMetadata(
                resource.CompartmentIndices,
                resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)),
                resource.LastModifiedClaims);

            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                    using (var stream = new RecyclableMemoryStream(_memoryStreamManager))
                    {
                        CompressedRawResourceConverter.WriteCompressedRawResource(stream, resource.RawResource.Data);

                        stream.Seek(0, 0);

                        PopulateUpsertResourceCommand(sqlCommandWrapper, resource, resourceMetadata, allowCreate, keepHistory, eTag, stream);

                        try
                        {
                            var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken);

                            if (newVersion == null)
                            {
                                // indicates a redundant delete
                                return(null);
                            }

                            resource.Version = newVersion.ToString();

                            SaveOutcomeType saveOutcomeType;
                            if (newVersion == 1)
                            {
                                saveOutcomeType = SaveOutcomeType.Created;
                            }
                            else
                            {
                                saveOutcomeType = SaveOutcomeType.Updated;
                                resource.RawResource.IsMetaSet = false;
                            }

                            return(new UpsertOutcome(resource, saveOutcomeType));
                        }
                        catch (SqlException e)
                        {
                            switch (e.Number)
                            {
                            case SqlErrorCodes.PreconditionFailed:
                                throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag?.VersionId));

                            case SqlErrorCodes.NotFound:
                                if (weakETag != null)
                                {
                                    throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId));
                                }

                                goto default;

                            case SqlErrorCodes.MethodNotAllowed:
                                throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed);

                            default:
                                _logger.LogError(e, "Error from SQL database on upsert");
                                throw;
                            }
                        }
                    }
        }