Skip to content

Commit 09d7ee2

Browse files
committed
feat: bg implementation
1 parent 4d4846b commit 09d7ee2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+5908
-577
lines changed

AwsWrapperDataProvider/AwsWrapperConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class AwsWrapperConnection : DbConnection, IWrapper
3939

4040
private bool deferredInitialization = false;
4141

42-
internal ConnectionPluginManager? PluginManager { get; private set; }
42+
public ConnectionPluginManager? PluginManager { get; private set; }
4343

4444
internal Dictionary<string, string>? ConnectionProperties { get; private set; }
4545

AwsWrapperDataProvider/AwsWrapperDataProvider.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4+
<Version>1.0.0</Version>
45
<TargetFramework>net8.0</TargetFramework>
56
<ImplicitUsings>enable</ImplicitUsings>
67
<Nullable>enable</Nullable>

AwsWrapperDataProvider/Driver/ConnectionPluginManager.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@
2020
using AwsWrapperDataProvider.Driver.HostInfo;
2121
using AwsWrapperDataProvider.Driver.HostListProviders;
2222
using AwsWrapperDataProvider.Driver.Plugins;
23+
using AwsWrapperDataProvider.Driver.Utils;
2324
using AwsWrapperDataProvider.Properties;
25+
using Microsoft.Extensions.Logging;
2426

2527
namespace AwsWrapperDataProvider.Driver;
2628

