diff --git a/docs/using-the-aws-driver/UsingTheAwsDriver.md b/docs/using-the-aws-driver/UsingTheAwsDriver.md index ea2a1fd65..f395dc565 100644 --- a/docs/using-the-aws-driver/UsingTheAwsDriver.md +++ b/docs/using-the-aws-driver/UsingTheAwsDriver.md @@ -33,6 +33,7 @@ In addition to the parameters that you can configure for the [MySQL Connector/OD | ---------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|------------------------------------------| | `ENABLE_CLUSTER_FAILOVER` | Set to `1` to enable the fast failover behaviour offered by the AWS ODBC Driver for MySQL. | bool | No | `1` | | `ALLOW_READER_CONNECTIONS` | Set to `1` to allow connections to reader instances during the failover process. | bool | No | `0` | +| `ENABLE_STRICT_READER_FAILOVER` | Set to `1` to only allow failover to reader nodes during the reader failover process. If enabled, reader failover to a writer node will only be allowed for single-node clusters. This parameter is only applicable when `ALLOW_READER_CONNECTIONS` is set to `1`. | bool | No | `0` | | `GATHER_PERF_METRICS` | Set to `1` to record failover-associated metrics. | bool | No | `0` | | `GATHER_PERF_METRICS_PER_INSTANCE` | Set to `1` to gather additional performance metrics per instance as well as cluster. | bool | No | `0` | | `HOST_PATTERN` | This parameter is not required unless connecting to an AWS RDS cluster via an IP address or custom domain URL. In those cases, this parameter specifies the cluster instance DNS pattern that will be used to build a complete instance endpoint. A "?" character in this pattern should be used as a placeholder for the DB instance identifiers of the instances in the cluster.

Example: `?.my-domain.com`, `any-subdomain.?.my-domain.com:9999`

Usecase Example: If your cluster instance endpoint follows this pattern:`instanceIdentifier1.customHost`, `instanceIdentifier2.customHost`, etc. and you want your initial connection to be to `customHost:1234`, then your connection string should look like this: `SERVER=customHost;PORT=1234;DATABASE=test;HOST_PATTERN=?.customHost`

If the provided connection string is not an IP address or custom domain, the driver will automatically acquire the cluster instance host pattern from the customer-provided connection string. | char\* | If connecting using an IP address or custom domain URL: Yes

Otherwise: No

See [Host Pattern](#host-pattern) for more details. | `NONE` | diff --git a/driver/failover.h b/driver/failover.h index 032a02b56..82c45cd54 100644 --- a/driver/failover.h +++ b/driver/failover.h @@ -73,6 +73,7 @@ class FAILOVER_READER_HANDLER { std::shared_ptr topology_service, std::shared_ptr connection_handler, int failover_timeout_ms, int failover_reader_connect_timeout, + bool enable_strict_reader_failover, unsigned long dbc_id, bool enable_logging = false); ~FAILOVER_READER_HANDLER(); @@ -100,6 +101,7 @@ class FAILOVER_READER_HANDLER { std::shared_ptr topology_service; std::shared_ptr connection_handler; const int READER_CONNECT_INTERVAL_SEC = 1; // 1 sec + bool enable_strict_reader_failover = false; std::shared_ptr logger = nullptr; unsigned long dbc_id = 0; }; diff --git a/driver/failover_handler.cc b/driver/failover_handler.cc index 74924bdc9..3e60b6240 100644 --- a/driver/failover_handler.cc +++ b/driver/failover_handler.cc @@ -81,7 +81,8 @@ FAILOVER_HANDLER::FAILOVER_HANDLER(DBC* dbc, DataSource* ds, this->failover_reader_handler = std::make_shared( this->topology_service, this->connection_handler, ds->failover_timeout, - ds->failover_reader_connect_timeout, dbc->id, ds->save_queries); + ds->failover_reader_connect_timeout, ds->enable_strict_reader_failover, + dbc->id, ds->save_queries); this->failover_writer_handler = std::make_shared( this->topology_service, this->failover_reader_handler, this->connection_handler, ds->failover_timeout, diff --git a/driver/failover_reader_handler.cc b/driver/failover_reader_handler.cc index cf9ebbf36..fc6d73d5b 100644 --- a/driver/failover_reader_handler.cc +++ b/driver/failover_reader_handler.cc @@ -40,11 +40,13 @@ FAILOVER_READER_HANDLER::FAILOVER_READER_HANDLER( std::shared_ptr topology_service, std::shared_ptr connection_handler, int failover_timeout_ms, int failover_reader_connect_timeout, + bool enable_strict_reader_failover, unsigned long dbc_id, bool enable_logging) : topology_service{topology_service}, connection_handler{connection_handler}, max_failover_timeout_ms{failover_timeout_ms}, reader_connect_timeout_ms{failover_reader_connect_timeout}, + enable_strict_reader_failover{enable_strict_reader_failover}, dbc_id{dbc_id} { if (enable_logging) @@ -69,7 +71,7 @@ READER_FAILOVER_RESULT FAILOVER_READER_HANDLER::failover( auto reader_result_future = std::async(std::launch::async, [=, &global_sync, &reader_result]() { std::vector> hosts_list; while (!global_sync.is_completed()) { - hosts_list = build_hosts_list(current_topology, true); + hosts_list = build_hosts_list(current_topology, !enable_strict_reader_failover); reader_result = get_connection_from_hosts(hosts_list, global_sync); if (reader_result.connected) { global_sync.mark_as_complete(true); diff --git a/installer/myodbc-installer.cc b/installer/myodbc-installer.cc index 2efc95d23..84673bd68 100644 --- a/installer/myodbc-installer.cc +++ b/installer/myodbc-installer.cc @@ -579,6 +579,7 @@ int list_datasource_details(DataSource *ds) /* Failover */ if (ds->enable_cluster_failover) printf("\tENABLE_CLUSTER_FAILOVER\n"); if (ds->allow_reader_connections) printf("\tALLOW_READER_CONNECTIONS\n"); + if (ds->enable_strict_reader_failover) printf("\tENABLE_STRICT_READER_FAILOVER\n"); if (ds->gather_perf_metrics) printf("\tGATHER_PERF_METRICS\n"); if (ds->gather_metrics_per_instance) printf("\tGATHER_METRICS_PER_INSTANCE\n"); if (ds->topology_refresh_rate) printf("\tTOPOLOGY_REFRESH_RATE=%d\n", ds->topology_refresh_rate); diff --git a/integration/connection_string_builder.h b/integration/connection_string_builder.h index f1531a99f..9b46aa432 100644 --- a/integration/connection_string_builder.h +++ b/integration/connection_string_builder.h @@ -43,15 +43,16 @@ class ConnectionString { ConnectionString() : m_dsn(""), m_server(""), m_port(-1), m_uid(""), m_pwd(""), m_db(""), m_log_query(true), m_allow_reader_connections(false), - m_multi_statements(false), m_enable_cluster_failover(true), m_failover_timeout(-1), - m_connect_timeout(-1), m_network_timeout(-1), m_host_pattern(""), + m_enable_strict_reader_failover(false), m_multi_statements(false), m_enable_cluster_failover(true), + m_failover_timeout(-1), m_connect_timeout(-1), m_network_timeout(-1), m_host_pattern(""), m_enable_failure_detection(true), m_failure_detection_time(-1), m_failure_detection_timeout(-1), m_failure_detection_interval(-1), m_failure_detection_count(-1), m_monitor_disposal_time(-1), m_read_timeout(-1), m_write_timeout(-1), m_auth_mode(""), m_auth_region(""), m_auth_host(""), m_auth_port(-1), m_auth_expiration(-1), m_secret_id(""), is_set_uid(false), is_set_pwd(false), is_set_db(false), is_set_log_query(false), - is_set_allow_reader_connections(false), is_set_multi_statements(false), is_set_enable_cluster_failover(false), + is_set_allow_reader_connections(false), is_set_enable_strict_reader_failover(false), + is_set_multi_statements(false), is_set_enable_cluster_failover(false), is_set_failover_timeout(false), is_set_connect_timeout(false), is_set_network_timeout(false), is_set_host_pattern(false), is_set_enable_failure_detection(false), is_set_failure_detection_time(false), is_set_failure_detection_timeout(false), is_set_failure_detection_interval(false), is_set_failure_detection_count(false), is_set_monitor_disposal_time(false), @@ -78,6 +79,9 @@ class ConnectionString { if (is_set_allow_reader_connections) { length += sprintf(conn_in + length, "ALLOW_READER_CONNECTIONS=%d;", m_allow_reader_connections ? 1 : 0); } + if (is_set_enable_strict_reader_failover) { + length += sprintf(conn_in + length, "ENABLE_STRICT_READER_FAILOVER=%d;", m_enable_strict_reader_failover ? 1 : 0); + } if (is_set_multi_statements) { length += sprintf(conn_in + length, "MULTI_STATEMENTS=%d;", m_multi_statements ? 1 : 0); } @@ -151,7 +155,7 @@ class ConnectionString { // Optional fields std::string m_uid, m_pwd, m_db; - bool m_log_query, m_allow_reader_connections, m_multi_statements, m_enable_cluster_failover; + bool m_log_query, m_allow_reader_connections, m_enable_strict_reader_failover, m_multi_statements, m_enable_cluster_failover; int m_failover_timeout, m_connect_timeout, m_network_timeout; std::string m_host_pattern; bool m_enable_failure_detection; @@ -160,7 +164,7 @@ class ConnectionString { int m_auth_port, m_auth_expiration; bool is_set_uid, is_set_pwd, is_set_db; - bool is_set_log_query, is_set_allow_reader_connections, is_set_multi_statements; + bool is_set_log_query, is_set_allow_reader_connections, is_set_enable_strict_reader_failover, is_set_multi_statements; bool is_set_enable_cluster_failover; bool is_set_failover_timeout, is_set_connect_timeout, is_set_network_timeout; bool is_set_host_pattern; @@ -207,6 +211,11 @@ class ConnectionString { is_set_allow_reader_connections = true; } + void set_enable_strict_reader_failover(const bool& enable_strict_reader_failover) { + m_enable_strict_reader_failover = enable_strict_reader_failover; + is_set_enable_strict_reader_failover = true; + } + void set_multi_statements(const bool& multi_statements) { m_multi_statements = multi_statements; is_set_multi_statements = true; @@ -354,6 +363,11 @@ class ConnectionStringBuilder { return *this; } + ConnectionStringBuilder& withEnableStrictReaderFailover(const bool& enable_strict_reader_failover) { + connection_string->set_enable_strict_reader_failover(enable_strict_reader_failover); + return *this; + } + ConnectionStringBuilder& withMultiStatements(const bool& multi_statements) { connection_string->set_multi_statements(multi_statements); return *this; diff --git a/integration/connection_string_builder_test.cc b/integration/connection_string_builder_test.cc index ae8e3b125..aed2b71f0 100644 --- a/integration/connection_string_builder_test.cc +++ b/integration/connection_string_builder_test.cc @@ -72,6 +72,7 @@ TEST_F(ConnectionStringBuilderTest, test_complete_string) { .withPWD("testPwd") .withLogQuery(false) .withAllowReaderConnections(true) + .withEnableStrictReaderFailover(true) .withMultiStatements(false) .withDSN("testDSN") .withFailoverTimeout(120000) @@ -88,7 +89,7 @@ TEST_F(ConnectionStringBuilderTest, test_complete_string) { .withEnableClusterFailover(true) .build(); - const std::string expected = "DSN=testDSN;SERVER=testServer;PORT=3306;UID=testUser;PWD=testPwd;DATABASE=testDb;LOG_QUERY=0;ALLOW_READER_CONNECTIONS=1;MULTI_STATEMENTS=0;ENABLE_CLUSTER_FAILOVER=1;FAILOVER_TIMEOUT=120000;CONNECT_TIMEOUT=20;NETWORK_TIMEOUT=20;HOST_PATTERN=?.testDomain;ENABLE_FAILURE_DETECTION=1;FAILURE_DETECTION_TIME=10000;FAILURE_DETECTION_INTERVAL=100;FAILURE_DETECTION_COUNT=4;MONITOR_DISPOSAL_TIME=300;"; + const std::string expected = "DSN=testDSN;SERVER=testServer;PORT=3306;UID=testUser;PWD=testPwd;DATABASE=testDb;LOG_QUERY=0;ALLOW_READER_CONNECTIONS=1;ENABLE_STRICT_READER_FAILOVER=1;MULTI_STATEMENTS=0;ENABLE_CLUSTER_FAILOVER=1;FAILOVER_TIMEOUT=120000;CONNECT_TIMEOUT=20;NETWORK_TIMEOUT=20;HOST_PATTERN=?.testDomain;ENABLE_FAILURE_DETECTION=1;FAILURE_DETECTION_TIME=10000;FAILURE_DETECTION_INTERVAL=100;FAILURE_DETECTION_COUNT=4;MONITOR_DISPOSAL_TIME=300;"; EXPECT_EQ(0, expected.compare(connection_string)); } diff --git a/integration/network_failover_integration_test.cc b/integration/network_failover_integration_test.cc index 27061ca05..2c3630e57 100644 --- a/integration/network_failover_integration_test.cc +++ b/integration/network_failover_integration_test.cc @@ -165,6 +165,20 @@ TEST_F(NetworkFailoverIntegrationTest, lost_connection_to_all_readers) { EXPECT_EQ(SQL_SUCCESS, SQLDisconnect(dbc)); } +TEST_F(NetworkFailoverIntegrationTest, lost_connection_to_all_readers_strict_reader_failover) { + connection_string = builder.withServer(reader_endpoint).withAllowReaderConnections(true).withEnableStrictReaderFailover(true).build(); + EXPECT_EQ(SQL_SUCCESS, SQLDriverConnect(dbc, nullptr, AS_SQLCHAR(connection_string.c_str()), SQL_NTS, conn_out, MAX_NAME_LEN, &len, SQL_DRIVER_NOPROMPT)); + + for (const auto& x : proxy_map) { + if (x.first != writer_id) { + disable_connectivity(x.second); + } + } + + assert_query_failed(dbc, SERVER_ID_QUERY, ERROR_COMM_LINK_FAILURE); + EXPECT_EQ(SQL_SUCCESS, SQLDisconnect(dbc)); +} + TEST_F(NetworkFailoverIntegrationTest, lost_connection_to_reader_instance) { connection_string = builder.withServer(reader_endpoint).build(); EXPECT_EQ(SQL_SUCCESS, SQLDriverConnect(dbc, nullptr, AS_SQLCHAR(connection_string.c_str()), SQL_NTS, conn_out, MAX_NAME_LEN, &len, SQL_DRIVER_NOPROMPT)); diff --git a/setupgui/callbacks.cc b/setupgui/callbacks.cc index 92ff10e63..e76bff8eb 100644 --- a/setupgui/callbacks.cc +++ b/setupgui/callbacks.cc @@ -315,6 +315,10 @@ void syncTabsData(HWND hwnd, DataSource *params) /* 4 - Failover */ GET_BOOL_TAB(FAILOVER_TAB, enable_cluster_failover); GET_BOOL_TAB(FAILOVER_TAB, allow_reader_connections); + if (READ_BOOL_TAB(FAILOVER_TAB, allow_reader_connections)) + { + GET_BOOL_TAB(FAILOVER_TAB, enable_strict_reader_failover); + } GET_BOOL_TAB(FAILOVER_TAB, gather_perf_metrics); if (READ_BOOL_TAB(FAILOVER_TAB, gather_perf_metrics)) { @@ -453,6 +457,15 @@ void syncTabs(HWND hwnd, DataSource *params) /* 4 - Failover */ SET_BOOL_TAB(FAILOVER_TAB, enable_cluster_failover); SET_BOOL_TAB(FAILOVER_TAB, allow_reader_connections); + if (READ_BOOL_TAB(FAILOVER_TAB, allow_reader_connections)) + { +#ifdef _WIN32 + SET_ENABLED(FAILOVER_TAB, IDC_CHECK_enable_strict_reader_failover, TRUE); +#endif + SET_CHECKED_TAB(FAILOVER_TAB, allow_reader_connections, TRUE); + SET_BOOL_TAB(FAILOVER_TAB, enable_strict_reader_failover); + } + SET_BOOL_TAB(FAILOVER_TAB, enable_strict_reader_failover); SET_BOOL_TAB(FAILOVER_TAB, gather_perf_metrics); if(READ_BOOL_TAB(FAILOVER_TAB, gather_perf_metrics)) { diff --git a/setupgui/windows/odbcdialogparams.cpp b/setupgui/windows/odbcdialogparams.cpp index 87ac215e6..ba4a61c3e 100644 --- a/setupgui/windows/odbcdialogparams.cpp +++ b/setupgui/windows/odbcdialogparams.cpp @@ -704,6 +704,18 @@ void FormMain_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) EnableWindow(secret_id, usingSecretsManager); } break; + case IDC_CHECK_allow_reader_connections: + { + HWND failoverTab = TabCtrl_1.hTabPages[FAILOVER_TAB - 1]; + assert(failoverTab); + HWND prefetch = GetDlgItem(failoverTab, IDC_CHECK_enable_strict_reader_failover); + assert(prefetch); + + EnableWindow(prefetch, !!Button_GetCheck(GetDlgItem(failoverTab, + IDC_CHECK_allow_reader_connections))); + setBoolFieldData(failoverTab, IDC_CHECK_enable_strict_reader_failover, Button_GetCheck(prefetch)); + } + break; case IDC_CHECK_gather_perf_metrics: { HWND failoverTab = TabCtrl_1.hTabPages[FAILOVER_TAB-1]; diff --git a/setupgui/windows/odbcdialogparams.rc b/setupgui/windows/odbcdialogparams.rc index 13388c5c7..98d79523f 100644 --- a/setupgui/windows/odbcdialogparams.rc +++ b/setupgui/windows/odbcdialogparams.rc @@ -215,28 +215,30 @@ BEGIN "Button",BS_AUTOCHECKBOX | WS_TABSTOP, 12, 12, 89, 10 CONTROL "&Allow reader connections", IDC_CHECK_allow_reader_connections, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 12, 27, 100, 10 + CONTROL "&Enable strict reader failover", IDC_CHECK_enable_strict_reader_failover, + "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED, 132, 27, 100, 10 CONTROL "&Gather performance metrics",IDC_CHECK_gather_perf_metrics, "Button",BS_AUTOCHECKBOX | WS_TABSTOP, 12, 42, 100, 10 CONTROL "&Gather instance-specific metrics",IDC_CHECK_gather_metrics_per_instance, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED, 12, 57, 115, 10 - RTEXT "&Host pattern:",IDC_STATIC,12,75,117,8 - EDITTEXT IDC_EDIT_host_pattern,132,73,196,12,ES_AUTOHSCROLL - RTEXT "&Cluster ID:",IDC_STATIC,12,90,116,8 - EDITTEXT IDC_EDIT_cluster_id,132,88,196,12,ES_AUTOHSCROLL - RTEXT "Topology refresh rate (ms):",IDC_STATIC,12,105,116,8 - EDITTEXT IDC_EDIT_topology_refresh_rate,132,103,64,12,ES_AUTOHSCROLL | ES_NUMBER - RTEXT "Failover timeout (ms):",IDC_STATIC,12,120,116,8 - EDITTEXT IDC_EDIT_failover_timeout,132,118,64,12,ES_AUTOHSCROLL | ES_NUMBER - RTEXT "Failover topology refresh rate (ms):",IDC_STATIC,11,135,117,8 - EDITTEXT IDC_EDIT_failover_topology_refresh_rate,132,133,64,12,ES_AUTOHSCROLL | ES_NUMBER - RTEXT "Writer reconnect interval (ms):",IDC_STATIC,11,150,117,8 - EDITTEXT IDC_EDIT_failover_writer_reconnect_interval,132,148,64,12,ES_AUTOHSCROLL | ES_NUMBER - RTEXT "Reader connect timeout (ms):",IDC_STATIC,11,165,117,8 - EDITTEXT IDC_EDIT_failover_reader_connect_timeout,132,162,64,12,ES_AUTOHSCROLL | ES_NUMBER - RTEXT "Host Connect timeout (s):",IDC_STATIC,11,180,117,8 - EDITTEXT IDC_EDIT_connect_timeout,132,178,64,12,ES_AUTOHSCROLL | ES_NUMBER - RTEXT "Host Read/Write timeout (s):",IDC_STATIC,11,195,117,8 - EDITTEXT IDC_EDIT_network_timeout,132,193,64,12,ES_AUTOHSCROLL | ES_NUMBER + "Button",BS_AUTOCHECKBOX | WS_TABSTOP | WS_DISABLED, 132, 42, 115, 10 + RTEXT "&Host pattern:",IDC_STATIC,12,60,117,8 + EDITTEXT IDC_EDIT_host_pattern,132,58,196,12,ES_AUTOHSCROLL + RTEXT "&Cluster ID:",IDC_STATIC,12,77,116,8 + EDITTEXT IDC_EDIT_cluster_id,132,75,196,12,ES_AUTOHSCROLL + RTEXT "Topology refresh rate (ms):",IDC_STATIC,12,94,116,8 + EDITTEXT IDC_EDIT_topology_refresh_rate,132,92,64,12,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Failover timeout (ms):",IDC_STATIC,12,111,116,8 + EDITTEXT IDC_EDIT_failover_timeout,132,109,64,12,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Failover topology refresh rate (ms):",IDC_STATIC,11,128,117,8 + EDITTEXT IDC_EDIT_failover_topology_refresh_rate,132,126,64,12,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Writer reconnect interval (ms):",IDC_STATIC,11,145,117,8 + EDITTEXT IDC_EDIT_failover_writer_reconnect_interval,132,143,64,12,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Reader connect timeout (ms):",IDC_STATIC,11,162,117,8 + EDITTEXT IDC_EDIT_failover_reader_connect_timeout,132,160,64,12,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Host Connect timeout (s):",IDC_STATIC,11,179,117,8 + EDITTEXT IDC_EDIT_connect_timeout,132,177,64,12,ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Host Read/Write timeout (s):",IDC_STATIC,11,196,117,8 + EDITTEXT IDC_EDIT_network_timeout,132,194,64,12,ES_AUTOHSCROLL | ES_NUMBER END IDD_TAB5 DIALOGEX 0, 0, 209, 181 diff --git a/setupgui/windows/resource.h b/setupgui/windows/resource.h index 6dec0867e..17fe0ea57 100644 --- a/setupgui/windows/resource.h +++ b/setupgui/windows/resource.h @@ -172,6 +172,7 @@ #define IDC_EDIT_connect_timeout 10088 #define IDC_EDIT_network_timeout 10089 #define IDC_CHECK_allow_reader_connections 10090 +#define IDC_CHECK_enable_strict_reader_failover 10091 #define IDC_CHECK_enable_failure_detection 10100 #define IDC_EDIT_failure_detection_time 10101 #define IDC_EDIT_failure_detection_interval 10102 diff --git a/unit_testing/failover_reader_handler_test.cc b/unit_testing/failover_reader_handler_test.cc index 00255f89d..4a8569f7a 100644 --- a/unit_testing/failover_reader_handler_test.cc +++ b/unit_testing/failover_reader_handler_test.cc @@ -155,7 +155,7 @@ TEST_F(FailoverReaderHandlerTest, GenerateTopology) { } TEST_F(FailoverReaderHandlerTest, BuildHostsList) { - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, false, 0); std::shared_ptr topology_info; std::vector> hosts_list; @@ -227,7 +227,7 @@ TEST_F(FailoverReaderHandlerTest, GetConnectionFromHosts_Failure) { EXPECT_CALL(*mock_ts, mark_host_down(reader_c_host)).Times(1); EXPECT_CALL(*mock_ts, mark_host_down(writer_host)).Times(1); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, false, 0); auto hosts_list = reader_handler.build_hosts_list(topology, true); READER_FAILOVER_RESULT result = reader_handler.get_connection_from_hosts(hosts_list, std::ref(mock_sync)); @@ -250,7 +250,7 @@ TEST_F(FailoverReaderHandlerTest, GetConnectionFromHosts_Success_Reader) { // Reader C will not be used as it is put at the end. Will only try to connect to A and B EXPECT_CALL(*mock_ts, mark_host_up(reader_a_host)).Times(1); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, false, 0); auto hosts_list = reader_handler.build_hosts_list(topology, true); READER_FAILOVER_RESULT result = reader_handler.get_connection_from_hosts(hosts_list, std::ref(mock_sync)); @@ -276,7 +276,7 @@ TEST_F(FailoverReaderHandlerTest, GetConnectionFromHosts_Success_Writer) { EXPECT_CALL(*mock_ts, mark_host_up(writer_host)).Times(1); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, false, 0); auto hosts_list = reader_handler.build_hosts_list(topology, true); READER_FAILOVER_RESULT result = reader_handler.get_connection_from_hosts(hosts_list, std::ref(mock_sync)); @@ -313,7 +313,7 @@ TEST_F(FailoverReaderHandlerTest, GetConnectionFromHosts_FastestHost) { return mock_reader_b_proxy; })); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, false, 0); auto hosts_list = reader_handler.build_hosts_list(topology, true); READER_FAILOVER_RESULT result = reader_handler.get_connection_from_hosts(hosts_list, std::ref(mock_sync)); @@ -352,7 +352,7 @@ TEST_F(FailoverReaderHandlerTest, GetConnectionFromHosts_Timeout) { EXPECT_CALL(*mock_ts, mark_host_down(_)).Times(AnyNumber()); EXPECT_CALL(*mock_ts, mark_host_down(writer_host)).Times(1); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 1000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 1000, false, 0); auto hosts_list = reader_handler.build_hosts_list(topology, true); READER_FAILOVER_RESULT result = reader_handler.get_connection_from_hosts(hosts_list, std::ref(mock_sync)); @@ -372,7 +372,7 @@ TEST_F(FailoverReaderHandlerTest, Failover_Failure) { EXPECT_CALL(*mock_ts, mark_host_down(reader_c_host)).Times(1); EXPECT_CALL(*mock_ts, mark_host_down(writer_host)).Times(1); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 3000, 1000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 3000, 1000, false, 0); READER_FAILOVER_RESULT result = reader_handler.failover(topology); EXPECT_FALSE(result.connected); @@ -409,7 +409,7 @@ TEST_F(FailoverReaderHandlerTest, Failover_Success_Reader) { return mock_reader_b_proxy; })); - FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, 0); + FAILOVER_READER_HANDLER reader_handler(mock_ts, mock_connection_handler, 60000, 30000, false, 0); READER_FAILOVER_RESULT result = reader_handler.failover(current_topology); EXPECT_TRUE(result.connected); diff --git a/unit_testing/mock_objects.h b/unit_testing/mock_objects.h index 2914eefae..e234b633d 100644 --- a/unit_testing/mock_objects.h +++ b/unit_testing/mock_objects.h @@ -108,7 +108,7 @@ class MOCK_TOPOLOGY_SERVICE : public TOPOLOGY_SERVICE { class MOCK_READER_HANDLER : public FAILOVER_READER_HANDLER { public: - MOCK_READER_HANDLER() : FAILOVER_READER_HANDLER(nullptr, nullptr, 0, 0, 0) {} + MOCK_READER_HANDLER() : FAILOVER_READER_HANDLER(nullptr, nullptr, 0, 0, false, 0) {} MOCK_METHOD(READER_FAILOVER_RESULT, get_reader_connection, (std::shared_ptr, FAILOVER_SYNC&)); }; diff --git a/util/installer.cc b/util/installer.cc index a22d4142a..f83f170f5 100644 --- a/util/installer.cc +++ b/util/installer.cc @@ -245,6 +245,7 @@ static SQLWCHAR W_AUTH_SECRET_ID[] = { 'S', 'E', 'C', 'R', 'E', 'T', '_', 'I', ' /* Failover */ static SQLWCHAR W_ENABLE_CLUSTER_FAILOVER[] = { 'E', 'N', 'A', 'B', 'L', 'E', '_', 'C', 'L', 'U', 'S', 'T', 'E', 'R', '_', 'F', 'A', 'I', 'L', 'O', 'V', 'E', 'R', 0 }; static SQLWCHAR W_ALLOW_READER_CONNECTIONS[] = { 'A', 'L', 'L', 'O', 'W', '_', 'R', 'E', 'A', 'D', 'E', 'R', '_', 'C', 'O', 'N', 'N', 'E', 'C', 'T', 'I', 'O', 'N', 'S', 0 }; +static SQLWCHAR W_ENABLE_STRICT_READER_FAILOVER[] = { 'E', 'N', 'A', 'B', 'L', 'E', '_', 'S', 'T', 'R' , 'I' , 'C', 'T', '_', 'R', 'E', 'A', 'D', 'E', 'R', '_', 'F', 'A', 'I', 'L', 'O', 'V', 'E', 'R', 0 }; static SQLWCHAR W_GATHER_PERF_METRICS[] = { 'G', 'A', 'T', 'H', 'E', 'R', '_', 'P', 'E', 'R', 'F', '_', 'M', 'E', 'T', 'R', 'I', 'C', 'S', 0 }; static SQLWCHAR W_GATHER_PERF_METRICS_PER_INSTANCE[] = { 'G', 'A', 'T', 'H', 'E', 'R', '_', 'P', 'E', 'R', 'F', '_', 'M', 'E', 'T', 'R', 'I', 'C', 'S', '_','P','E','R','_','I','N','S','T','A','N', 'C', 'E', 0 }; static SQLWCHAR W_HOST_PATTERN[] = { 'H', 'O', 'S', 'T', '_', 'P', 'A', 'T', 'T', 'E', 'R', 'N', 0 }; @@ -308,7 +309,8 @@ SQLWCHAR *dsnparams[]= {W_DSN, W_DRIVER, W_DESCRIPTION, W_SERVER, /* AWS Auth */ W_AUTH_MODE, W_AUTH_REGION, W_AUTH_HOST, W_AUTH_PORT, W_AUTH_EXPIRATION, W_AUTH_SECRET_ID, /* Failover */ - W_ENABLE_CLUSTER_FAILOVER, W_ALLOW_READER_CONNECTIONS, + W_ENABLE_CLUSTER_FAILOVER, W_ALLOW_READER_CONNECTIONS, + W_ENABLE_STRICT_READER_FAILOVER, W_GATHER_PERF_METRICS, W_GATHER_PERF_METRICS_PER_INSTANCE, W_HOST_PATTERN, W_CLUSTER_ID, W_TOPOLOGY_REFRESH_RATE, W_FAILOVER_TIMEOUT, W_FAILOVER_TOPOLOGY_REFRESH_RATE, @@ -760,6 +762,7 @@ DataSource *ds_new() ds->auth_expiration = 900; // 15 minutes ds->enable_cluster_failover = true; ds->allow_reader_connections = false; + ds->enable_strict_reader_failover = false; ds->gather_perf_metrics = false; ds->topology_refresh_rate = TOPOLOGY_REFRESH_RATE_MS; ds->failover_timeout = FAILOVER_TIMEOUT_MS; @@ -1173,7 +1176,9 @@ void ds_map_param(DataSource *ds, const SQLWCHAR *param, else if (!sqlwcharcasecmp(W_ENABLE_CLUSTER_FAILOVER, param)) *booldest = &ds->enable_cluster_failover; else if (!sqlwcharcasecmp(W_ALLOW_READER_CONNECTIONS, param)) - *booldest = &ds->allow_reader_connections; + *booldest = &ds->allow_reader_connections; + else if (!sqlwcharcasecmp(W_ENABLE_STRICT_READER_FAILOVER, param)) + *booldest = &ds->enable_strict_reader_failover; else if (!sqlwcharcasecmp(W_GATHER_PERF_METRICS, param)) *booldest = &ds->gather_perf_metrics; else if (!sqlwcharcasecmp(W_GATHER_PERF_METRICS_PER_INSTANCE, param)) @@ -1761,6 +1766,7 @@ int ds_add(DataSource *ds) /* Failover */ if (ds_add_intprop(ds->name, W_ENABLE_CLUSTER_FAILOVER, ds->enable_cluster_failover, true)) goto error; if (ds_add_intprop(ds->name, W_ALLOW_READER_CONNECTIONS, ds->allow_reader_connections)) goto error; + if (ds_add_intprop(ds->name, W_ENABLE_STRICT_READER_FAILOVER, ds->enable_strict_reader_failover)) goto error; if (ds_add_intprop(ds->name, W_GATHER_PERF_METRICS, ds->gather_perf_metrics)) goto error; if (ds_add_intprop(ds->name, W_GATHER_PERF_METRICS_PER_INSTANCE, ds->gather_metrics_per_instance)) goto error; if (ds_add_strprop(ds->name, W_HOST_PATTERN, ds->host_pattern)) goto error; @@ -2107,6 +2113,7 @@ void ds_copy(DataSource *ds, DataSource *ds_source) { ds->enable_cluster_failover = ds_source->enable_cluster_failover; ds->allow_reader_connections = ds_source->allow_reader_connections; + ds->enable_strict_reader_failover = ds_source->enable_strict_reader_failover; ds->gather_perf_metrics = ds_source->gather_perf_metrics; ds->gather_metrics_per_instance = ds_source->gather_metrics_per_instance; ds->topology_refresh_rate = ds_source->topology_refresh_rate; diff --git a/util/installer.h b/util/installer.h index aafe1114a..3414e4ca3 100644 --- a/util/installer.h +++ b/util/installer.h @@ -232,6 +232,7 @@ typedef struct DataSource { /* Failover */ BOOL enable_cluster_failover; BOOL allow_reader_connections; + BOOL enable_strict_reader_failover; BOOL gather_perf_metrics; BOOL gather_metrics_per_instance; SQLCHAR *host_pattern8;