From ec54811f0a3ae7434d9146eb39427631e1c149a9 Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 7 May 2024 20:20:01 +0800 Subject: [PATCH] Add docs --- docs/configuration/hostbuilder.md | 34 +++++++++++++++++-- docs/schema/migrations.md | 2 +- .../MartenServiceCollectionExtensionsTests.cs | 31 ++++++++++++++++- src/Marten/StoreOptions.cs | 2 +- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/docs/configuration/hostbuilder.md b/docs/configuration/hostbuilder.md index f7622250a47..108050fddd6 100644 --- a/docs/configuration/hostbuilder.md +++ b/docs/configuration/hostbuilder.md @@ -129,7 +129,7 @@ services.AddMarten() .UseLightweightSessions() .UseNpgsqlDataSource(); ``` -snippet source | anchor +snippet source | anchor If you're on .NET 8 (and above), you can also use a dedicated [keyed registration](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services). This can be useful for scenarios where you need more than one data source registered: @@ -143,9 +143,39 @@ services.AddMarten() .UseLightweightSessions() .UseNpgsqlDataSource(); ``` -snippet source | anchor +snippet source | anchor +## Using a Multi-Host Data Source + +Marten includes support for `NpgsqlMultiHostDataSource`, allowing you to spread queries over your read replicas, potentially improving throughput in read-heavy applications. To get started, your connection string should specify your primary host along with all replicas, per [Npgsql documentation](https://www.npgsql.org/doc/failover-and-load-balancing.html). + +Configuring `NpgsqlMultiHostDataSource` is very similar to a normal data source, simply swapping it for `AddMultiHostNpgsqlDataSource`. Marten will always use the primary node for queries with a `NpgsqlMultiHostDataSource` unless you explictly opt to use the standby nodes. You can adjust what type of node Marten uses for querying via the `MultiHostSettings` store options: + + + +```cs +services.AddMultiHostNpgsqlDataSource(ConnectionSource.ConnectionString); + +services.AddMarten(x => + { + // Will prefer standby nodes for querying. + x.Advanced.MultiHostSettings.ReadSessionPreference = TargetSessionAttributes.PreferStandby; + }) + .UseLightweightSessions() + .UseNpgsqlDataSource(); +``` +snippet source | anchor + + +::: warning +Marten will only use your read node preference with user queries (using IQuerySession) that are using a Marten-managed lifetime. + +Internal queries, including the async daemon, will always use your primary node for reliability. + +Ensure your replication delay is acceptable as you risk returning outdated queries. +::: + ## Composite Configuration with ConfigureMarten() The `AddMarten()` mechanism assumes that you are expressing all of the Marten configuration in one place and "know" what that configuration is upfront. Consider these possibilities where that isn't necessarily possible or desirable: diff --git a/docs/schema/migrations.md b/docs/schema/migrations.md index d37cefee26d..ab42fa08cc5 100644 --- a/docs/schema/migrations.md +++ b/docs/schema/migrations.md @@ -154,7 +154,7 @@ services.AddMarten(opts => // database changes on application startup .ApplyAllDatabaseChangesOnStartup(); ``` -snippet source | anchor +snippet source | anchor In the option above, Marten is calling the same functionality within an `IHostedService` background task. diff --git a/src/CoreTests/MartenServiceCollectionExtensionsTests.cs b/src/CoreTests/MartenServiceCollectionExtensionsTests.cs index 677adc93b89..0e95c5f7e49 100644 --- a/src/CoreTests/MartenServiceCollectionExtensionsTests.cs +++ b/src/CoreTests/MartenServiceCollectionExtensionsTests.cs @@ -14,6 +14,7 @@ using Marten.Testing.Harness; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Npgsql; using Shouldly; using Weasel.Core.Migrations; using Xunit; @@ -138,7 +139,7 @@ public void no_error_if_IHostEnvironment_does_not_exist() var rules = store.Options.CreateGenerationRules(); rules.ApplicationAssembly.ShouldBe(store.Options.ApplicationAssembly); } - + [Fact] public async Task apply_changes_on_startup() { @@ -305,6 +306,34 @@ public async Task use_npgsql_data_source() Call(session).ShouldNotThrow(); } + [Fact] + public async Task use_npgsql_multi_host_data_source() + { + var services = new ServiceCollection(); + + #region sample_using_UseNpgsqlDataSourceMultiHost + + services.AddMultiHostNpgsqlDataSource(ConnectionSource.ConnectionString); + + services.AddMarten(x => + { + // Will prefer standby nodes for querying. + x.Advanced.MultiHostSettings.ReadSessionPreference = TargetSessionAttributes.PreferStandby; + }) + .UseLightweightSessions() + .UseNpgsqlDataSource(); + + #endregion + + var serviceProvider = services.BuildServiceProvider(); + + await using var session = serviceProvider.GetService(); + Func Call(IDocumentSession s) => () => s.Query().Any(); + Call(session).ShouldNotThrow(); + } + + + #if NET8_0 [Fact] public async Task use_npgsql_data_source_with_keyed_registration() diff --git a/src/Marten/StoreOptions.cs b/src/Marten/StoreOptions.cs index d94a423e2fc..62d6db6fdca 100644 --- a/src/Marten/StoreOptions.cs +++ b/src/Marten/StoreOptions.cs @@ -1025,7 +1025,7 @@ public void ModifySerializer(Action configure) public PostgresqlMigrator Migrator { get; } = new(); /// - /// Configuration options when using + /// Configuration options when using a /// public MultiHostSettings MultiHostSettings { get; } = new();