2729
public class ConnectionPluginManager
2830
{
2931
private readonly Dictionary<string, Delegate> pluginChainDelegates = [];
30-
protected IList<IConnectionPlugin> plugins = [];
32+
protected IList<IConnectionPlugin> Plugins = [];
33+
protected string[] ActivePluginCodes = [];
3134
protected IConnectionProvider defaultConnProvider;
3235
protected IConnectionProvider? effectiveConnProvider;
3336
protected ConfigurationProfile? configurationProfile;
@@ -74,7 +77,7 @@ public ConnectionPluginManager(
7477
{
7578
this.defaultConnProvider = defaultConnectionProvider;
7679
this.effectiveConnProvider = effectiveConnectionProvider;
77-
this.plugins = plugins;
80+
this.Plugins = plugins;
7881
this.ConnectionWrapper = connection;
7982
}
8083

@@ -84,12 +87,13 @@ public void InitConnectionPluginChain(
8487
{
8588
this.pluginService = pluginService;
8689
ConnectionPluginChainBuilder pluginChainBuilder = new();
87-
this.plugins = pluginChainBuilder.GetPlugins(
90+
this.Plugins = pluginChainBuilder.GetPlugins(
8891
this.pluginService,
8992
this.defaultConnProvider,
9093
this.effectiveConnProvider,
9194
props,
9295
this.configurationProfile);
96+
this.ActivePluginCodes = pluginChainBuilder.GetPluginCodes(this.pluginService, props);
9397
}
9498

9599
private async Task<T> ExecuteWithSubscribedPlugins<T>(
@@ -104,7 +108,7 @@ private async Task<T> ExecuteWithSubscribedPlugins<T>(
104108
if (!this.pluginChainDelegates.TryGetValue(methodName, out Delegate? del))
105109
{
106110
del = this.MakePluginChainDelegate<T>(methodName);
107-
this.pluginChainDelegates.Add(methodName, del);
111+
this.pluginChainDelegates[methodName] = del;
108112
}
109113

110114
if (del is not PluginChainADONetDelegate<T> pluginChainDelegate)
@@ -130,10 +134,9 @@ private async Task<T> ExecuteWithSubscribedPlugins<T>(
130134
private PluginChainADONetDelegate<T> MakePluginChainDelegate<T>(string methodName)
131135
{
132136
PluginChainADONetDelegate<T>? pluginChainDelegate = null;
133-
134-
for (int i = this.plugins.Count - 1; i >= 0; i--)
137+
for (int i = this.Plugins.Count - 1; i >= 0; i--)
135138
{
136-
IConnectionPlugin plugin = this.plugins[i];
139+
IConnectionPlugin plugin = this.Plugins[i];
137140
IReadOnlySet<string> subscribedMethods = plugin.SubscribedMethods;
138141
bool isSubscribed = subscribedMethods.Contains(AllMethods) || subscribedMethods.Contains(methodName);
139142

@@ -248,4 +251,9 @@ public virtual bool AcceptsStrategy(string strategy)
248251
{
249252
return this.defaultConnProvider.AcceptsStrategy(strategy);
250253
}
254+
255+
public bool IsPluginActive(string pluginCode)
256+
{
257+
return this.ActivePluginCodes.Contains(pluginCode);
258+
}
251259
}

AwsWrapperDataProvider/Driver/Dialects/AuroraMySqlDialect.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
namespace AwsWrapperDataProvider.Driver.Dialects;
2323

24-
public class AuroraMySqlDialect : MySqlDialect
24+
public class AuroraMySqlDialect : MySqlDialect, IBlueGreenDialect
2525
{
2626
private static readonly ILogger<AuroraMySqlDialect> Logger = LoggerUtils.GetLogger<AuroraMySqlDialect>();
2727

@@ -39,6 +39,10 @@ public class AuroraMySqlDialect : MySqlDialect
3939
private static readonly string IsWriterQuery = "SELECT SERVER_ID FROM information_schema.replica_host_status "
4040
+ "WHERE SESSION_ID = 'MASTER_SESSION_ID' AND SERVER_ID = @@aurora_server_id";
4141

42+
private static readonly string AuroraMySqlBgTopologyExistsQuery = "SELECT 1 AS tmp FROM information_schema.tables WHERE table_schema = 'mysql' AND table_name = 'rds_topology'";
43+
44+
private static readonly string AuroraMySqlBgStatusQuery = "SELECT * FROM mysql.rds_topology";
45+
4246
public override IList<Type> DialectUpdateCandidates { get; } = [
4347
typeof(RdsMultiAzDbClusterMySqlDialect),
4448
];
@@ -54,7 +58,7 @@ public override async Task<bool> IsDialect(DbConnection connection)
5458
}
5559
catch (Exception ex) when (this.ExceptionHandler.IsSyntaxError(ex))
5660
{
57-
// Syntax error - expected when querying against incorrect dialect
61+
Logger.LogTrace(ex, Resources.Error_CantCheckDialect_Syntax, nameof(AuroraMySqlDialect));
5862
}
5963
catch (Exception ex)
6064
{
@@ -85,4 +89,14 @@ private HostListProviderSupplier GetHostListProviderSupplier()
8589
NodeIdQuery,
8690
IsReaderQuery);
8791
}
92+
93+
public async Task<bool> IsBlueGreenStatusAvailable(DbConnection connection)
94+
{
95+
return await DialectUtils.CheckExistenceQueries(connection, this.ExceptionHandler, Logger, AuroraMySqlBgTopologyExistsQuery);
96+
}
97+
98+
public string GetBlueGreenStatusQuery()
99+
{
100+
return AuroraMySqlBgStatusQuery;
101+
}
88102
}

AwsWrapperDataProvider/Driver/Dialects/AuroraPgDialect.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using System.Data.Common;
16+
using System.Reflection;
1617
using AwsWrapperDataProvider.Driver.HostListProviders;
1718
using AwsWrapperDataProvider.Driver.HostListProviders.Monitoring;
1819
using AwsWrapperDataProvider.Driver.Utils;
@@ -21,7 +22,7 @@
2122

2223
namespace AwsWrapperDataProvider.Driver.Dialects;
2324

24-
public class AuroraPgDialect : PgDialect, IAuroraLimitlessDialect
25+
public class AuroraPgDialect : PgDialect, IAuroraLimitlessDialect, IBlueGreenDialect
2526
{
2627
private const string ReaderOrdinal = "aurora_stat_utils";
2728

@@ -48,6 +49,11 @@ public class AuroraPgDialect : PgDialect, IAuroraLimitlessDialect
4849
private static readonly string IsWriterQuery = "SELECT SERVER_ID FROM pg_catalog.aurora_replica_status() "
4950
+ "WHERE SESSION_ID OPERATOR(pg_catalog.=) 'MASTER_SESSION_ID' AND SERVER_ID OPERATOR(pg_catalog.=) aurora_db_instance_identifier()";
5051

52+
protected static readonly string AuroraPostgreSqlBgTopologyExistsQuery = "SELECT 'pg_catalog.get_blue_green_fast_switchover_metadata'::regproc";
53+
54+
protected static readonly string DriverVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0";
55+
protected static readonly string AuroraPostgreSqlBgStatusQuery = $"SELECT * FROM pg_catalog.get_blue_green_fast_switchover_metadata('aws_advanced_dotnet_data_provider_wrapper-{DriverVersion}')";
56+
5157
public override IList<Type> DialectUpdateCandidates { get; } = [
5258
typeof(RdsMultiAzDbClusterPgDialect),
5359
];
@@ -83,7 +89,7 @@ public override async Task<bool> IsDialect(DbConnection connection)
8389
}
8490
catch (Exception ex) when (this.ExceptionHandler.IsSyntaxError(ex))
8591
{
86-
// Syntax error - expected when querying against incorrect dialect
92+
Logger.LogTrace(ex, Resources.Error_CantCheckDialect_Syntax, nameof(AuroraPgDialect));
8793
}
8894
catch (Exception ex)
8995
{
@@ -115,5 +121,15 @@ private HostListProviderSupplier GetHostListProviderSupplier()
115121
IsReaderQuery);
116122
}
117123

124+
public async Task<bool> IsBlueGreenStatusAvailable(DbConnection connection)
125+
{
126+
return await DialectUtils.CheckExistenceQueries(connection, this.ExceptionHandler, Logger, AuroraPostgreSqlBgTopologyExistsQuery);
127+
}
128+
129+
public string GetBlueGreenStatusQuery()
130+
{
131+
return AuroraPostgreSqlBgStatusQuery;
132+
}
133+
118134
public string LimitlessRouterEndpointQuery { get => "SELECT router_endpoint, load from aurora_limitless_router_endpoints()"; }
119135
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// You may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Data.Common;
16+
using AwsWrapperDataProvider.Driver.Exceptions;
17+
using AwsWrapperDataProvider.Properties;
18+
using Microsoft.Extensions.Logging;
19+
20+
namespace AwsWrapperDataProvider.Driver.Dialects;
21+
22+
public static class DialectUtils
23+
{
24+
public static async Task<bool> CheckExistenceQueries(DbConnection connection, IExceptionHandler exceptionHandler, ILogger logger, string query)
25+
{
26+
try
27+
{
28+
await using var command = connection.CreateCommand();
29+
command.CommandText = query;
30+
await using var reader = await command.ExecuteReaderAsync();
31+
return await reader.ReadAsync();
32+
}
33+
catch (Exception ex) when (exceptionHandler.IsSyntaxError(ex))
34+
{
35+
// Syntax error - expected when querying against incorrect dialect
36+
}
37+
catch (Exception ex)
38+
{
39+
logger.LogTrace(ex, Resources.Error_CantCheckDialect, nameof(AuroraMySqlDialect));
40+
}
41+
42+
return false;
43+
}
44+
45+
public static bool IsBlueGreenConnectionDialect(IDialect dialect)
46+
{
47+
return dialect is IBlueGreenDialect;
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License").
4+
// You may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Data.Common;
16+
17+
namespace AwsWrapperDataProvider.Driver.Dialects;
18+
19+
public interface IBlueGreenDialect
20+
{
21+
Task<bool> IsBlueGreenStatusAvailable(DbConnection connection);
22+
23+
string GetBlueGreenStatusQuery();
24+
}

AwsWrapperDataProvider/Driver/Dialects/RdsMultiAzDbClusterPgDialect.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class RdsMultiAzDbClusterPgDialect : PgDialect
3232
private static readonly ILogger<RdsMultiAzDbClusterPgDialect> Logger = LoggerUtils.GetLogger<RdsMultiAzDbClusterPgDialect>();
3333

3434
private static readonly string TopologyQuery =
35-
$"SELECT id, endpoint, port FROM rds_tools.show_topology('aws_dotnet_driver-{DriverVersion}')";
35+
$"SELECT id, endpoint, port FROM rds_tools.show_topology('aws_advanced_dotnet_data_provider_wrapper-{DriverVersion}')";
3636

3737
private static readonly string FetchWriterNodeQuery =
3838
"SELECT multi_az_db_cluster_source_dbi_resource_id FROM rds_tools.multi_az_db_cluster_source_dbi_resource_id()"

AwsWrapperDataProvider/Driver/Dialects/RdsMySqlDialect.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@
1919

2020
namespace AwsWrapperDataProvider.Driver.Dialects;
2121

22-
public class RdsMySqlDialect : MySqlDialect
22+
public class RdsMySqlDialect : MySqlDialect, IBlueGreenDialect
2323
{
24+
protected static readonly string RdsMySqlTopologyTableExistsQuery = "SELECT 1 AS tmp FROM information_schema.tables WHERE" +
25+
" table_schema = 'mysql' AND table_name = 'rds_topology'";
26+
27+
protected static readonly string RdsMySqlBgStatusQuery = "SELECT * FROM mysql.rds_topology";
28+
2429
private static readonly ILogger<RdsMySqlDialect> Logger = LoggerUtils.GetLogger<RdsMySqlDialect>();
2530

2631
public override IList<Type> DialectUpdateCandidates { get; } =
@@ -56,7 +61,7 @@ public override async Task<bool> IsDialect(DbConnection conn)
5661
}
5762
catch (Exception ex) when (this.ExceptionHandler.IsSyntaxError(ex))
5863
{
59-
// Syntax error - expected when querying against incorrect dialect
64+
Logger.LogTrace(ex, Resources.Error_CantCheckDialect_Syntax, nameof(RdsPgDialect));
6065
}
6166
catch (Exception ex)
6267
{
@@ -65,4 +70,14 @@ public override async Task<bool> IsDialect(DbConnection conn)
6570

6671
return false;
6772
}
73+
74+
public async Task<bool> IsBlueGreenStatusAvailable(DbConnection connection)
75+
{
76+
return await DialectUtils.CheckExistenceQueries(connection, this.ExceptionHandler, Logger, RdsMySqlTopologyTableExistsQuery);
77+
}
78+
79+
public string GetBlueGreenStatusQuery()
80+
{
81+
return RdsMySqlBgStatusQuery;
82+
}
6883
}

AwsWrapperDataProvider/Driver/Dialects/RdsPgDialect.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919

2020
namespace AwsWrapperDataProvider.Driver.Dialects;
2121

22-
public class RdsPgDialect : PgDialect
22+
public class RdsPgDialect : PgDialect, IBlueGreenDialect
2323
{
2424
internal const string ExtensionsSql = "SELECT (setting LIKE '%rds_tools%') AS rds_tools, "
2525
+ "(setting LIKE '%aurora_stat_utils%') AS aurora_stat_utils "
2626
+ "FROM pg_catalog.pg_settings "
2727
+ "WHERE name OPERATOR(pg_catalog.=) 'rds.extensions'";
2828

29+
protected static readonly string RdsPgTopologyTableExistsQuery = "SELECT 'rds_tools.show_topology'::regproc";
30+
31+
protected static readonly string RdsPgBgStatusQuery = "SELECT * FROM rds_tools.show_topology('aws_advanced_dotnet_data_provider_wrapper')";
32+
2933
private static readonly ILogger<RdsPgDialect> Logger = LoggerUtils.GetLogger<RdsPgDialect>();
3034

3135
public override IList<Type> DialectUpdateCandidates { get; } =
@@ -58,7 +62,7 @@ public override async Task<bool> IsDialect(DbConnection conn)
5862
}
5963
catch (Exception ex) when (this.ExceptionHandler.IsSyntaxError(ex))
6064
{
61-
// Syntax error - expected when querying against incorrect dialect
65+
Logger.LogTrace(ex, Resources.Error_CantCheckDialect_Syntax, nameof(RdsPgDialect));
6266
}
6367
catch (Exception ex)
6468
{
@@ -67,4 +71,14 @@ public override async Task<bool> IsDialect(DbConnection conn)
6771

6872
return false;
6973
}
74+
75+
public async Task<bool> IsBlueGreenStatusAvailable(DbConnection connection)
76+
{
77+
return await DialectUtils.CheckExistenceQueries(connection, this.ExceptionHandler, Logger, RdsPgTopologyTableExistsQuery);
78+
}
79+
80+
public string GetBlueGreenStatusQuery()
81+
{
82+
return RdsPgBgStatusQuery;
83+
}
7084
}

0 commit comments

Comments
 (0)