public async Task <UpsertOutcome> UpsertAsync(ResourceWrapper resource, WeakETag weakETag, bool allowCreate, bool keepHistory, CancellationToken cancellationToken)
        {
            int?eTag = weakETag == null
                ? (int?)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();

                            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;
                            }
                        }
                    }
        }
        public async Task <TaskInfo> CompleteAsync(string taskId, TaskResultData taskResultData, string runId, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    try
                    {
                        VLatest.CompleteTask.PopulateCommand(sqlCommandWrapper, taskId, JsonConvert.SerializeObject(taskResultData), 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 <TaskInfo> CreateTaskAsync(TaskInfo task, bool isUniqueTaskByType, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    try
                    {
                        VLatest.CreateTask.PopulateCommand(sqlCommandWrapper, task.TaskId, task.QueueId, task.TaskTypeId, task.MaxRetryCount, task.InputData, isUniqueTaskByType);
                        SqlDataReader sqlDataReader = await sqlCommandWrapper.ExecuteReaderAsync(cancellationToken);

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

                        var taskInfoTable = VLatest.TaskInfo;

                        string   taskId            = 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);

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

                        throw;
                    }
                }
        }
예제 #4
0
        public async Task <ExportJobOutcome> CreateExportJobAsync(ExportJobRecord jobRecord, CancellationToken cancellationToken)
        {
            using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true))
                using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand())
                {
                    VLatest.CreateExportJob.PopulateCommand(
                        sqlCommandWrapper,
                        jobRecord.Id,
                        jobRecord.Hash,
                        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.Export, "Failed to create export job because no row version was returned."), HttpStatusCode.InternalServerError);
                    }

                    return(new ExportJobOutcome(jobRecord, WeakETag.FromVersionId(rowVersion.ToString())));
                }
        }
        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;
                    }
                }
        }
예제 #6
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;
                            }
                        }
                    }
        }