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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +175 -0
- data/NOTICE +1 -0
- data/README.md +168 -0
- data/THIRD-PARTY-LICENSES +473 -0
- data/aws_advanced_ruby_driver_wrapper.gemspec +73 -0
- data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_mysql2_adapter.rb +73 -0
- data/lib/aws_advanced_ruby_driver_wrapper/active_record/aws_postgresql_adapter.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/custom_configuration.rb +58 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_mysql_dialect.rb +103 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/aurora_pg_dialect.rb +124 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/dialect_codes.rb +38 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_mysql_dialect.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/global_pg_dialect.rb +92 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_mysql_dialect.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/multi_az_cluster_pg_dialect.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/mysql_dialect.rb +98 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/pg_dialect.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_mysql_dialect.rb +88 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/rds_pg_dialect.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/unknown_dialect.rb +72 -0
- data/lib/aws_advanced_ruby_driver_wrapper/db_dialects/utils/dialect_utils.rb +71 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect.rb +154 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/driver_dialect_manager.rb +55 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/mysql_driver_dialect.rb +165 -0
- data/lib/aws_advanced_ruby_driver_wrapper/driver_dialects/pg_driver_dialect.rb +201 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/error_handler.rb +62 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/mysql_error_handler.rb +80 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors/pg_error_handler.rb +126 -0
- data/lib/aws_advanced_ruby_driver_wrapper/errors.rb +59 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/connection_string_host_list_provider.rb +95 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/global_aurora_host_list_provider.rb +65 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability.rb +24 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_availability_strategy.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_info.rb +137 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/host_role.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/random_host_selector.rb +40 -0
- data/lib/aws_advanced_ruby_driver_wrapper/host/rds_host_list_provider.rb +206 -0
- data/lib/aws_advanced_ruby_driver_wrapper/logging.rb +110 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/cluster_topology_monitor.rb +709 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/global_cluster_topology_monitor.rb +72 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_connection.rb +57 -0
- data/lib/aws_advanced_ruby_driver_wrapper/monitoring/monitor_state.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/mysql.rb +429 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/blue_green_plugin.rb +205 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/host_mapper.rb +132 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/iam_host_tracker.rb +84 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interim_status.rb +92 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/interval_rate.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase.rb +69 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_event_log.rb +85 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/phase_time_info.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/role.rb +38 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/base_routing.rb +83 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/reject_connect_routing.rb +40 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/substitute_connect_routing.rb +136 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_connect_routing.rb +53 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_execute_routing.rb +52 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/routing/suspend_until_corresponding_host_found_connect_routing.rb +83 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status.rb +68 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_builder.rb +244 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_info.rb +30 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_monitor.rb +564 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/status_provider.rb +414 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_state.rb +98 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/blue_green/switchover_timer.rb +46 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_monitor.rb +266 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/custom_endpoint_plugin.rb +158 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/info.rb +111 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/member_list_type.rb +31 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/custom_endpoint/role.rb +45 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/default_plugin.rb +108 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_mode.rb +43 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/failover_plugin.rb +467 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_mode.rb +68 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/gdb/gdb_failover_plugin.rb +403 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/iam_auth_plugin.rb +159 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/initial_connection_strategy_plugin.rb +485 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/audit_logger.rb +157 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_cipher.rb +159 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/column_encryption_config.rb +61 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/connection_source.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/data_key_cache.rb +220 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_algorithm.rb +75 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_config.rb +146 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/encryption_service.rb +391 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/error_context.rb +198 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/errors.rb +259 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_management_utility.rb +435 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_manager.rb +378 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/key_metadata.rb +86 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_plugin.rb +890 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/kms_encryption_utility.rb +281 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/metadata_manager.rb +332 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sanitizer.rb +147 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_name.rb +70 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/schema_validator.rb +211 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/sql_runner.rb +147 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/kms_encryption/type_marker.rb +109 -0
- data/lib/aws_advanced_ruby_driver_wrapper/plugins/secrets_manager_plugin.rb +358 -0
- data/lib/aws_advanced_ruby_driver_wrapper/postgresql.rb +659 -0
- data/lib/aws_advanced_ruby_driver_wrapper/property_definition.rb +409 -0
- data/lib/aws_advanced_ruby_driver_wrapper/ruby_method.rb +122 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/connection_service.rb +143 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/dialect_service.rb +267 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/host_service.rb +199 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/monitor_service.rb +186 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_call_context.rb +63 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/plugin_manager.rb +273 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/service_container.rb +30 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/service_utility.rb +78 -0
- data/lib/aws_advanced_ruby_driver_wrapper/services/session_state_service.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/accessible_regions.rb +52 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/ar_constants.rb +25 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/aurora_topology_utils.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/aws_credentials_utils.rb +62 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config.rb +91 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/connection_config_parser.rb +368 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/conversion_utils.rb +51 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/batching_event_publisher.rb +119 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/data_access_event.rb +26 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/events/monitor_reset_event.rb +26 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/global_aurora_topology_utils.rb +185 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/host_list_utils.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/iam_auth_utils.rb +112 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/multi_az_topology_utils.rb +117 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/encryption_annotation_parser.rb +99 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/mysql_statement_analyzer.rb +641 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/pg_statement_analyzer.rb +502 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_analysis.rb +63 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/query_type.rb +35 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint.rb +27 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/routing_hint_parser.rb +50 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/parser/sql_parser.rb +139 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_url_type.rb +71 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/rds_utils.rb +575 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/retry_util.rb +153 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_encoding.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/sql_method_analyzer.rb +195 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/cache_entry.rb +56 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/expiration_cache.rb +108 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/sliding_expiration_cache.rb +137 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/storage/storage_service.rb +172 -0
- data/lib/aws_advanced_ruby_driver_wrapper/utils/topology_utils.rb +127 -0
- data/lib/aws_advanced_ruby_driver_wrapper/version.rb +19 -0
- data/lib/aws_advanced_ruby_driver_wrapper/wrapper_property.rb +64 -0
- data/lib/aws_advanced_ruby_driver_wrapper.rb +116 -0
- metadata +227 -0
|
@@ -0,0 +1,63 @@
|
|
|
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 Services
|
|
19
|
+
# What a plugin can learn about, and change about, the call it is currently handling.
|
|
20
|
+
#
|
|
21
|
+
# A plugin's +execute+ already receives the arguments and the block of the call, but only as
|
|
22
|
+
# copies: the pipeline reads what it hands to the next plugin from here, so a plugin that needs
|
|
23
|
+
# to change them has to replace {#args} or {#block} rather than modify what it was given.
|
|
24
|
+
# Likewise {#sql} is not always among the arguments, since a result method has no SQL of its
|
|
25
|
+
# own and a prepared statement only carries the name it was prepared under.
|
|
26
|
+
#
|
|
27
|
+
# This context belongs to a single call on a single thread, and is reached through
|
|
28
|
+
# {PluginManager#current_call_context}.
|
|
29
|
+
class PluginCallContext
|
|
30
|
+
# @return [String, nil] the SQL the call originated from, nil when the caller could not say
|
|
31
|
+
attr_reader :sql
|
|
32
|
+
|
|
33
|
+
# @return [Array] the arguments the target driver method will be called with
|
|
34
|
+
attr_accessor :args
|
|
35
|
+
|
|
36
|
+
# @return [Proc, nil] the block the target driver method will be called with
|
|
37
|
+
attr_accessor :block
|
|
38
|
+
|
|
39
|
+
# @param sql [String, nil]
|
|
40
|
+
# @param args [Array]
|
|
41
|
+
# @param block [Proc, nil]
|
|
42
|
+
# @param field_names [Array<String>, Proc, nil] the result's column names in order, or a
|
|
43
|
+
# callable that produces them; see {#field_names}
|
|
44
|
+
def initialize(sql, args, block = nil, field_names = nil)
|
|
45
|
+
@sql = sql
|
|
46
|
+
@args = args
|
|
47
|
+
@block = block
|
|
48
|
+
@field_names = field_names
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The result's column names, in order, for a plugin that reads rows as bare arrays of values
|
|
52
|
+
# and has to match each position back to a column. Only result methods supply it, and only
|
|
53
|
+
# when a plugin might need it, so it is resolved lazily and remembered: a call that never asks
|
|
54
|
+
# pays nothing, and one that asks more than once pays once.
|
|
55
|
+
#
|
|
56
|
+
# @return [Array<String>, nil] nil when the call carries no column names
|
|
57
|
+
def field_names
|
|
58
|
+
@field_names = @field_names.call if @field_names.respond_to?(:call)
|
|
59
|
+
@field_names
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,273 @@
|
|
|
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 'logger'
|
|
18
|
+
require_relative '../errors'
|
|
19
|
+
require_relative '../property_definition'
|
|
20
|
+
require_relative '../ruby_method'
|
|
21
|
+
require_relative '../utils/sql_encoding'
|
|
22
|
+
require_relative '../plugins/default_plugin'
|
|
23
|
+
require_relative '../plugins/failover_plugin'
|
|
24
|
+
require_relative '../plugins/gdb/gdb_failover_plugin'
|
|
25
|
+
require_relative '../plugins/iam_auth_plugin'
|
|
26
|
+
require_relative '../plugins/initial_connection_strategy_plugin'
|
|
27
|
+
require_relative '../plugins/kms_encryption/kms_encryption_plugin'
|
|
28
|
+
require_relative '../plugins/secrets_manager_plugin'
|
|
29
|
+
require_relative '../plugins/blue_green/blue_green_plugin'
|
|
30
|
+
require_relative '../plugins/custom_endpoint/custom_endpoint_plugin'
|
|
31
|
+
require_relative 'plugin_call_context'
|
|
32
|
+
|
|
33
|
+
module AwsAdvancedRubyDriverWrapper
|
|
34
|
+
module Services
|
|
35
|
+
class PluginManager
|
|
36
|
+
WEIGHT_RELATIVE_TO_PRIOR_PLUGIN = -1
|
|
37
|
+
NOOP_CALLABLE = -> {}.freeze
|
|
38
|
+
CURRENT_CALL_CONTEXT_KEY = :aws_ruby_wrapper_plugin_call_context
|
|
39
|
+
private_constant :WEIGHT_RELATIVE_TO_PRIOR_PLUGIN, :NOOP_CALLABLE, :CURRENT_CALL_CONTEXT_KEY
|
|
40
|
+
|
|
41
|
+
@plugin_classes = {
|
|
42
|
+
'bg' => Plugins::BlueGreen::BlueGreenPlugin,
|
|
43
|
+
'custom_endpoint' => Plugins::CustomEndpoint::CustomEndpointPlugin,
|
|
44
|
+
'failover' => Plugins::FailoverPlugin,
|
|
45
|
+
'gdb_failover' => Plugins::Gdb::GdbFailoverPlugin,
|
|
46
|
+
'iam' => Plugins::IamAuthPlugin,
|
|
47
|
+
'initial_connection' => Plugins::InitialConnectionStrategyPlugin,
|
|
48
|
+
'kms_encryption' => Plugins::KmsEncryptionPlugin,
|
|
49
|
+
'secrets_manager' => Plugins::SecretsManagerPlugin
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# The final list of plugins will be sorted by weight, starting from the lowest values up to
|
|
53
|
+
# the highest values. The first plugin of the list will have the lowest weight, and the
|
|
54
|
+
# last one will have the highest weight.
|
|
55
|
+
@plugin_weights = {
|
|
56
|
+
Plugins::BlueGreen::BlueGreenPlugin => 200,
|
|
57
|
+
Plugins::CustomEndpoint::CustomEndpointPlugin => 250,
|
|
58
|
+
Plugins::InitialConnectionStrategyPlugin => 300,
|
|
59
|
+
Plugins::FailoverPlugin => 400,
|
|
60
|
+
Plugins::Gdb::GdbFailoverPlugin => 500,
|
|
61
|
+
Plugins::IamAuthPlugin => 1800,
|
|
62
|
+
Plugins::SecretsManagerPlugin => 1900,
|
|
63
|
+
Plugins::KmsEncryptionPlugin => 2050
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class << self
|
|
67
|
+
def register_plugin(plugin_code, plugin_class, weight: WEIGHT_RELATIVE_TO_PRIOR_PLUGIN)
|
|
68
|
+
@plugin_classes[plugin_code] = plugin_class
|
|
69
|
+
@plugin_weights[plugin_class] = weight
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
attr_reader :plugin_classes, :plugin_weights
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def initialize(service_container)
|
|
76
|
+
@plugins = load_plugins(service_container)
|
|
77
|
+
@pipeline_cache = {}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def connect(host_info, driver_props, is_initial_connection, plugin_to_skip: nil)
|
|
81
|
+
execute_with_subscribed_plugins(
|
|
82
|
+
'connect',
|
|
83
|
+
lambda do |plugin, next_plugin_callable|
|
|
84
|
+
plugin.connect(host_info, driver_props, is_initial_connection, next_plugin_callable)
|
|
85
|
+
end,
|
|
86
|
+
NOOP_CALLABLE,
|
|
87
|
+
plugin_to_skip:
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def internal_connect(host_info, driver_props, wrapper_props, is_initial_connection, plugin_to_skip: nil)
|
|
92
|
+
execute_with_subscribed_plugins(
|
|
93
|
+
'internal_connect',
|
|
94
|
+
lambda do |plugin, next_plugin_callable|
|
|
95
|
+
plugin.internal_connect(host_info, driver_props, wrapper_props, is_initial_connection, next_plugin_callable)
|
|
96
|
+
end,
|
|
97
|
+
NOOP_CALLABLE,
|
|
98
|
+
plugin_to_skip:
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# The context of the call currently being executed, for plugins that need to know more about
|
|
103
|
+
# it than their own arguments say, or that need to change the arguments the target driver
|
|
104
|
+
# method is called with. See {PluginCallContext}.
|
|
105
|
+
#
|
|
106
|
+
# The context belongs to the calling thread and is restored when the call returns, so nested
|
|
107
|
+
# calls cannot see one another's.
|
|
108
|
+
#
|
|
109
|
+
# @return [PluginCallContext, nil] nil outside of a call
|
|
110
|
+
def current_call_context
|
|
111
|
+
Thread.current[CURRENT_CALL_CONTEXT_KEY]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @return [String, nil] the SQL the call currently being executed originated from
|
|
115
|
+
def current_sql
|
|
116
|
+
current_call_context&.sql
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @param sql [String, nil] the SQL the call originated from, for plugins that inspect
|
|
120
|
+
# statements; it is published as valid UTF-8 (see {Utils::SqlEncoding.inspectable}), and is
|
|
121
|
+
# consumed here and never forwarded to the target driver method. Since invalid bytes in the
|
|
122
|
+
# published copy are replaced, a plugin that rewrites the SQL builds the new statement from
|
|
123
|
+
# the call's arguments rather than from this copy
|
|
124
|
+
# @param field_names [Array<String>, Proc, nil] the result's column names in order, for a
|
|
125
|
+
# plugin that reads rows as arrays; like +sql+, it is consumed here rather than forwarded
|
|
126
|
+
def execute(ruby_method, current_conn, target_callable, *args, bounded_conn: nil, sql: nil, field_names: nil, **kwargs, &block)
|
|
127
|
+
if ruby_method.is_a?(MethodInfo)
|
|
128
|
+
method_name = ruby_method.name
|
|
129
|
+
|
|
130
|
+
if ruby_method.check_bounded_connection && !bounded_conn.nil? && !current_conn.nil? && (bounded_conn != current_conn)
|
|
131
|
+
raise Errors::AwsError, "Method invoked against old connection: #{bounded_conn}"
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
# Fallback for dynamic method names (method_missing with string)
|
|
135
|
+
method_name = ruby_method.to_s
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context = PluginCallContext.new(Utils::SqlEncoding.inspectable(sql), args, block, field_names)
|
|
139
|
+
previous_context = Thread.current[CURRENT_CALL_CONTEXT_KEY]
|
|
140
|
+
Thread.current[CURRENT_CALL_CONTEXT_KEY] = context
|
|
141
|
+
|
|
142
|
+
begin
|
|
143
|
+
execute_with_subscribed_plugins(
|
|
144
|
+
method_name,
|
|
145
|
+
lambda do |plugin, next_plugin_callable|
|
|
146
|
+
# Read from the context rather than from args and block, so that a plugin which
|
|
147
|
+
# replaced either is honoured by the plugins after it and by the target method.
|
|
148
|
+
plugin.execute(method_name, next_plugin_callable, *context.args, **kwargs, &context.block)
|
|
149
|
+
end,
|
|
150
|
+
target_callable
|
|
151
|
+
)
|
|
152
|
+
ensure
|
|
153
|
+
Thread.current[CURRENT_CALL_CONTEXT_KEY] = previous_context
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def num_plugins
|
|
158
|
+
@plugins&.length || 0
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def plugin_in_use?(plugin_class)
|
|
162
|
+
return false if @plugins.nil? || @plugins.empty?
|
|
163
|
+
|
|
164
|
+
@plugins.any?(plugin_class)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
def load_plugins(service_container)
|
|
170
|
+
wrapper_props = service_container.connection_service.wrapper_props
|
|
171
|
+
plugin_codes = PropertyDefinition::PLUGINS.get(wrapper_props)
|
|
172
|
+
codes_list = plugin_codes.split(',').map(&:strip)
|
|
173
|
+
ensure_single_auth_plugin(codes_list)
|
|
174
|
+
ensure_single_failover_plugin(codes_list)
|
|
175
|
+
raise Errors::AwsError, 'Duplicate plugins detected' if codes_list.length != codes_list.uniq.length
|
|
176
|
+
|
|
177
|
+
plugin_classes = plugin_codes.empty? ? [] : get_plugin_classes(codes_list, wrapper_props)
|
|
178
|
+
|
|
179
|
+
plugins = plugin_classes.map do |plugin_class|
|
|
180
|
+
plugin_class.new(service_container, wrapper_props)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
plugins << Plugins::DefaultPlugin.new(service_container, wrapper_props)
|
|
184
|
+
plugins
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def ensure_single_auth_plugin(plugin_code_list)
|
|
188
|
+
auth_plugins_used = plugin_code_list & %w[iam secrets_manager].freeze
|
|
189
|
+
|
|
190
|
+
return unless auth_plugins_used.length > 1
|
|
191
|
+
|
|
192
|
+
raise Errors::PluginConflictError,
|
|
193
|
+
"Only one authentication plugin may be used at a time. Found: #{auth_plugins_used.join(', ')}"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def ensure_single_failover_plugin(plugin_code_list)
|
|
197
|
+
failover_plugins_used = plugin_code_list & %w[failover gdb_failover].freeze
|
|
198
|
+
|
|
199
|
+
return unless failover_plugins_used.length > 1
|
|
200
|
+
|
|
201
|
+
raise Errors::PluginConflictError,
|
|
202
|
+
"Only one failover plugin may be used at a time. Found: #{failover_plugins_used.join(', ')}"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def get_plugin_classes(plugin_code_list, _wrapper_props)
|
|
206
|
+
plugin_classes = plugin_code_list.map do |plugin_code|
|
|
207
|
+
plugin_class = self.class.plugin_classes[plugin_code]
|
|
208
|
+
raise Errors::AwsError, "Invalid plugin: #{plugin_code}" if plugin_class.nil?
|
|
209
|
+
|
|
210
|
+
plugin_class
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
return [] if plugin_classes.empty?
|
|
214
|
+
|
|
215
|
+
weights = plugin_weights_for(plugin_classes)
|
|
216
|
+
plugin_classes.sort_by! { |ft| weights[ft] }
|
|
217
|
+
|
|
218
|
+
plugin_classes
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def plugin_weights_for(plugin_classes)
|
|
222
|
+
last_weight = 0
|
|
223
|
+
plugin_classes.each_with_object({}) do |plugin_class, weights|
|
|
224
|
+
weight = self.class.plugin_weights[plugin_class]
|
|
225
|
+
|
|
226
|
+
if weight.nil? || weight == WEIGHT_RELATIVE_TO_PRIOR_PLUGIN
|
|
227
|
+
last_weight += 1
|
|
228
|
+
weights[plugin_class] = last_weight
|
|
229
|
+
else
|
|
230
|
+
weights[plugin_class] = weight
|
|
231
|
+
last_weight = weight
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def execute_with_subscribed_plugins(
|
|
237
|
+
target_method_name,
|
|
238
|
+
plugin_callable,
|
|
239
|
+
target_driver_callable,
|
|
240
|
+
plugin_to_skip: nil
|
|
241
|
+
)
|
|
242
|
+
pipeline_callable = @pipeline_cache[target_method_name] ||= make_pipeline(target_method_name)
|
|
243
|
+
pipeline_callable.call(plugin_callable, target_driver_callable, target_method_name, plugin_to_skip)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Builds the plugin pipeline function chain. The pipeline allows plugins to perform logic both before and after
|
|
247
|
+
# the target driver function is called.
|
|
248
|
+
def make_pipeline(target_method_name)
|
|
249
|
+
subscribed_plugins = @plugins.select do |p|
|
|
250
|
+
p.subscribed_methods.include?('*') || p.subscribed_methods.include?(target_method_name)
|
|
251
|
+
end
|
|
252
|
+
raise Errors::AwsError, 'Plugin pipeline is nil' if subscribed_plugins.empty?
|
|
253
|
+
|
|
254
|
+
base_plugin = subscribed_plugins.last
|
|
255
|
+
base = lambda do |plugin_callable, target_driver_callable, *, **|
|
|
256
|
+
plugin_callable.call(base_plugin, target_driver_callable)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
subscribed_plugins[0...-1].reverse.reduce(base) do |next_plugin_callable, plugin|
|
|
260
|
+
lambda do |plugin_callable, target_driver_callable, method_name, plugin_to_skip|
|
|
261
|
+
if plugin_to_skip == plugin
|
|
262
|
+
next_plugin_callable.call(plugin_callable, target_driver_callable, method_name, plugin_to_skip)
|
|
263
|
+
else
|
|
264
|
+
plugin_callable.call(plugin, lambda do
|
|
265
|
+
next_plugin_callable.call(plugin_callable, target_driver_callable, method_name, plugin_to_skip)
|
|
266
|
+
end)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
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 Services
|
|
19
|
+
ServiceContainer = Struct.new(
|
|
20
|
+
:connection_service,
|
|
21
|
+
:dialect_service,
|
|
22
|
+
:event_publisher,
|
|
23
|
+
:host_service,
|
|
24
|
+
:plugin_manager,
|
|
25
|
+
:session_state_service,
|
|
26
|
+
:storage_service,
|
|
27
|
+
:monitor_service
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
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 'service_container'
|
|
18
|
+
require_relative 'connection_service'
|
|
19
|
+
require_relative 'dialect_service'
|
|
20
|
+
require_relative 'host_service'
|
|
21
|
+
require_relative 'plugin_manager'
|
|
22
|
+
require_relative 'monitor_service'
|
|
23
|
+
require_relative 'session_state_service'
|
|
24
|
+
require_relative '../utils/storage/storage_service'
|
|
25
|
+
require_relative '../utils/events/batching_event_publisher'
|
|
26
|
+
|
|
27
|
+
module AwsAdvancedRubyDriverWrapper
|
|
28
|
+
module Services
|
|
29
|
+
# Manages shared singleton services with correct dependency order.
|
|
30
|
+
# Initialized eagerly at require-time to avoid thread-safety races.
|
|
31
|
+
module CoreServices
|
|
32
|
+
@event_publisher = Utils::Events::BatchingEventPublisher.new
|
|
33
|
+
@storage_service = Utils::Storage::StorageService.new(event_publisher: @event_publisher)
|
|
34
|
+
@monitor_service = MonitorService.new(event_publisher: @event_publisher)
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
attr_reader :event_publisher, :storage_service, :monitor_service
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Resets all shared instances. For testing only.
|
|
41
|
+
# @api private
|
|
42
|
+
def self.reset!
|
|
43
|
+
@monitor_service.shutdown(grace_period: 2)
|
|
44
|
+
@storage_service.shutdown
|
|
45
|
+
@event_publisher.release_resources
|
|
46
|
+
@event_publisher = Utils::Events::BatchingEventPublisher.new
|
|
47
|
+
@storage_service = Utils::Storage::StorageService.new(event_publisher: @event_publisher)
|
|
48
|
+
@monitor_service = MonitorService.new(event_publisher: @event_publisher)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module ServiceUtility
|
|
53
|
+
def self.create_standard_container(config)
|
|
54
|
+
container = ServiceContainer.new
|
|
55
|
+
container.event_publisher = CoreServices.event_publisher
|
|
56
|
+
container.connection_service = ConnectionService.new(container, config)
|
|
57
|
+
container.dialect_service = DialectService.new(container.connection_service, config.driver_name)
|
|
58
|
+
container.host_service = HostService.new(container)
|
|
59
|
+
container.session_state_service = SessionStateService.new
|
|
60
|
+
container.storage_service = CoreServices.storage_service
|
|
61
|
+
container.monitor_service = CoreServices.monitor_service
|
|
62
|
+
# PluginManager must be assigned last: plugins are instantiated during its constructor
|
|
63
|
+
# and must not eagerly access container.plugin_manager (it will be nil at that point).
|
|
64
|
+
container.plugin_manager = PluginManager.new(container)
|
|
65
|
+
container.dialect_service.setup_initial_provider(container)
|
|
66
|
+
container
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.create_monitor_container(parent_container)
|
|
70
|
+
container = ServiceContainer.new
|
|
71
|
+
container.event_publisher = parent_container.event_publisher
|
|
72
|
+
container.dialect_service = parent_container.dialect_service
|
|
73
|
+
container.host_service = parent_container.host_service
|
|
74
|
+
container
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
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 '../utils/sql_method_analyzer'
|
|
18
|
+
|
|
19
|
+
module AwsAdvancedRubyDriverWrapper
|
|
20
|
+
module Services
|
|
21
|
+
class SessionStateService
|
|
22
|
+
attr_accessor :in_transaction, :autocommit
|
|
23
|
+
alias in_transaction? in_transaction
|
|
24
|
+
alias autocommit? autocommit
|
|
25
|
+
|
|
26
|
+
def initialize
|
|
27
|
+
reset
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reset
|
|
31
|
+
@in_transaction = false
|
|
32
|
+
@autocommit = true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update_transaction_state(method_name, args, autocommit_before, dialect, connection, succeeded: true)
|
|
36
|
+
connection_in_transaction = dialect.reported_in_transaction(connection)
|
|
37
|
+
unless connection_in_transaction.nil?
|
|
38
|
+
self.in_transaction = connection_in_transaction
|
|
39
|
+
return
|
|
40
|
+
end
|
|
41
|
+
# When the statement failed, SQL inference can't tell what took effect, so keep the current state.
|
|
42
|
+
return unless succeeded
|
|
43
|
+
|
|
44
|
+
effect = Utils::SqlMethodAnalyzer.transaction_effect(
|
|
45
|
+
method_name, args, autocommit: autocommit?, autocommit_before: autocommit_before
|
|
46
|
+
)
|
|
47
|
+
if effect.opens_transaction
|
|
48
|
+
self.in_transaction = true
|
|
49
|
+
elsif effect.closes_transaction
|
|
50
|
+
self.in_transaction = false
|
|
51
|
+
end
|
|
52
|
+
self.autocommit = effect.autocommit_value unless effect.autocommit_value.nil?
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
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 '../property_definition'
|
|
19
|
+
require_relative 'rds_utils'
|
|
20
|
+
|
|
21
|
+
module AwsAdvancedRubyDriverWrapper
|
|
22
|
+
module Utils
|
|
23
|
+
module AccessibleRegions
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def parse(props)
|
|
27
|
+
value = PropertyDefinition::ACCESSIBLE_REGIONS.get(props)
|
|
28
|
+
return nil if value.nil? || value.strip.empty?
|
|
29
|
+
|
|
30
|
+
regions = value.split(',').map { |r| r.strip.downcase }.reject(&:empty?)
|
|
31
|
+
|
|
32
|
+
invalid = regions.reject { |r| RdsUtils.valid_region?(r) }
|
|
33
|
+
unless invalid.empty?
|
|
34
|
+
raise Errors::AwsError,
|
|
35
|
+
"#{PropertyDefinition::ACCESSIBLE_REGIONS.name} contains unknown or misspelled AWS " \
|
|
36
|
+
"region(s): #{invalid.join(', ')}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
regions.empty? ? nil : regions.to_set
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def filter_by_region(hosts, accessible_regions)
|
|
43
|
+
return hosts if accessible_regions.nil?
|
|
44
|
+
|
|
45
|
+
hosts.select do |host|
|
|
46
|
+
region = RdsUtils.rds_region(host.host)
|
|
47
|
+
region && accessible_regions.include?(region.downcase)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
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 Utils
|
|
19
|
+
# ActiveRecord-only keys that should not be passed to the wrapper or native driver.
|
|
20
|
+
AR_COMMON_KEYS = %i[
|
|
21
|
+
adapter pool checkout_timeout idle_timeout reaping_frequency
|
|
22
|
+
variables prepared_statements encoding reconnect socket flags
|
|
23
|
+
].freeze
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
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 'topology_utils'
|
|
18
|
+
require_relative 'conversion_utils'
|
|
19
|
+
|
|
20
|
+
module AwsAdvancedRubyDriverWrapper
|
|
21
|
+
module Utils
|
|
22
|
+
# Topology utilities specific to Aurora database clusters.
|
|
23
|
+
# Processes topology query results that return instance ID, writer flag, CPU utilization, and instance lag columns.
|
|
24
|
+
class AuroraTopologyUtils
|
|
25
|
+
include TopologyUtils
|
|
26
|
+
include ConversionUtils
|
|
27
|
+
|
|
28
|
+
def initialize(dialect:)
|
|
29
|
+
raise ArgumentError, 'dialect cannot be nil' if dialect.nil?
|
|
30
|
+
|
|
31
|
+
@dialect = dialect
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Evaluate whether the given connection is to a writer instance.
|
|
35
|
+
#
|
|
36
|
+
# @param conn [Object] the connection to evaluate.
|
|
37
|
+
# @return [Boolean] true if the connection is to a writer instance, false otherwise.
|
|
38
|
+
def writer_instance?(conn)
|
|
39
|
+
results = @dialect.execute(conn, @dialect.writer_id_query)
|
|
40
|
+
return false if results.nil? || results.none?
|
|
41
|
+
|
|
42
|
+
row = results.first
|
|
43
|
+
!row.nil? && !row.values.first.nil? && !row.values.first.to_s.empty?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Process Aurora topology query results into a list of HostInfo objects.
|
|
47
|
+
# Data in the result set is ordered by last update time, so the latest records are last.
|
|
48
|
+
# Newer records replace older ones if there are duplicate hosts.
|
|
49
|
+
#
|
|
50
|
+
# @param results [Object] the query result set (enumerable of row hashes).
|
|
51
|
+
# @param initial_host_info [AwsAdvancedRubyDriverWrapper::Host::HostInfo] the initial host info.
|
|
52
|
+
# @param instance_template [AwsAdvancedRubyDriverWrapper::Host::HostInfo] the template for building hosts.
|
|
53
|
+
# @return [Array<AwsAdvancedRubyDriverWrapper::Host::HostInfo>, nil] the parsed hosts or nil on failure.
|
|
54
|
+
def build_hosts(_conn, results, initial_host_info, instance_template)
|
|
55
|
+
hosts_map = {}
|
|
56
|
+
|
|
57
|
+
results.each do |row|
|
|
58
|
+
host = build_host_from_row(row, initial_host_info, instance_template)
|
|
59
|
+
|
|
60
|
+
# Ensure newer records replace the older ones if there are duplicate keys.
|
|
61
|
+
existing = hosts_map[host.host]
|
|
62
|
+
if existing.nil? || (existing.last_update_time && host.last_update_time &&
|
|
63
|
+
existing.last_update_time < host.last_update_time)
|
|
64
|
+
hosts_map[host.host] = host
|
|
65
|
+
end
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
logger.debug("Error processing topology query results: #{e.message}")
|
|
68
|
+
return nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
hosts_map.values
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Creates a HostInfo from a single Aurora topology query result row.
|
|
77
|
+
#
|
|
78
|
+
# According to the topology query, the result set should contain columns:
|
|
79
|
+
# host_id, is_writer, cpu_utilization, instance_lag, last_update_time.
|
|
80
|
+
#
|
|
81
|
+
# @param row [Hash] a single row from the topology query result.
|
|
82
|
+
# @param initial_host_info [AwsAdvancedRubyDriverWrapper::Host::HostInfo] the initial host info.
|
|
83
|
+
# @param instance_template [AwsAdvancedRubyDriverWrapper::Host::HostInfo] the template for building hosts.
|
|
84
|
+
# @return [AwsAdvancedRubyDriverWrapper::Host::HostInfo] the constructed host info.
|
|
85
|
+
def build_host_from_row(row, initial_host_info, instance_template)
|
|
86
|
+
instance_id = row_value(row, 'instance_id')
|
|
87
|
+
is_writer = to_boolean(row_value(row, 'is_writer'))
|
|
88
|
+
cpu_utilization = to_float(row_value(row, 'cpu_utilization'))
|
|
89
|
+
instance_lag = to_float(row_value(row, 'instance_lag'))
|
|
90
|
+
last_update_time = to_time(row_value(row, 'last_update_time'))
|
|
91
|
+
|
|
92
|
+
# Calculate weight based on instance lag and CPU utilization.
|
|
93
|
+
weight = (instance_lag.round * 100) + cpu_utilization.round
|
|
94
|
+
|
|
95
|
+
build_host(instance_id, instance_id, is_writer, weight, last_update_time, initial_host_info, instance_template)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|