Skip to content

Commit 559b114

Browse files
authored
Fixing incorrect versioning on close records (#4786)
* Fixing incorrect versioning on close duplicates * 85.diff * Comment * Changing 1 minute in the future to 8 seconds
1 parent c8f678c commit 559b114

File tree

11 files changed

+5630
-14
lines changed

11 files changed

+5630
-14
lines changed

src/Microsoft.Health.Fhir.Shared.Core/Features/Operations/Import/ImportResourceParser.cs

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ public ImportResource Parse(long index, long offset, int length, string rawResou
4242
var lastUpdatedIsNull = importMode == ImportMode.InitialLoad || resource.Meta.LastUpdated == null;
4343
var lastUpdated = lastUpdatedIsNull ? Clock.UtcNow : resource.Meta.LastUpdated.Value;
4444
resource.Meta.LastUpdated = new DateTimeOffset(lastUpdated.DateTime.TruncateToMillisecond(), lastUpdated.Offset);
45+
if (!lastUpdatedIsNull && resource.Meta.LastUpdated.Value > Clock.UtcNow.AddSeconds(10)) // 5 sec is the max for the computers in the domain
46+
{
47+
throw new NotSupportedException("LastUpdated in the resource cannot be in the future.");
48+
}
4549

4650
var keepVersion = true;
4751
if (lastUpdatedIsNull || string.IsNullOrEmpty(resource.Meta.VersionId) || !int.TryParse(resource.Meta.VersionId, out var _))

src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.diff.sql

+418
Large diffs are not rendered by default.

src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Migrations/85.sql

+5,178
Large diffs are not rendered by default.

src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersion.cs

+1
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,6 @@ public enum SchemaVersion
9494
V82 = 82,
9595
V83 = 83,
9696
V84 = 84,
97+
V85 = 85,
9798
}
9899
}

src/Microsoft.Health.Fhir.SqlServer/Features/Schema/SchemaVersionConstants.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.Health.Fhir.SqlServer.Features.Schema
88
public static class SchemaVersionConstants
99
{
1010
public const int Min = (int)SchemaVersion.V83;
11-
public const int Max = (int)SchemaVersion.V84;
11+
public const int Max = (int)SchemaVersion.V85;
1212
public const int MinForUpgrade = (int)SchemaVersion.V83; // this is used for upgrade tests only
1313
public const int SearchParameterStatusSchemaVersion = (int)SchemaVersion.V6;
1414
public const int SupportForReferencesWithMissingTypeVersion = (int)SchemaVersion.V7;

src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Scripts/TransactionCheckWithInitialiScript.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ Go
1919

2020
INSERT INTO dbo.SchemaVersion
2121
VALUES
22-
(84, 'started')
22+
(85, 'started')
2323

2424
Go

src/Microsoft.Health.Fhir.SqlServer/Features/Schema/Sql/Sprocs/MergeResources.sql

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ BEGIN TRY
101101
ON B.ResourceTypeId = A.ResourceTypeId AND B.ResourceId = A.ResourceId AND B.IsHistory = 0
102102
OPTION (MAXDOP 1, OPTIMIZE FOR (@DummyTop = 1))
103103

104-
IF @RaiseExceptionOnConflict = 1 AND EXISTS (SELECT * FROM @ResourceInfos WHERE PreviousVersion IS NOT NULL AND Version <= PreviousVersion)
104+
-- Consider surrogate id out of allignment as a conflict
105+
IF @RaiseExceptionOnConflict = 1 AND EXISTS (SELECT * FROM @ResourceInfos WHERE (PreviousVersion IS NOT NULL AND Version <= PreviousVersion) OR (PreviousSurrogateId IS NOT NULL AND SurrogateId <= PreviousSurrogateId))
105106
THROW 50409, 'Resource has been recently updated or added, please compare the resource content in code for any duplicate updates', 1
106107

107108
INSERT INTO @PreviousSurrogateIds

src/Microsoft.Health.Fhir.SqlServer/Features/Storage/SqlServerFhirDataStore.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,8 @@ async Task MergeUnversioned(List<ImportResource> inputs, bool keepLastUpdated, b
627627
var inputsNoVersionForCheck = new List<ImportResource>();
628628
foreach (var input in inputs)
629629
{
630-
if (currentInDb.TryGetValue(input.ResourceWrapper.ToResourceKey(true), out var current) && input.ResourceWrapper.LastModified < current.LastModified)
630+
// Include inputs only with explicit lastUpdated (input.KeepLastUpdated = true)
631+
if (currentInDb.TryGetValue(input.ResourceWrapper.ToResourceKey(true), out var current) && input.KeepLastUpdated && input.ResourceWrapper.LastModified < current.LastModified)
631632
{
632633
inputsNoVersionForCheck.Add(input);
633634
}

src/Microsoft.Health.Fhir.SqlServer/Microsoft.Health.Fhir.SqlServer.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<!-- Properties used by sql task to generate full script -->
33
<PropertyGroup>
4-
<LatestSchemaVersion>84</LatestSchemaVersion>
4+
<LatestSchemaVersion>85</LatestSchemaVersion>
55
<GeneratedFullScriptPath>Features\Schema\Migrations\$(LatestSchemaVersion).sql</GeneratedFullScriptPath>
66
</PropertyGroup>
77

test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Import/ImportHistorySoftDeleteTestFixture.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private async Task<List<Resource>> SavePrerequisiteResourcesViaHttp(List<Resourc
8080
},
8181
new()
8282
{
83-
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow.AddMinutes(1)),
83+
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow.AddSeconds(8)),
8484
});
8585
}
8686

