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); } }
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; } } } }
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); } } }
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, }); } }
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); } }
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; } } }
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; } } } }
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, }); } } } }
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; } } } } }
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; } } } }