diff --git a/src/Microsoft.Health.Fhir.Core/Exceptions/PreconditionFailedException.cs b/src/Microsoft.Health.Fhir.Core/Exceptions/PreconditionFailedException.cs
index ecc83dd75c..b21f4a29db 100644
--- a/src/Microsoft.Health.Fhir.Core/Exceptions/PreconditionFailedException.cs
+++ b/src/Microsoft.Health.Fhir.Core/Exceptions/PreconditionFailedException.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------
+using System;
using System.Diagnostics;
using Microsoft.Health.Fhir.Core.Models;
diff --git a/src/Microsoft.Health.Fhir.Core/Exceptions/TransactionFailureException.cs b/src/Microsoft.Health.Fhir.Core/Exceptions/TransactionFailureException.cs
new file mode 100644
index 0000000000..a7e985f962
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Core/Exceptions/TransactionFailureException.cs
@@ -0,0 +1,24 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System.Diagnostics;
+using Microsoft.Health.Fhir.Core.Models;
+
+namespace Microsoft.Health.Fhir.Core.Exceptions
+{
+ public sealed class TransactionFailureException : FhirException
+ {
+ public TransactionFailureException(string message)
+ : base(message)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(message), "Exception message should not be empty");
+
+ Issues.Add(new OperationOutcomeIssue(
+ OperationOutcomeConstants.IssueSeverity.Error,
+ OperationOutcomeConstants.IssueType.Exception,
+ message));
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.CosmosDb/Features/Storage/CosmosFhirDataStore.cs b/src/Microsoft.Health.Fhir.CosmosDb/Features/Storage/CosmosFhirDataStore.cs
index 44230b3dd5..e5652abf95 100644
--- a/src/Microsoft.Health.Fhir.CosmosDb/Features/Storage/CosmosFhirDataStore.cs
+++ b/src/Microsoft.Health.Fhir.CosmosDb/Features/Storage/CosmosFhirDataStore.cs
@@ -12,6 +12,7 @@
using System.Linq;
using System.Net;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using EnsureThat;
@@ -199,6 +200,13 @@ await Parallel.ForEachAsync(resources, parallelOptions, async (resource, innerCt
var result = new DataStoreOperationOutcome(upsertOutcome);
results.AddOrUpdate(identifier, _ => result, (_, _) => result);
}
+ catch (CosmosException cme) when (cme.StatusCode == HttpStatusCode.Forbidden && cme.SubStatusCode == 1014)
+ {
+ // https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/troubleshoot-forbidden#partition-key-exceeding-storage
+
+ _logger.LogWarning(cme, "PreconditionFailed: Partition reached maximum size.");
+ results.TryAdd(identifier, new DataStoreOperationOutcome(new PreconditionFailedException(Resources.MaxPartitionSizeErrorMessage)));
+ }
catch (RequestRateExceededException rateExceededException)
{
results.TryAdd(identifier, new DataStoreOperationOutcome(rateExceededException));
@@ -432,7 +440,7 @@ await _containerScope.Value.CreateTransactionalBatch(partitionKey)
continue;
}
- throw new InvalidOperationException(transactionalBatchResponse.ErrorMessage);
+ throw new TransactionFailureException(transactionalBatchResponse.ErrorMessage);
}
}
else
diff --git a/src/Microsoft.Health.Fhir.CosmosDb/Resources.Designer.cs b/src/Microsoft.Health.Fhir.CosmosDb/Resources.Designer.cs
index 2d2efdea74..ee6aeff2e0 100644
--- a/src/Microsoft.Health.Fhir.CosmosDb/Resources.Designer.cs
+++ b/src/Microsoft.Health.Fhir.CosmosDb/Resources.Designer.cs
@@ -204,6 +204,15 @@ internal static string KeyVaultWrapUnwrapFailure {
}
}
+ ///
+ /// Looks up a localized string similar to Partition reached maximum size..
+ ///
+ internal static string MaxPartitionSizeErrorMessage {
+ get {
+ return ResourceManager.GetString("MaxPartitionSizeErrorMessage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Not able to execute a query. Retry the operation..
///
diff --git a/src/Microsoft.Health.Fhir.CosmosDb/Resources.resx b/src/Microsoft.Health.Fhir.CosmosDb/Resources.resx
index c3a0475cf5..29a424e98a 100644
--- a/src/Microsoft.Health.Fhir.CosmosDb/Resources.resx
+++ b/src/Microsoft.Health.Fhir.CosmosDb/Resources.resx
@@ -174,4 +174,8 @@
Not able to execute a query. Retry the operation.
+
+ Partition reached maximum size.
+ Error raised when a partition reaches its max size.
+
\ No newline at end of file
diff --git a/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/Bundle/BundleHandler.cs b/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/Bundle/BundleHandler.cs
index c03e682707..70ea90ccf3 100644
--- a/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/Bundle/BundleHandler.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/Bundle/BundleHandler.cs
@@ -964,7 +964,16 @@ private void RefreshProfilesIfApplicable()
{
if (_forceProfilesRefresh)
{
- _profilesResolver.Refresh();
+ Stopwatch watch = Stopwatch.StartNew();
+ try
+ {
+ _profilesResolver.Refresh();
+ _logger.LogInformation("FHIR Profiles cache is refreshed. Elapsed time: {ElapsedMilliseconds}", watch.ElapsedMilliseconds);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "FHIR Profiles cache failed while refreshing. Elapsed time: {ElapsedMilliseconds}", watch.ElapsedMilliseconds);
+ }
}
}
@@ -986,6 +995,11 @@ private static OperationOutcome CreateOperationOutcome(OperationOutcome.IssueSev
private static string SanitizeString(string input)
{
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return string.Empty;
+ }
+
return input
.Replace(Environment.NewLine, string.Empty, StringComparison.OrdinalIgnoreCase)
.Replace("\r", " ", StringComparison.OrdinalIgnoreCase)