|
15 | 15 | using Npgsql.Properties; |
16 | 16 | using static Npgsql.Tests.Support.MockState; |
17 | 17 | using static Npgsql.Tests.TestUtil; |
| 18 | +using IsolationLevel = System.Transactions.IsolationLevel; |
| 19 | +using TransactionStatus = Npgsql.Internal.TransactionStatus; |
18 | 20 |
|
19 | 21 | namespace Npgsql.Tests; |
20 | 22 |
|
@@ -160,84 +162,6 @@ public async Task Valid_host_not_found(string targetSessionAttributes, MockState |
160 | 162 | _ = await postmasters[i].WaitForServerConnection(); |
161 | 163 | } |
162 | 164 |
|
163 | | - [Test] |
164 | | - [Description("Test that enlist returns a new connector if a previous connector is for an incompatible server type")] |
165 | | - public async Task Enlist_depends_on_session_attributes() |
166 | | - { |
167 | | - await using var primaryPostmaster = PgPostmasterMock.Start(state: Primary); |
168 | | - await using var standbyPostmaster = PgPostmasterMock.Start(state: Standby); |
169 | | - |
170 | | - var defaultCsb = new NpgsqlConnectionStringBuilder |
171 | | - { |
172 | | - Host = MultipleHosts(primaryPostmaster, standbyPostmaster), |
173 | | - ServerCompatibilityMode = ServerCompatibilityMode.NoTypeLoading, |
174 | | - Enlist = true, |
175 | | - }; |
176 | | - |
177 | | - using var _ = CreateTempPool(defaultCsb.ConnectionString, out var defaultConnectionString); |
178 | | - |
179 | | - var primaryCsb = new NpgsqlConnectionStringBuilder(defaultConnectionString) |
180 | | - { |
181 | | - TargetSessionAttributes = "primary", |
182 | | - }; |
183 | | - |
184 | | - var standbyCsb = new NpgsqlConnectionStringBuilder(defaultConnectionString) |
185 | | - { |
186 | | - TargetSessionAttributes = "standby", |
187 | | - }; |
188 | | - |
189 | | - var preferPrimaryCsb = new NpgsqlConnectionStringBuilder(defaultConnectionString) |
190 | | - { |
191 | | - TargetSessionAttributes = "prefer-primary", |
192 | | - }; |
193 | | - |
194 | | - var preferStandbyCsb = new NpgsqlConnectionStringBuilder(defaultConnectionString) |
195 | | - { |
196 | | - TargetSessionAttributes = "prefer-standby", |
197 | | - }; |
198 | | - |
199 | | - // Note that the transaction scope is not disposed due to a rollback (which isn't something a mock expects) |
200 | | - var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled); |
201 | | - |
202 | | - NpgsqlConnector primaryConnector; |
203 | | - NpgsqlConnector standbyConnector; |
204 | | - |
205 | | - using (var primaryConnection = await OpenConnectionAsync(primaryCsb.ConnectionString)) |
206 | | - { |
207 | | - primaryConnector = primaryConnection.Connector!; |
208 | | - } |
209 | | - |
210 | | - using (var preferPrimaryConnection = await OpenConnectionAsync(preferPrimaryCsb.ConnectionString)) |
211 | | - { |
212 | | - Assert.AreSame(primaryConnector, preferPrimaryConnection.Connector); |
213 | | - } |
214 | | - |
215 | | - using (var preferStandbyConnection = await OpenConnectionAsync(preferStandbyCsb.ConnectionString)) |
216 | | - { |
217 | | - Assert.AreSame(primaryConnector, preferStandbyConnection.Connector); |
218 | | - } |
219 | | - |
220 | | - using (var standbyConnection = await OpenConnectionAsync(standbyCsb.ConnectionString)) |
221 | | - { |
222 | | - standbyConnector = standbyConnection.Connector!; |
223 | | - } |
224 | | - |
225 | | - using (var preferPrimaryConnection = await OpenConnectionAsync(preferPrimaryCsb.ConnectionString)) |
226 | | - { |
227 | | - Assert.AreSame(standbyConnector, preferPrimaryConnection.Connector); |
228 | | - } |
229 | | - |
230 | | - using (var preferStandbyConnection = await OpenConnectionAsync(preferStandbyCsb.ConnectionString)) |
231 | | - { |
232 | | - Assert.AreSame(standbyConnector, preferStandbyConnection.Connector); |
233 | | - } |
234 | | - |
235 | | - Assert.AreNotSame(primaryConnector, standbyConnector); |
236 | | - |
237 | | - await primaryPostmaster.WaitForServerConnection(); |
238 | | - await standbyPostmaster.WaitForServerConnection(); |
239 | | - } |
240 | | - |
241 | 165 | [Test, Platform(Exclude = "MacOsX", Reason = "#3786")] |
242 | 166 | public void All_hosts_are_down() |
243 | 167 | { |
@@ -836,6 +760,75 @@ public async Task Clear_pool_one_host_only_on_admin_shutdown() |
836 | 760 | Assert.That(primaryConn.NpgsqlDataSource.Statistics.Total, Is.EqualTo(1)); |
837 | 761 | } |
838 | 762 |
|
| 763 | + [Test] |
| 764 | + [TestCase("any", true)] |
| 765 | + [TestCase("primary", true)] |
| 766 | + [TestCase("standby", false)] |
| 767 | + [TestCase("prefer-primary", true)] |
| 768 | + [TestCase("prefer-standby", false)] |
| 769 | + [TestCase("read-write", true)] |
| 770 | + [TestCase("read-only", false)] |
| 771 | + public async Task Transaction_enlist_reuses_connection(string targetSessionAttributes, bool primary) |
| 772 | + { |
| 773 | + await using var primaryPostmaster = PgPostmasterMock.Start(ConnectionString, state: Primary); |
| 774 | + await using var standbyPostmaster = PgPostmasterMock.Start(ConnectionString, state: Standby); |
| 775 | + var csb = new NpgsqlConnectionStringBuilder |
| 776 | + { |
| 777 | + Host = MultipleHosts(primaryPostmaster, standbyPostmaster), |
| 778 | + TargetSessionAttributes = targetSessionAttributes, |
| 779 | + ServerCompatibilityMode = ServerCompatibilityMode.NoTypeLoading, |
| 780 | + MaxPoolSize = 10, |
| 781 | + }; |
| 782 | + |
| 783 | + using var _ = CreateTempPool(csb, out var connString); |
| 784 | + |
| 785 | + using var scope = new TransactionScope(TransactionScopeOption.Required, |
| 786 | + new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled); |
| 787 | + |
| 788 | + var query1Task = Query(connString); |
| 789 | + |
| 790 | + var server = primary |
| 791 | + ? await primaryPostmaster.WaitForServerConnection() |
| 792 | + : await standbyPostmaster.WaitForServerConnection(); |
| 793 | + |
| 794 | + await server |
| 795 | + .WriteCommandComplete() |
| 796 | + .WriteReadyForQuery(TransactionStatus.InTransactionBlock) |
| 797 | + .WriteParseComplete() |
| 798 | + .WriteBindComplete() |
| 799 | + .WriteNoData() |
| 800 | + .WriteCommandComplete() |
| 801 | + .WriteReadyForQuery(TransactionStatus.InTransactionBlock) |
| 802 | + .FlushAsync(); |
| 803 | + await query1Task; |
| 804 | + |
| 805 | + var query2Task = Query(connString); |
| 806 | + await server |
| 807 | + .WriteParseComplete() |
| 808 | + .WriteBindComplete() |
| 809 | + .WriteNoData() |
| 810 | + .WriteCommandComplete() |
| 811 | + .WriteReadyForQuery(TransactionStatus.InTransactionBlock) |
| 812 | + .FlushAsync(); |
| 813 | + await query2Task; |
| 814 | + |
| 815 | + await server |
| 816 | + .WriteCommandComplete() |
| 817 | + .WriteReadyForQuery() |
| 818 | + .FlushAsync(); |
| 819 | + scope.Complete(); |
| 820 | + |
| 821 | + async Task Query(string connectionString) |
| 822 | + { |
| 823 | + await using var conn = new NpgsqlConnection(connectionString); |
| 824 | + await conn.OpenAsync(); |
| 825 | + |
| 826 | + await using var cmd = conn.CreateCommand(); |
| 827 | + cmd.CommandText = "SELECT 1"; |
| 828 | + await cmd.ExecuteNonQueryAsync(); |
| 829 | + } |
| 830 | + } |
| 831 | + |
839 | 832 | // This is the only test in this class which actually connects to PostgreSQL (the others use the PostgreSQL mock) |
840 | 833 | [Test, NonParallelizable] |
841 | 834 | public void IntegrationTest([Values] bool loadBalancing, [Values] bool alwaysCheckHostState) |
|
0 commit comments