diff --git a/src/Microsoft.Health.Fhir.Api.UnitTests/Microsoft.Health.Fhir.R4.Api.UnitTests.csproj b/src/Microsoft.Health.Fhir.Api.UnitTests/Microsoft.Health.Fhir.R4.Api.UnitTests.csproj
index a1020bb235..ea7defa336 100644
--- a/src/Microsoft.Health.Fhir.Api.UnitTests/Microsoft.Health.Fhir.R4.Api.UnitTests.csproj
+++ b/src/Microsoft.Health.Fhir.Api.UnitTests/Microsoft.Health.Fhir.R4.Api.UnitTests.csproj
@@ -15,6 +15,9 @@
+
+
+
diff --git a/src/Microsoft.Health.Fhir.Api/Features/Health/StorageInitializedHealthCheck.cs b/src/Microsoft.Health.Fhir.Api/Features/Health/StorageInitializedHealthCheck.cs
new file mode 100644
index 0000000000..2c7506a590
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Api/Features/Health/StorageInitializedHealthCheck.cs
@@ -0,0 +1,45 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediatR;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Health.Core;
+using Microsoft.Health.Fhir.Core.Extensions;
+using Microsoft.Health.Fhir.Core.Messages.Storage;
+
+namespace Microsoft.Health.Fhir.Api.Features.Health
+{
+ public class StorageInitializedHealthCheck : IHealthCheck, INotificationHandler
+ {
+ private const string SuccessfullyInitializedMessage = "Successfully initialized.";
+ private bool _storageReady;
+ private readonly DateTimeOffset _started = Clock.UtcNow;
+
+ public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ if (_storageReady)
+ {
+ return Task.FromResult(HealthCheckResult.Healthy(SuccessfullyInitializedMessage));
+ }
+
+ TimeSpan waited = Clock.UtcNow - _started;
+ if (waited < TimeSpan.FromMinutes(5))
+ {
+ return Task.FromResult(new HealthCheckResult(HealthStatus.Degraded, $"Storage is initializing. Waited: {(int)waited.TotalSeconds}s."));
+ }
+
+ return Task.FromResult(new HealthCheckResult(HealthStatus.Unhealthy, $"Storage has not been initialized. Waited: {(int)waited.TotalSeconds}s."));
+ }
+
+ public Task Handle(StorageInitializedNotification notification, CancellationToken cancellationToken)
+ {
+ _storageReady = true;
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.R4B.Api.UnitTests/Microsoft.Health.Fhir.R4B.Api.UnitTests.csproj b/src/Microsoft.Health.Fhir.R4B.Api.UnitTests/Microsoft.Health.Fhir.R4B.Api.UnitTests.csproj
index bf44b92bcb..b0e4e1feff 100644
--- a/src/Microsoft.Health.Fhir.R4B.Api.UnitTests/Microsoft.Health.Fhir.R4B.Api.UnitTests.csproj
+++ b/src/Microsoft.Health.Fhir.R4B.Api.UnitTests/Microsoft.Health.Fhir.R4B.Api.UnitTests.csproj
@@ -15,6 +15,9 @@
+
+
+
diff --git a/src/Microsoft.Health.Fhir.R5.Api.UnitTests/Microsoft.Health.Fhir.R5.Api.UnitTests.csproj b/src/Microsoft.Health.Fhir.R5.Api.UnitTests/Microsoft.Health.Fhir.R5.Api.UnitTests.csproj
index 239a28af30..7adb326aad 100644
--- a/src/Microsoft.Health.Fhir.R5.Api.UnitTests/Microsoft.Health.Fhir.R5.Api.UnitTests.csproj
+++ b/src/Microsoft.Health.Fhir.R5.Api.UnitTests/Microsoft.Health.Fhir.R5.Api.UnitTests.csproj
@@ -15,6 +15,9 @@
+
+
+
diff --git a/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Health/StorageInitializedHealthCheckTests.cs b/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Health/StorageInitializedHealthCheckTests.cs
new file mode 100644
index 0000000000..9679d19e5d
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Health/StorageInitializedHealthCheckTests.cs
@@ -0,0 +1,52 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Health.Fhir.Core.Extensions;
+using Microsoft.Health.Fhir.Core.Messages.Storage;
+using Microsoft.Health.Fhir.Tests.Common;
+using Microsoft.Health.Test.Utilities;
+using Xunit;
+
+namespace Microsoft.Health.Fhir.Api.Features.Health;
+
+[Trait(Traits.OwningTeam, OwningTeam.Fhir)]
+[Trait(Traits.Category, Categories.DataSourceValidation)]
+public class StorageInitializedHealthCheckTests
+{
+ private readonly StorageInitializedHealthCheck _sut = new();
+
+ [Fact]
+ public async Task GivenStorageInitialized_WhenCheckHealthAsync_ThenReturnsHealthy()
+ {
+ await _sut.Handle(new StorageInitializedNotification(), CancellationToken.None);
+
+ HealthCheckResult result = await _sut.CheckHealthAsync(new HealthCheckContext(), CancellationToken.None);
+
+ Assert.Equal(HealthStatus.Healthy, result.Status);
+ }
+
+ [Fact]
+ public async Task GivenStorageInitializedHealthCheck_WhenCheckHealthAsync_ThenStartsAsDegraded()
+ {
+ HealthCheckResult result = await _sut.CheckHealthAsync(new HealthCheckContext(), CancellationToken.None);
+ Assert.Equal(HealthStatus.Degraded, result.Status);
+ }
+
+#if NET8_0_OR_GREATER
+ [Fact]
+ public async Task GivenStorageInitializedHealthCheck_WhenCheckHealthAsync_ThenChangedToUnhealthyAfter5Minute()
+ {
+ using (Mock.Property(() => ClockResolver.TimeProvider, new Microsoft.Extensions.Time.Testing.FakeTimeProvider(DateTimeOffset.Now.AddMinutes(5).AddSeconds(1))))
+ {
+ HealthCheckResult result = await _sut.CheckHealthAsync(new HealthCheckContext(), CancellationToken.None);
+ Assert.Equal(HealthStatus.Unhealthy, result.Status);
+ }
+ }
+#endif
+}
diff --git a/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Microsoft.Health.Fhir.Shared.Api.UnitTests.projitems b/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Microsoft.Health.Fhir.Shared.Api.UnitTests.projitems
index c237305398..9dd83921e8 100644
--- a/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Microsoft.Health.Fhir.Shared.Api.UnitTests.projitems
+++ b/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Microsoft.Health.Fhir.Shared.Api.UnitTests.projitems
@@ -58,6 +58,7 @@
+
@@ -80,7 +81,4 @@
Never
-
-
-
\ No newline at end of file
diff --git a/src/Microsoft.Health.Fhir.Shared.Api/Modules/FhirModule.cs b/src/Microsoft.Health.Fhir.Shared.Api/Modules/FhirModule.cs
index bbae24e968..c79a4e4849 100644
--- a/src/Microsoft.Health.Fhir.Shared.Api/Modules/FhirModule.cs
+++ b/src/Microsoft.Health.Fhir.Shared.Api/Modules/FhirModule.cs
@@ -35,6 +35,7 @@
using Microsoft.Health.Fhir.Core.Features.Persistence;
using Microsoft.Health.Fhir.Core.Features.Security;
using Microsoft.Health.Fhir.Core.Messages.CapabilityStatement;
+using Microsoft.Health.Fhir.Core.Messages.Storage;
using Microsoft.Health.Fhir.Core.Models;
namespace Microsoft.Health.Fhir.Api.Modules
@@ -176,6 +177,16 @@ ResourceElement SetMetadata(Resource resource, string versionId, DateTimeOffset
services.AddHealthChecks().AddCheck(name: "BehaviorHealthCheck");
+ // Registers a health check to ensure storage gets initialized
+ services.RemoveServiceTypeExact>()
+ .Add()
+ .Singleton()
+ .AsSelf()
+ .AsService()
+ .AsService>();
+
+ services.AddHealthChecks().AddCheck(name: "StorageInitializedHealthCheck");
+
services.AddLazy();
services.AddScoped();
diff --git a/src/Microsoft.Health.Fhir.Stu3.Api.UnitTests/Microsoft.Health.Fhir.Stu3.Api.UnitTests.csproj b/src/Microsoft.Health.Fhir.Stu3.Api.UnitTests/Microsoft.Health.Fhir.Stu3.Api.UnitTests.csproj
index 753994bc5c..0a72f20685 100644
--- a/src/Microsoft.Health.Fhir.Stu3.Api.UnitTests/Microsoft.Health.Fhir.Stu3.Api.UnitTests.csproj
+++ b/src/Microsoft.Health.Fhir.Stu3.Api.UnitTests/Microsoft.Health.Fhir.Stu3.Api.UnitTests.csproj
@@ -17,6 +17,9 @@
+
+
+