aws_advanced_ruby_driver_wrapper 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +175 -0
  4. data/NOTICE +1 -0
  5. data/README.md +168 -0
  6. data/THIRD-PARTY-LICENSES +473 -0
  7. data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
  8. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
  9. data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
  10. data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
  11. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
  12. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
  13. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
  14. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
  15. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
  16. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
  17. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
  18. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
  19. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
  20. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
  21. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
  22. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
  23. data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
  24. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
  25. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
  26. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
  27. data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
  28. data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
  29. data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
  30. data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
  31. data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
  32. data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
  33. data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
  34. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
  35. data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
  36. data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
  37. data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
  38. data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
  39. data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
  40. data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
  41. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
  42. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
  43. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
  44. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
  45. data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
  46. data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
  47. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
  48. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
  49. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
  50. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
  51. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
  52. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
  53. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
  54. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
  55. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
  56. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
  57. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
  58. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
  59. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
  60. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
  61. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
  62. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
  63. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
  64. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
  65. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
  66. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
  67. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
  68. data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
  69. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
  70. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
  71. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
  72. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
  73. data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
  74. data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
  75. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
  76. data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
  77. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
  78. data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
  79. data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
  80. data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
  81. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
  82. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
  83. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
  84. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
  85. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
  86. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
  87. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
  88. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
  89. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
  90. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
  91. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
  92. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
  93. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
  94. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
  95. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
  96. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
  97. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
  98. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
  99. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
  100. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
  101. data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
  102. data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
  103. data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
  104. data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
  105. data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
  106. data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
  107. data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
  108. data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
  109. data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
  110. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
  111. data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
  112. data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
  113. data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
  114. data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
  115. data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
  116. data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
  117. data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
  118. data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
  119. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
  120. data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
  121. data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
  122. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
  123. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
  124. data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
  125. data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
  126. data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
  127. data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
  128. data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
  129. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
  130. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
  131. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
  132. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
  133. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
  134. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
  135. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
  136. data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
  137. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
  138. data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
  139. data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
  140. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
  141. data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
  142. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
  143. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
  144. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
  145. data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
  146. data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
  147. data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
  148. data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
  149. data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
  150. metadata +227 -0
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'error_handler'
18
+
19
+ module AwsAdvancedRubyDriverWrapper
20
+ module Errors
21
+ class MysqlErrorHandler
22
+ include ErrorHandler
23
+
24
+ ACCESS_ERROR_SQL_STATE = '28000'
25
+ READ_ONLY_ERROR_CODES = Set[1290, 1836].freeze
26
+ NETWORK_ERROR_CODES = Set[
27
+ 2002, # CR_CONNECTION_ERROR (can't connect via socket)
28
+ 2003, # CR_CONN_HOST_ERROR (can't connect to host)
29
+ 2006, # CR_SERVER_GONE_ERROR (server has gone away)
30
+ 2013, # CR_SERVER_LOST (lost connection during query)
31
+ 2026, # CR_SSL_CONNECTION_ERROR
32
+ 2055 # CR_SERVER_LOST_EXTENDED (lost connection with state info)
33
+ ].freeze
34
+
35
+ def initialize(driver_dialect)
36
+ @driver_dialect = driver_dialect
37
+ end
38
+
39
+ def network_error?(error)
40
+ return true if super
41
+
42
+ check_cause_chain(error) { |_, current| connection_error_network?(current) }
43
+ end
44
+
45
+ def network_error_by_sql_state?(sql_state)
46
+ return false if sql_state.nil?
47
+
48
+ sql_state.start_with?('08') && sql_state != '08004'
49
+ end
50
+
51
+ def login_error_by_sql_state?(sql_state)
52
+ return false if sql_state.nil?
53
+
54
+ sql_state == ACCESS_ERROR_SQL_STATE
55
+ end
56
+
57
+ def read_only_error?(error)
58
+ check_cause_chain(error) do |sql_state, ex|
59
+ error_code = ex.respond_to?(:error_number) ? ex.error_number : nil
60
+ read_only_error_by_sql_state?(sql_state, error_code)
61
+ end
62
+ end
63
+
64
+ def read_only_error_by_sql_state?(sql_state, error_code = nil)
65
+ sql_state == 'HY000' && !error_code.nil? && READ_ONLY_ERROR_CODES.include?(error_code)
66
+ end
67
+
68
+ private
69
+
70
+ def connection_error_network?(error)
71
+ return false unless defined?(Mysql2::Error::ConnectionError) && error.is_a?(Mysql2::Error::ConnectionError)
72
+
73
+ error_number = error.respond_to?(:error_number) ? error.error_number : nil
74
+ return true if error_number && NETWORK_ERROR_CODES.include?(error_number)
75
+
76
+ false
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'error_handler'
18
+
19
+ module AwsAdvancedRubyDriverWrapper
20
+ module Errors
21
+ class PgErrorHandler
22
+ include ErrorHandler
23
+
24
+ NETWORK_SQL_STATE_PREFIXES = %w[57P01 57P02 57P03 58 08 99 F0].freeze
25
+ ACCESS_ERROR_SQL_STATES = Set['08004', '28P01', '28000'].freeze
26
+ READ_ONLY_SQL_STATE = '25006'
27
+ LOGIN_ERROR_MESSAGE = 'authentication failed'
28
+ NETWORK_ERROR_MESSAGES = [
29
+ 'unexpected eof',
30
+ 'closed the connection unexpectedly',
31
+ 'reset by peer',
32
+ 'could not receive data',
33
+ 'could not send data',
34
+ 'connection not open',
35
+ 'no connection to the server',
36
+ 'connection is closed',
37
+ 'broken pipe',
38
+ 'terminating connection due to administrator command',
39
+ "can't get socket descriptor",
40
+ 'ssl connection has been closed unexpectedly'
41
+ ].freeze
42
+
43
+ # Failures raised while establishing a connection, where the server was never reached and so
44
+ # never had the chance to accept or reject the login. These are transient and worth retrying.
45
+ # libpq discards the structured error fields for connect-time failures, so the message text
46
+ # is the only signal available.
47
+ CONNECT_FAILURE_MESSAGES = [
48
+ 'connection refused',
49
+ 'timeout expired',
50
+ 'could not translate host name',
51
+ 'no route to host',
52
+ 'network is unreachable',
53
+ 'host is unreachable',
54
+ 'could not connect to server',
55
+ 'no such file or directory', # unix socket path does not exist
56
+ 'connection timed out'
57
+ ].freeze
58
+
59
+ # Rejections issued by a server that was successfully reached. Retrying to connect is futile because
60
+ # the cause is the credentials, the requested database, or server configuration rather than the network path.
61
+ SERVER_REJECTION_MESSAGES = [
62
+ LOGIN_ERROR_MESSAGE,
63
+ 'pg_hba.conf',
64
+ 'does not exist',
65
+ 'is not permitted',
66
+ 'too many clients'
67
+ ].freeze
68
+
69
+ def initialize(driver_dialect)
70
+ @driver_dialect = driver_dialect
71
+ end
72
+
73
+ def network_error?(error)
74
+ return true if super
75
+
76
+ check_cause_chain(error) { |_, current| connection_bad_network_error?(current) }
77
+ end
78
+
79
+ def network_error_by_sql_state?(sql_state)
80
+ return false if sql_state.nil?
81
+
82
+ NETWORK_SQL_STATE_PREFIXES.any? { |prefix| sql_state.start_with?(prefix) }
83
+ end
84
+
85
+ def login_error?(error)
86
+ return true if super
87
+
88
+ # PG::ConnectionBad from auth failure during connect may not carry a SQLSTATE.
89
+ # Fall back to message inspection.
90
+ if defined?(PG::ConnectionBad) && error.is_a?(PG::ConnectionBad)
91
+ msg = error.message
92
+ return msg.include?(LOGIN_ERROR_MESSAGE) if msg
93
+ end
94
+
95
+ false
96
+ end
97
+
98
+ def login_error_by_sql_state?(sql_state)
99
+ return false if sql_state.nil?
100
+
101
+ ACCESS_ERROR_SQL_STATES.include?(sql_state)
102
+ end
103
+
104
+ def read_only_error_by_sql_state?(sql_state, _error_code = nil)
105
+ sql_state == READ_ONLY_SQL_STATE
106
+ end
107
+
108
+ private
109
+
110
+ def connection_bad_network_error?(error)
111
+ return false unless defined?(PG::ConnectionBad) && error.is_a?(PG::ConnectionBad)
112
+
113
+ msg = error.message&.downcase
114
+ return false if msg.nil?
115
+ return false if server_rejection?(msg)
116
+
117
+ NETWORK_ERROR_MESSAGES.any? { |pattern| msg.include?(pattern) } ||
118
+ CONNECT_FAILURE_MESSAGES.any? { |pattern| msg.include?(pattern) }
119
+ end
120
+
121
+ def server_rejection?(msg)
122
+ SERVER_REJECTION_MESSAGES.any? { |pattern| msg.include?(pattern) }
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module AwsAdvancedRubyDriverWrapper
18
+ module Errors
19
+ class AwsError < StandardError
20
+ attr_reader :connection_broken, :needs_reconfiguration
21
+
22
+ def initialize(message, connection_broken: false, needs_reconfiguration: false)
23
+ super(message)
24
+ @needs_reconfiguration = needs_reconfiguration
25
+ @connection_broken = connection_broken
26
+ end
27
+ end
28
+
29
+ class FailoverFailedError < AwsError
30
+ def initialize(message)
31
+ super(message, connection_broken: true)
32
+ end
33
+ end
34
+
35
+ class FailoverSuccessError < AwsError
36
+ def initialize
37
+ super(
38
+ 'The active SQL connection has changed due to a connection failure. Please re-configure session state if required.',
39
+ needs_reconfiguration: true)
40
+ end
41
+ end
42
+
43
+ class TransactionStateUnknownError < AwsError
44
+ def initialize
45
+ super(
46
+ 'Transaction resolution unknown. Please re-configure session state if required and retry the transaction.',
47
+ connection_broken: true,
48
+ needs_reconfiguration: true
49
+ )
50
+ end
51
+ end
52
+
53
+ class IamAuthError < AwsError; end
54
+ class SecretsManagerAuthError < AwsError; end
55
+ class PluginConflictError < AwsError; end
56
+ class BlueGreenTimeoutError < AwsError; end
57
+ class BlueGreenSwitchoverError < AwsError; end
58
+ end
59
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative '../errors'
18
+ require_relative 'host_info'
19
+ require_relative 'host_role'
20
+
21
+ module AwsAdvancedRubyDriverWrapper
22
+ module Host
23
+ # A static host list provider that parses the connection string to determine host information.
24
+ # The host list is determined once during initialization and does not change over time.
25
+ class ConnectionStringHostListProvider
26
+ # @param service_container [Services::ServiceContainer]
27
+ def initialize(service_container:)
28
+ @service_container = service_container
29
+ @hosts = []
30
+ @initialized = false
31
+ end
32
+
33
+ # Returns the static host list parsed from the connection string.
34
+ # @return [Array<HostInfo>]
35
+ def refresh
36
+ initialize_hosts
37
+ @hosts.map(&:deep_dup)
38
+ end
39
+
40
+ # Same as {#refresh} — the host list is static and never changes.
41
+ # @param _verify_writer [Boolean] unused; accepted for compatibility with other host list providers.
42
+ # @param _timeout_ms [Integer] unused; accepted for compatibility with other host list providers.
43
+ # @return [Array<HostInfo>]
44
+ def force_refresh(_verify_writer = false, _timeout_ms = 0)
45
+ initialize_hosts
46
+ @hosts.map(&:deep_dup)
47
+ end
48
+
49
+ # Returns the cluster ID. Since this is a static provider with no cluster awareness,
50
+ # returns a placeholder value.
51
+ # @return [String]
52
+ def cluster_id
53
+ '<none>'
54
+ end
55
+
56
+ # No-op — there is no monitor to stop for this host list provider.
57
+ def stop_monitor; end
58
+
59
+ private
60
+
61
+ def initialize_hosts
62
+ return if @initialized
63
+
64
+ connection_service = @service_container.connection_service
65
+ config = connection_service.config
66
+ @hosts = if config.multi_host_url?
67
+ build_multi_host_list(config.original_host, config.original_port)
68
+ else
69
+ [connection_service.initial_host_info]
70
+ end
71
+ @initialized = true
72
+ end
73
+
74
+ def build_multi_host_list(original_host, original_port)
75
+ hosts = original_host.split(',').map(&:strip)
76
+ port_str = original_port.to_s
77
+
78
+ # If a single port is specified (no commas), it applies to all hosts.
79
+ # If commas are present, each port maps positionally to each host (empty entries become NO_PORT).
80
+ single_port = !port_str.include?(',')
81
+ ports = port_str.split(',', -1)
82
+
83
+ hosts.each_with_index.map do |host, i|
84
+ port_value = if single_port
85
+ port_str.strip.empty? ? HostInfo::NO_PORT : port_str.strip
86
+ else
87
+ port = ports[i]&.strip
88
+ port.nil? || port.empty? ? HostInfo::NO_PORT : port
89
+ end
90
+ HostInfo.new(host:, port: port_value, role: HostRole::UNKNOWN)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'rds_host_list_provider'
18
+ require_relative '../monitoring/global_cluster_topology_monitor'
19
+ require_relative '../property_definition'
20
+
21
+ module AwsAdvancedRubyDriverWrapper
22
+ module Host
23
+ # Host list provider for Aurora Global Database clusters.
24
+ # Extends RdsHostListProvider with multi-region instance templates
25
+ # and uses GlobalClusterTopologyMonitor.
26
+ class GlobalAuroraHostListProvider < RdsHostListProvider
27
+ attr_reader :instance_templates_by_region
28
+
29
+ def initialize(service_container:, topology_utils:)
30
+ super
31
+ @instance_templates_by_region = build_instance_templates_by_region
32
+ end
33
+
34
+ def force_refresh(verify_writer, timeout_sec)
35
+ monitor = @service_container.monitor_service.run_if_absent(:cluster_topology, @cluster_id, @service_container) do |_sc|
36
+ Monitoring::GlobalClusterTopologyMonitor.new(
37
+ service_container: @service_container,
38
+ cluster_id: @cluster_id,
39
+ instance_template: @instance_template,
40
+ instance_templates_by_region: @instance_templates_by_region,
41
+ topology_utils: @topology_utils,
42
+ monitoring_driver_props: @monitoring_driver_props,
43
+ monitoring_wrapper_props: @monitoring_wrapper_props
44
+ )
45
+ end
46
+ monitor.force_refresh(verify_writer, timeout_sec)
47
+ rescue Timeout::Error
48
+ nil
49
+ end
50
+
51
+ private
52
+
53
+ def build_instance_templates_by_region
54
+ props = @service_container.connection_service.wrapper_props
55
+ patterns_str = PropertyDefinition::GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS.get(props)
56
+ unless patterns_str
57
+ raise Errors::AwsError,
58
+ "#{PropertyDefinition::GLOBAL_CLUSTER_INSTANCE_HOST_PATTERNS.name} is required for Global Aurora Databases"
59
+ end
60
+
61
+ @topology_utils.parse_instance_templates(patterns_str, method(:validate_host_pattern!))
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module AwsAdvancedRubyDriverWrapper
18
+ module Host
19
+ module HostAvailability
20
+ AVAILABLE = :available
21
+ UNAVAILABLE = :unavailable
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module AwsAdvancedRubyDriverWrapper
18
+ module Host
19
+ class HostAvailabilityStrategy
20
+ def host_availability(raw_host_availability) = raw_host_availability
21
+
22
+ def host_availability=(_host_availability)
23
+ # Intentionally do nothing.
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'host_availability'
18
+ require_relative 'host_availability_strategy'
19
+ require_relative 'host_role'
20
+
21
+ module AwsAdvancedRubyDriverWrapper
22
+ module Host
23
+ class HostInfo
24
+ NO_PORT = '-1'
25
+ NO_HOST = ''
26
+ DEFAULT_WEIGHT = 100
27
+
28
+ # The attributes that define this host's identity, and that {#==} and {#hash} are derived from. They are
29
+ # read-only so that the hash code of an instance cannot change while it is held in a Hash or Set, which
30
+ # would otherwise make the instance impossible to look up. Use {#deep_dup} to obtain a copy with
31
+ # different values.
32
+ attr_reader :host, :port, :role
33
+
34
+ attr_accessor :availability_strategy, :weight, :id, :last_update_time
35
+
36
+ def initialize(
37
+ host: NO_HOST,
38
+ port: NO_PORT,
39
+ role: HostRole::WRITER,
40
+ availability: HostAvailability::AVAILABLE,
41
+ availability_strategy: HostAvailabilityStrategy.new,
42
+ weight: DEFAULT_WEIGHT,
43
+ id: nil,
44
+ last_update_time: nil
45
+ )
46
+ @host = host
47
+ @port = port
48
+ @role = role
49
+ @availability = availability
50
+ @availability_strategy = availability_strategy
51
+ @weight = weight
52
+ @id = id
53
+ @last_update_time = last_update_time
54
+ end
55
+
56
+ def ==(other)
57
+ return true if equal?(other)
58
+ return false unless other.is_a?(HostInfo)
59
+
60
+ host == other.host &&
61
+ port == other.port &&
62
+ role == other.role
63
+ end
64
+
65
+ alias eql? ==
66
+
67
+ def hash
68
+ [host, port, role].hash
69
+ end
70
+
71
+ def to_s
72
+ "HostInfo(#{host}, #{port}, #{role}, #{availability})"
73
+ end
74
+
75
+ def inspect
76
+ to_s
77
+ end
78
+
79
+ # Returns a deep copy of this HostInfo, duplicating all mutable fields. Since {#host}, {#port} and {#role}
80
+ # are read-only, this is also the way to obtain an instance that differs from this one in any of them.
81
+ #
82
+ # @param host [String] the host of the copy, defaulting to this host's.
83
+ # @param port [String] the port of the copy, defaulting to this host's.
84
+ # @param role [Symbol] the role of the copy, defaulting to this host's.
85
+ # @return [HostInfo]
86
+ def deep_dup(host: self.host, port: self.port, role: self.role)
87
+ HostInfo.new(
88
+ host: host.dup,
89
+ port: port.dup,
90
+ role:,
91
+ availability:,
92
+ weight:,
93
+ id: id&.dup,
94
+ last_update_time:
95
+ )
96
+ end
97
+
98
+ def url
99
+ "#{host_and_port}/"
100
+ end
101
+
102
+ def host_and_port
103
+ port_specified? ? "#{host}:#{port}" : host
104
+ end
105
+
106
+ def port_specified?
107
+ port != NO_PORT
108
+ end
109
+
110
+ def host_specified?
111
+ !host.nil? && host != NO_HOST
112
+ end
113
+
114
+ # Returns the availability of this host.
115
+ #
116
+ # @return [Symbol] either {HostAvailability::AVAILABLE} or {HostAvailability::UNAVAILABLE}
117
+ def availability
118
+ return availability_strategy.host_availability(@availability) if availability_strategy
119
+
120
+ @availability
121
+ end
122
+
123
+ def availability=(availability)
124
+ @availability = availability
125
+ availability_strategy&.host_availability = availability
126
+ end
127
+
128
+ # Returns the raw availability of this host.
129
+ #
130
+ # @return [Symbol] the raw availability of this host, ignoring the
131
+ # {#availability_strategy} if it has one.
132
+ def raw_availability
133
+ @availability
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module AwsAdvancedRubyDriverWrapper
18
+ module Host
19
+ module HostRole
20
+ UNKNOWN = :unknown
21
+ READER = :reader
22
+ WRITER = :writer
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License").
6
+ # You may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module AwsAdvancedRubyDriverWrapper
18
+ module Host
19
+ class RandomHostSelector
20
+ STRATEGY_NAME = 'random'
21
+
22
+ # Selects a random available host matching the requested role from the given host list.
23
+ #
24
+ # @param hosts [Array<HostInfo>] list of available hosts to pick from.
25
+ # @param role [Symbol, nil] the desired host role, or nil for no preference.
26
+ # @param _props [Hash, nil] connection properties.
27
+ # @return [HostInfo, nil] an available host matching the requested role, or nil if none are eligible.
28
+ def select_host(hosts, role, _props = nil)
29
+ eligible_hosts = hosts.select do |host|
30
+ (role.nil? || host.role == role) &&
31
+ host.availability == HostAvailability::AVAILABLE
32
+ end
33
+
34
+ return nil if eligible_hosts.empty?
35
+
36
+ eligible_hosts.sample
37
+ end
38
+ end
39
+ end
40
+ end