From da94042882d773852056851292cda6070c54323d Mon Sep 17 00:00:00 2001 From: Mauro Servienti Date: Thu, 22 Jan 2026 07:06:12 +0100 Subject: [PATCH 1/5] Use the internal RavenDB HTTP client --- .../DatabaseSetup.cs | 2 +- .../LicenseStatusCheck.cs | 12 +++++------- .../DatabaseSetup.cs | 2 +- .../LicenseStatusCheck.cs | 12 +++++------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs b/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs index e2def6dfc0..f32f82bf2b 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs @@ -27,7 +27,7 @@ public async Task Execute(IDocumentStore documentStore, CancellationToken cancel await CreateIndexes(documentStore, configuration.EnableFullTextSearch, cancellationToken); - await LicenseStatusCheck.WaitForLicenseOrThrow(configuration, cancellationToken); + await LicenseStatusCheck.WaitForLicenseOrThrow(documentStore, configuration, cancellationToken); await ConfigureExpiration(documentStore, cancellationToken); } diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs index 9aee22a425..c88dcbe3f3 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs @@ -1,27 +1,25 @@ namespace ServiceControl.Audit.Persistence.RavenDB; using System; -using System.Net.Http; using System.Net.Http.Json; using System.Threading; using System.Threading.Tasks; +using Raven.Client.Documents; static class LicenseStatusCheck { record LicenseStatusFragment(string Id, string LicensedTo, string Status, bool Expired); - public static async Task WaitForLicenseOrThrow(DatabaseConfiguration configuration, CancellationToken cancellationToken) + public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, DatabaseConfiguration configuration, CancellationToken cancellationToken) { - using var client = new HttpClient - { - BaseAddress = new Uri(configuration.ServerConfiguration.ConnectionString ?? configuration.ServerConfiguration.ServerUrl) - }; + var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; + var licenseCheckUrl = (configuration.ServerConfiguration.ConnectionString ?? configuration.ServerConfiguration.ServerUrl).TrimEnd('/') + "/license/status"; // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown using var cts = new CancellationTokenSource(30_000); while (!cts.IsCancellationRequested) { - var httpResponse = await client.GetAsync("license/status", cancellationToken); + var httpResponse = await ravenConfiguredHttpClient.GetAsync(licenseCheckUrl, cancellationToken); var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); if (licenseStatus.Expired) { diff --git a/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs b/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs index c77342946b..7c01008d49 100644 --- a/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs +++ b/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs @@ -23,7 +23,7 @@ public async Task Execute(CancellationToken cancellationToken) await IndexCreation.CreateIndexesAsync(typeof(DatabaseSetup).Assembly, documentStore, null, null, cancellationToken); - await LicenseStatusCheck.WaitForLicenseOrThrow(settings, cancellationToken); + await LicenseStatusCheck.WaitForLicenseOrThrow(documentStore, settings, cancellationToken); await ConfigureExpiration(settings, cancellationToken); } diff --git a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs index deb1956cc6..8c54a20cb5 100644 --- a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs @@ -1,27 +1,25 @@ namespace ServiceControl.Persistence.RavenDB; using System; -using System.Net.Http; using System.Net.Http.Json; using System.Threading; using System.Threading.Tasks; +using Raven.Client.Documents; static class LicenseStatusCheck { record LicenseStatusFragment(string Id, string LicensedTo, string Status, bool Expired); - public static async Task WaitForLicenseOrThrow(RavenPersisterSettings configuration, CancellationToken cancellationToken) + public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, RavenPersisterSettings ravenPersisterSettings, CancellationToken cancellationToken) { - using var client = new HttpClient - { - BaseAddress = new Uri(configuration.ConnectionString ?? configuration.ServerUrl) - }; + var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; + var licenseCheckUrl = (ravenPersisterSettings.ConnectionString ?? ravenPersisterSettings.ServerUrl).TrimEnd('/') + "/license/status"; // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown using var cts = new CancellationTokenSource(30_000); while (!cts.IsCancellationRequested) { - var httpResponse = await client.GetAsync("license/status", cancellationToken); + var httpResponse = await ravenConfiguredHttpClient.GetAsync(licenseCheckUrl, cancellationToken); var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); if (licenseStatus.Expired) { From 913345a96e892c62f577c9f2f0977e652aac70fa Mon Sep 17 00:00:00 2001 From: Mauro Servienti Date: Thu, 22 Jan 2026 09:28:27 +0100 Subject: [PATCH 2/5] extract the server URL from the document store --- src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs | 2 +- .../LicenseStatusCheck.cs | 4 ++-- src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs | 2 +- src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs b/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs index f32f82bf2b..b1d42b3d00 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/DatabaseSetup.cs @@ -27,7 +27,7 @@ public async Task Execute(IDocumentStore documentStore, CancellationToken cancel await CreateIndexes(documentStore, configuration.EnableFullTextSearch, cancellationToken); - await LicenseStatusCheck.WaitForLicenseOrThrow(documentStore, configuration, cancellationToken); + await LicenseStatusCheck.WaitForLicenseOrThrow(documentStore, cancellationToken); await ConfigureExpiration(documentStore, cancellationToken); } diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs index c88dcbe3f3..7e4861caa5 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs @@ -10,10 +10,10 @@ static class LicenseStatusCheck { record LicenseStatusFragment(string Id, string LicensedTo, string Status, bool Expired); - public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, DatabaseConfiguration configuration, CancellationToken cancellationToken) + public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, CancellationToken cancellationToken) { var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; - var licenseCheckUrl = (configuration.ServerConfiguration.ConnectionString ?? configuration.ServerConfiguration.ServerUrl).TrimEnd('/') + "/license/status"; + var licenseCheckUrl = documentStore.Urls[0].TrimEnd('/') + "/license/status"; // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown using var cts = new CancellationTokenSource(30_000); diff --git a/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs b/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs index 7c01008d49..4b71a0a43b 100644 --- a/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs +++ b/src/ServiceControl.Persistence.RavenDB/DatabaseSetup.cs @@ -23,7 +23,7 @@ public async Task Execute(CancellationToken cancellationToken) await IndexCreation.CreateIndexesAsync(typeof(DatabaseSetup).Assembly, documentStore, null, null, cancellationToken); - await LicenseStatusCheck.WaitForLicenseOrThrow(documentStore, settings, cancellationToken); + await LicenseStatusCheck.WaitForLicenseOrThrow(documentStore, cancellationToken); await ConfigureExpiration(settings, cancellationToken); } diff --git a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs index 8c54a20cb5..a99915d0a5 100644 --- a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs @@ -10,10 +10,10 @@ static class LicenseStatusCheck { record LicenseStatusFragment(string Id, string LicensedTo, string Status, bool Expired); - public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, RavenPersisterSettings ravenPersisterSettings, CancellationToken cancellationToken) + public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, CancellationToken cancellationToken) { var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; - var licenseCheckUrl = (ravenPersisterSettings.ConnectionString ?? ravenPersisterSettings.ServerUrl).TrimEnd('/') + "/license/status"; + var licenseCheckUrl = documentStore.Urls[0].TrimEnd('/') + "/license/status"; // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown using var cts = new CancellationTokenSource(30_000); From 9425a4e71e1b4a46031c459167c56036c55f7793 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Thu, 22 Jan 2026 14:16:11 +0100 Subject: [PATCH 3/5] Refactor license status check with cancellation token --- .../LicenseStatusCheck.cs | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs index 7e4861caa5..357c5aa724 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs @@ -14,26 +14,32 @@ public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, Can { var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; var licenseCheckUrl = documentStore.Urls[0].TrimEnd('/') + "/license/status"; - - // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown - using var cts = new CancellationTokenSource(30_000); - while (!cts.IsCancellationRequested) + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + cts.CancelAfter(30_000); + + try { - var httpResponse = await ravenConfiguredHttpClient.GetAsync(licenseCheckUrl, cancellationToken); - var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); - if (licenseStatus.Expired) - { - throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); - } - - if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) + while (true) { - return; + var httpResponse = await ravenConfiguredHttpClient.GetAsync(licenseCheckUrl, cts.Token); + var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cts.Token); + if (licenseStatus.Expired) + { + throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); + } + + if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) + { + return; + } + + await Task.Delay(200, cts.Token); } - - await Task.Delay(200, cancellationToken); } - - throw new InvalidOperationException("Cannot validate the current RavenDB license. Please, contact support"); + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + throw new InvalidOperationException("Cannot validate the current RavenDB license. Please, contact support"); + } } -} \ No newline at end of file +} From 25a8151155bd769229dbd48ffe3c258ecc84fef6 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Thu, 22 Jan 2026 14:20:57 +0100 Subject: [PATCH 4/5] Fix whitespace --- .../LicenseStatusCheck.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs index 357c5aa724..342013df76 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/LicenseStatusCheck.cs @@ -14,10 +14,10 @@ public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, Can { var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; var licenseCheckUrl = documentStore.Urls[0].TrimEnd('/') + "/license/status"; - + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(30_000); - + try { while (true) @@ -28,12 +28,12 @@ public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, Can { throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); } - + if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) { return; } - + await Task.Delay(200, cts.Token); } } From d0d33bf3230aaea6f605b6db220c655c7c11af99 Mon Sep 17 00:00:00 2001 From: Mauro Servienti Date: Thu, 22 Jan 2026 17:47:18 +0100 Subject: [PATCH 5/5] Make the primary instance LicenseStatusCheck behave like the audit instance one --- .../LicenseStatusCheck.cs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs index a99915d0a5..6b7ff4736e 100644 --- a/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs +++ b/src/ServiceControl.Persistence.RavenDB/LicenseStatusCheck.cs @@ -15,25 +15,31 @@ public static async Task WaitForLicenseOrThrow(IDocumentStore documentStore, Can var ravenConfiguredHttpClient = documentStore.GetRequestExecutor().HttpClient; var licenseCheckUrl = documentStore.Urls[0].TrimEnd('/') + "/license/status"; - // Not linking to the incoming cancellationToken to ensure no OperationCancelledException prevents the last InvalidOperationException to be thrown - using var cts = new CancellationTokenSource(30_000); - while (!cts.IsCancellationRequested) + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + cts.CancelAfter(30_000); + + try { - var httpResponse = await ravenConfiguredHttpClient.GetAsync(licenseCheckUrl, cancellationToken); - var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); - if (licenseStatus.Expired) + while (!cts.IsCancellationRequested) { - throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); - } + var httpResponse = await ravenConfiguredHttpClient.GetAsync(licenseCheckUrl, cancellationToken); + var licenseStatus = await httpResponse.Content.ReadFromJsonAsync(cancellationToken); + if (licenseStatus.Expired) + { + throw new InvalidOperationException("The current RavenDB license is expired. Please, contact support"); + } - if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) - { - return; - } + if (licenseStatus.LicensedTo != null && licenseStatus.Id != null) + { + return; + } - await Task.Delay(200, cancellationToken); + await Task.Delay(200, cts.Token); + } + } + catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) + { + throw new InvalidOperationException("Cannot validate the current RavenDB license. Please, contact support"); } - - throw new InvalidOperationException("Cannot validate the current RavenDB license. Please, contact support"); } } \ No newline at end of file