@@ -94,7 +94,7 @@ private async Task<List<Resource>> SavePrerequisiteResourcesViaHttp(List<Resourc
9494
},
9595
new()
9696
{
97-
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow.AddMinutes(1), versionId: "1"),
97+
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow.AddSeconds(8), versionId: "1"),
9898
});
9999
}
100100

@@ -106,7 +106,7 @@ private async Task<List<Resource>> SavePrerequisiteResourcesViaHttp(List<Resourc
106106
new()
107107
{
108108
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow, versionId: "1"),
109-
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow.AddMinutes(1), versionId: "1"),
109+
CreateTestPatient(id: sharedId, lastUpdated: DateTimeOffset.UtcNow.AddSeconds(8), versionId: "1"),
110110
});
111111
}
112112

test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Import/ImportTests.cs

+19-6
Original file line numberDiff line numberDiff line change
@@ -419,20 +419,33 @@ public async Task GivenIncrementalLoad_MultipleInputsWithImplicitVersionsExplici
419419
Assert.Equal("1", history.Resource.Entry[0].Resource.VersionId);
420420
}
421421

422+
[Fact]
423+
public async Task GivenIncrementalLoad_LastUpdatedOnResourceCannotBeInTheFuture()
424+
{
425+
var id = Guid.NewGuid().ToString("N");
426+
var ndJson = CreateTestPatient(id, DateTimeOffset.UtcNow.AddSeconds(20)); // set value higher than 10 seconds tolerance
427+
var location = (await ImportTestHelper.UploadFileAsync(ndJson, _fixture.StorageAccount)).location;
428+
var request = CreateImportRequest(location, ImportMode.IncrementalLoad, false, true);
429+
var result = await ImportCheckAsync(request, null, 1);
430+
var errorLocation = result.Error.ToArray()[0].Url;
431+
var errorContent = await ImportTestHelper.DownloadFileAsync(errorLocation, _fixture.StorageAccount);
432+
Assert.Contains("LastUpdated in the resource cannot be in the future.", errorContent);
433+
}
434+
422435
[Fact]
423436
public async Task GivenIncrementalLoad_MultipleInputsWithImplicitVersionsExplicitLastUpdatedAfterImplicit()
424437
{
425438
var id = Guid.NewGuid().ToString("N");
426439
var ndJson1 = CreateTestPatient(id);
427-
var ndJson2 = CreateTestPatient(id, DateTimeOffset.UtcNow.AddDays(1));
440+
var ndJson2 = CreateTestPatient(id, DateTimeOffset.UtcNow.AddSeconds(8), birhDate: "1990"); // last updated within 10 seconds tolerance, different content
428441
var location = (await ImportTestHelper.UploadFileAsync(ndJson1 + ndJson2, _fixture.StorageAccount)).location;
429442
var request = CreateImportRequest(location, ImportMode.IncrementalLoad, false, true);
430443
await ImportCheckAsync(request, null, 0);
431-
432444
var history = await _client.SearchAsync($"Patient/{id}/_history");
433445
Assert.Equal(2, history.Resource.Entry.Count);
434-
Assert.Equal("1", history.Resource.Entry[0].Resource.VersionId);
435-
Assert.Equal("-1", history.Resource.Entry[1].Resource.VersionId);
446+
//// same order
447+
Assert.True(int.Parse(history.Resource.Entry[0].Resource.VersionId) > int.Parse(history.Resource.Entry[1].Resource.VersionId));
448+
Assert.True(history.Resource.Entry[0].Resource.Meta.LastUpdated > history.Resource.Entry[1].Resource.Meta.LastUpdated);
436449
}
437450

438451
[Fact]
@@ -1725,7 +1738,7 @@ public async Task GivenImportRequestWithMultipleSameFile_ThenBadRequestShouldBeR
17251738
Assert.Equal(HttpStatusCode.BadRequest, fhirException.StatusCode);
17261739
}
17271740

1728-
private async Task<Uri> ImportCheckAsync(ImportRequest request, TestFhirClient client = null, int? errorCount = null)
1741+
private async Task<ImportJobResult> ImportCheckAsync(ImportRequest request, TestFhirClient client = null, int? errorCount = null)
17291742
{
17301743
client = client ?? _client;
17311744
Uri checkLocation = await ImportTestHelper.CreateImportTaskAsync(client, request);
@@ -1746,7 +1759,7 @@ private async Task<Uri> ImportCheckAsync(ImportRequest request, TestFhirClient c
17461759

17471760
Assert.NotEmpty(result.Request);
17481761

1749-
return checkLocation;
1762+
return result;
17501763
}
17511764

17521765
private async Task<HttpResponseMessage> ImportWaitAsync(Uri checkLocation, bool checkSuccessStatus = true)

0 commit comments

Comments
 (0)