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,205 @@
|
|
|
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 'concurrent'
|
|
18
|
+
require_relative '../../logging'
|
|
19
|
+
require_relative '../../ruby_method'
|
|
20
|
+
require_relative '../../property_definition'
|
|
21
|
+
require_relative '../../utils/rds_utils'
|
|
22
|
+
require_relative 'phase'
|
|
23
|
+
require_relative 'role'
|
|
24
|
+
require_relative 'status'
|
|
25
|
+
require_relative 'status_provider'
|
|
26
|
+
|
|
27
|
+
module AwsAdvancedRubyDriverWrapper
|
|
28
|
+
module Plugins
|
|
29
|
+
module BlueGreen
|
|
30
|
+
class BlueGreenPlugin
|
|
31
|
+
include Logging
|
|
32
|
+
|
|
33
|
+
BLUE_GREEN_NAME = :blue_green
|
|
34
|
+
|
|
35
|
+
# When present in wrapper_props, internal_connect calls bypass BG routing.
|
|
36
|
+
# Used by BG monitoring connections to avoid being intercepted by their own plugin.
|
|
37
|
+
BG_SKIP_ROUTING_KEY = :'bg.skip_routing'
|
|
38
|
+
|
|
39
|
+
CLOSING_METHODS = Set[
|
|
40
|
+
RubyMethod::CONNECTION_CLOSE.name,
|
|
41
|
+
RubyMethod::STATEMENT_CLOSE.name
|
|
42
|
+
].freeze
|
|
43
|
+
|
|
44
|
+
PROVIDERS = Concurrent::Map.new
|
|
45
|
+
|
|
46
|
+
def initialize(service_container, props = ::Concurrent::Map.new)
|
|
47
|
+
@service_container = service_container
|
|
48
|
+
@wrapper_props = props
|
|
49
|
+
@bgd_id = PropertyDefinition::BGD_ID.get(props).to_s.strip.downcase
|
|
50
|
+
@bg_status = nil
|
|
51
|
+
@cluster_id = nil
|
|
52
|
+
@start_time_ns = Concurrent::AtomicReference.new(0)
|
|
53
|
+
@end_time_ns = Concurrent::AtomicReference.new(0)
|
|
54
|
+
|
|
55
|
+
service_container.storage_service.register(BLUE_GREEN_NAME, ttl: 3600)
|
|
56
|
+
|
|
57
|
+
network_methods = service_container.dialect_service.driver_dialect.network_bound_methods
|
|
58
|
+
@subscribed_methods = (Set['connect', 'internal_connect'] | network_methods).freeze
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
attr_reader :subscribed_methods
|
|
62
|
+
|
|
63
|
+
def connect(host_info, driver_props, is_initial_connection, pipeline_callable)
|
|
64
|
+
route_connect(host_info, driver_props, is_initial_connection, pipeline_callable, is_internal: false)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def internal_connect(host_info, driver_props, wrapper_props, is_initial_connection, pipeline_callable)
|
|
68
|
+
# BG monitoring connections set BG_SKIP_ROUTING_KEY to bypass routing.
|
|
69
|
+
return pipeline_callable.call if wrapper_props[BG_SKIP_ROUTING_KEY]
|
|
70
|
+
|
|
71
|
+
route_connect(host_info, driver_props, is_initial_connection, pipeline_callable, is_internal: true)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def execute(method_name, pipeline_callable, *_, **, &)
|
|
75
|
+
reset_routing_time_ns
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
init_provider
|
|
79
|
+
|
|
80
|
+
return pipeline_callable.call if CLOSING_METHODS.include?(method_name)
|
|
81
|
+
|
|
82
|
+
@bg_status = storage_service.get(BLUE_GREEN_NAME, @bgd_id)
|
|
83
|
+
if @bg_status.nil? || @bg_status.current_phase == Phase::NOT_CREATED || @bg_status.current_phase == Phase::COMPLETED
|
|
84
|
+
return pipeline_callable.call
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
current_host = @service_container.connection_service.current_host_info
|
|
88
|
+
host_role = @bg_status.role(current_host)
|
|
89
|
+
return pipeline_callable.call if host_role.nil?
|
|
90
|
+
|
|
91
|
+
routing = @bg_status.execute_routing.find { |r| r.match?(current_host, host_role) }
|
|
92
|
+
return pipeline_callable.call if routing.nil?
|
|
93
|
+
|
|
94
|
+
@start_time_ns.set(nano_time)
|
|
95
|
+
result = nil
|
|
96
|
+
|
|
97
|
+
while routing && result.nil?
|
|
98
|
+
result = routing.apply(method_name, @wrapper_props, storage_service)
|
|
99
|
+
|
|
100
|
+
next unless result.nil?
|
|
101
|
+
|
|
102
|
+
@bg_status = storage_service.get(BLUE_GREEN_NAME, @bgd_id)
|
|
103
|
+
if @bg_status.nil?
|
|
104
|
+
@end_time_ns.set(nano_time)
|
|
105
|
+
return pipeline_callable.call
|
|
106
|
+
end
|
|
107
|
+
routing = @bg_status.execute_routing.find { |r| r.match?(current_host, host_role) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
@end_time_ns.set(nano_time)
|
|
111
|
+
result.nil? ? pipeline_callable.call : result
|
|
112
|
+
ensure
|
|
113
|
+
@end_time_ns.set(nano_time) if @start_time_ns.get.positive? && @end_time_ns.get.zero?
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def hold_time_ns
|
|
118
|
+
return 0 if @start_time_ns.get.zero?
|
|
119
|
+
|
|
120
|
+
end_ns = @end_time_ns.get
|
|
121
|
+
end_ns.zero? ? (nano_time - @start_time_ns.get) : (end_ns - @start_time_ns.get)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def reset_routing_time_ns
|
|
125
|
+
@start_time_ns.set(0)
|
|
126
|
+
@end_time_ns.set(0)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.clean_up_providers
|
|
130
|
+
PROVIDERS.each_key { |k| PROVIDERS.delete(k)&.stop }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def route_connect(host_info, driver_props, is_initial_connection, pipeline_callable, is_internal: false)
|
|
136
|
+
reset_routing_time_ns
|
|
137
|
+
|
|
138
|
+
begin
|
|
139
|
+
@bg_status = storage_service.get(BLUE_GREEN_NAME, @bgd_id)
|
|
140
|
+
return regular_connect(pipeline_callable, is_initial_connection) if @bg_status.nil?
|
|
141
|
+
|
|
142
|
+
host_role = @bg_status.role(host_info)
|
|
143
|
+
return regular_connect(pipeline_callable, is_initial_connection) if host_role.nil?
|
|
144
|
+
|
|
145
|
+
routing = @bg_status.connect_routing.find { |r| r.match?(host_info, host_role) }
|
|
146
|
+
return regular_connect(pipeline_callable, is_initial_connection) if routing.nil?
|
|
147
|
+
|
|
148
|
+
@start_time_ns.set(nano_time)
|
|
149
|
+
conn = nil
|
|
150
|
+
|
|
151
|
+
while routing && conn.nil?
|
|
152
|
+
# Pass self so a routing that opens a substitute connection can skip this plugin in the
|
|
153
|
+
# nested pipeline, preventing the connect path from re-entering BG routing (infinite recursion).
|
|
154
|
+
conn = routing.apply(host_info, driver_props, @wrapper_props, is_initial_connection, @service_container,
|
|
155
|
+
is_internal: is_internal, bg_plugin: self)
|
|
156
|
+
|
|
157
|
+
next unless conn.nil?
|
|
158
|
+
|
|
159
|
+
# Routing passed — re-read status in case it changed while we were waiting.
|
|
160
|
+
@bg_status = storage_service.get(BLUE_GREEN_NAME, @bgd_id)
|
|
161
|
+
if @bg_status.nil?
|
|
162
|
+
@end_time_ns.set(nano_time)
|
|
163
|
+
return regular_connect(pipeline_callable, is_initial_connection)
|
|
164
|
+
end
|
|
165
|
+
routing = @bg_status.connect_routing.find { |r| r.match?(host_info, host_role) }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
@end_time_ns.set(nano_time)
|
|
169
|
+
conn ||= pipeline_callable.call
|
|
170
|
+
|
|
171
|
+
init_provider if is_initial_connection
|
|
172
|
+
conn
|
|
173
|
+
ensure
|
|
174
|
+
@end_time_ns.set(nano_time) if @start_time_ns.get.positive? && @end_time_ns.get.zero?
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def regular_connect(pipeline_callable, is_initial_connection)
|
|
179
|
+
conn = pipeline_callable.call
|
|
180
|
+
init_provider if is_initial_connection
|
|
181
|
+
conn
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def init_provider
|
|
185
|
+
return if @cluster_id && PROVIDERS.key?(@bgd_id)
|
|
186
|
+
|
|
187
|
+
host_list_provider = @service_container.host_service.host_list_provider
|
|
188
|
+
@cluster_id = host_list_provider.respond_to?(:cluster_id) ? host_list_provider.cluster_id :
|
|
189
|
+
@service_container.connection_service.config.cluster_id
|
|
190
|
+
PROVIDERS.compute_if_absent(@bgd_id) do
|
|
191
|
+
StatusProvider.new(@service_container, @wrapper_props, @bgd_id, @cluster_id)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def storage_service
|
|
196
|
+
@service_container.storage_service
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def nano_time
|
|
200
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
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 'concurrent'
|
|
18
|
+
require_relative '../../host/host_info'
|
|
19
|
+
require_relative '../../host/host_role'
|
|
20
|
+
require_relative '../../utils/rds_utils'
|
|
21
|
+
require_relative 'role'
|
|
22
|
+
|
|
23
|
+
module AwsAdvancedRubyDriverWrapper
|
|
24
|
+
module Plugins
|
|
25
|
+
module BlueGreen
|
|
26
|
+
class HostMapper
|
|
27
|
+
attr_reader :corresponding_hosts, :role_by_host, :host_ip_addresses
|
|
28
|
+
|
|
29
|
+
def initialize
|
|
30
|
+
@corresponding_hosts = Concurrent::Hash.new
|
|
31
|
+
@role_by_host = Concurrent::Hash.new
|
|
32
|
+
@host_ip_addresses = Concurrent::Hash.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update(source_status, target_status)
|
|
36
|
+
@corresponding_hosts.clear
|
|
37
|
+
|
|
38
|
+
build_instance_pairs(source_status, target_status)
|
|
39
|
+
build_cluster_pairs(source_status, target_status)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def merge_ips(ip_map)
|
|
43
|
+
@host_ip_addresses.merge!(ip_map)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def register_role(host_names, role)
|
|
47
|
+
host_names.each { |x| @role_by_host[x.downcase] = role }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def clear
|
|
51
|
+
@corresponding_hosts.clear
|
|
52
|
+
@role_by_host.clear
|
|
53
|
+
@host_ip_addresses.clear
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_debug_s
|
|
57
|
+
@corresponding_hosts.map { |k, v| " #{k} -> #{v[1]&.host_and_port || '<null>'}" }.join("\n")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def build_instance_pairs(source_status, target_status)
|
|
63
|
+
return unless source_status&.start_topology&.any? && target_status&.start_topology&.any?
|
|
64
|
+
|
|
65
|
+
blue_writer = writer_host(source_status)
|
|
66
|
+
green_writer = writer_host(target_status)
|
|
67
|
+
blue_readers = reader_hosts(source_status)
|
|
68
|
+
green_readers = reader_hosts(target_status)
|
|
69
|
+
|
|
70
|
+
@corresponding_hosts[blue_writer.host] = [blue_writer, green_writer] if blue_writer
|
|
71
|
+
|
|
72
|
+
return unless blue_readers&.any?
|
|
73
|
+
|
|
74
|
+
if green_readers&.any?
|
|
75
|
+
blue_readers.each_with_index do |blue, i|
|
|
76
|
+
@corresponding_hosts[blue.host] = [blue, green_readers[i % green_readers.size]]
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
blue_readers.each { |blue| @corresponding_hosts[blue.host] = [blue, green_writer] }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def build_cluster_pairs(source_status, target_status)
|
|
84
|
+
return unless source_status&.host_names&.any? && target_status&.host_names&.any?
|
|
85
|
+
|
|
86
|
+
blue_hosts = source_status.host_names
|
|
87
|
+
green_hosts = target_status.host_names
|
|
88
|
+
|
|
89
|
+
pair_by_dns_type(blue_hosts, green_hosts, :writer_cluster_dns?)
|
|
90
|
+
pair_by_dns_type(blue_hosts, green_hosts, :reader_cluster_dns?)
|
|
91
|
+
pair_custom_clusters(blue_hosts, green_hosts)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def pair_by_dns_type(blue_hosts, green_hosts, dns_method)
|
|
95
|
+
blue = blue_hosts.find { |h| Utils::RdsUtils.public_send(dns_method, h) }
|
|
96
|
+
green = green_hosts.find { |h| Utils::RdsUtils.public_send(dns_method, h) }
|
|
97
|
+
return unless blue && green
|
|
98
|
+
|
|
99
|
+
@corresponding_hosts[blue] ||= [Host::HostInfo.new(host: blue), Host::HostInfo.new(host: green)]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def pair_custom_clusters(blue_hosts, green_hosts)
|
|
103
|
+
blue_hosts.select { |h| Utils::RdsUtils.rds_custom_cluster_dns?(h) }.each do |blue_host|
|
|
104
|
+
custom_name = Utils::RdsUtils.rds_cluster_id(blue_host)
|
|
105
|
+
next unless custom_name
|
|
106
|
+
|
|
107
|
+
green_host = green_hosts.find do |x|
|
|
108
|
+
Utils::RdsUtils.rds_custom_cluster_dns?(x) &&
|
|
109
|
+
custom_name == Utils::RdsUtils.remove_green_instance_prefix(Utils::RdsUtils.rds_cluster_id(x))
|
|
110
|
+
end
|
|
111
|
+
next unless green_host
|
|
112
|
+
|
|
113
|
+
@corresponding_hosts[blue_host] ||= [
|
|
114
|
+
Host::HostInfo.new(host: blue_host),
|
|
115
|
+
Host::HostInfo.new(host: green_host)
|
|
116
|
+
]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def writer_host(interim_status)
|
|
121
|
+
interim_status.start_topology&.find { |x| x.role == Host::HostRole::WRITER }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def reader_hosts(interim_status)
|
|
125
|
+
interim_status.start_topology
|
|
126
|
+
&.reject { |x| x.role == Host::HostRole::WRITER }
|
|
127
|
+
&.sort_by(&:host)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
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 'concurrent'
|
|
18
|
+
require_relative '../../logging'
|
|
19
|
+
|
|
20
|
+
module AwsAdvancedRubyDriverWrapper
|
|
21
|
+
module Plugins
|
|
22
|
+
module BlueGreen
|
|
23
|
+
# Tracks which green host have successfully connected using a non-prefixed (blue) IAM hostname,
|
|
24
|
+
# indicating the host has completed its DNS rename.
|
|
25
|
+
class IamHostTracker
|
|
26
|
+
include Logging
|
|
27
|
+
|
|
28
|
+
attr_reader :green_host_change_name_times
|
|
29
|
+
|
|
30
|
+
def initialize(on_all_changed:)
|
|
31
|
+
@successful_connects = Concurrent::Hash.new
|
|
32
|
+
@green_host_change_name_times = Concurrent::Hash.new
|
|
33
|
+
@all_changed = Concurrent::AtomicBoolean.new(false)
|
|
34
|
+
@on_all_changed = on_all_changed
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def register(connect_host, iam_host)
|
|
38
|
+
names_differ = connect_host && connect_host != iam_host
|
|
39
|
+
|
|
40
|
+
if names_differ && !connected?(connect_host, iam_host)
|
|
41
|
+
@green_host_change_name_times[connect_host] ||= Time.now
|
|
42
|
+
logger.debug { "Green host '#{connect_host}' has changed its name to '#{iam_host}'." }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
(@successful_connects[connect_host] ||= Set.new).add(iam_host)
|
|
46
|
+
|
|
47
|
+
return unless names_differ
|
|
48
|
+
|
|
49
|
+
all_changed = @successful_connects
|
|
50
|
+
.reject { |_, v| v.empty? }
|
|
51
|
+
.all? { |k, v| v.any? { |y| k != y } }
|
|
52
|
+
|
|
53
|
+
return unless all_changed && !@all_changed.true?
|
|
54
|
+
|
|
55
|
+
logger.debug { 'All green hosts changed' }
|
|
56
|
+
@all_changed.make_true
|
|
57
|
+
@on_all_changed&.call
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def connected?(connect_host, iam_host)
|
|
61
|
+
@successful_connects[connect_host]&.include?(iam_host) || false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def all_changed?
|
|
65
|
+
@all_changed.true?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def size
|
|
69
|
+
@successful_connects.size
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def clear
|
|
73
|
+
@green_host_change_name_times.clear
|
|
74
|
+
@all_changed.make_false
|
|
75
|
+
@successful_connects.clear
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_debug_s
|
|
79
|
+
@green_host_change_name_times.map { |k, v| " #{k} -> #{v}" }.join("\n")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
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 Plugins
|
|
19
|
+
module BlueGreen
|
|
20
|
+
InterimStatus = Struct.new(
|
|
21
|
+
:blue_green_phase,
|
|
22
|
+
:version,
|
|
23
|
+
:port,
|
|
24
|
+
:start_topology,
|
|
25
|
+
:current_topology,
|
|
26
|
+
:start_ip_addresses_by_host,
|
|
27
|
+
:current_ip_addresses_by_host,
|
|
28
|
+
:host_names,
|
|
29
|
+
:all_start_topology_ip_changed,
|
|
30
|
+
:all_start_topology_endpoints_removed,
|
|
31
|
+
:all_topology_changed
|
|
32
|
+
) do
|
|
33
|
+
def to_s
|
|
34
|
+
start_ip_map = format_map(start_ip_addresses_by_host)
|
|
35
|
+
current_ip_map = format_map(current_ip_addresses_by_host)
|
|
36
|
+
host_names_str = host_names&.join("\n ")
|
|
37
|
+
start_topo_str = log_topology(start_topology)
|
|
38
|
+
current_topo_str = log_topology(current_topology)
|
|
39
|
+
|
|
40
|
+
<<~STATUS
|
|
41
|
+
InterimStatus [
|
|
42
|
+
phase #{blue_green_phase || '<null>'},
|
|
43
|
+
version '#{version}',
|
|
44
|
+
port #{port},
|
|
45
|
+
host_names:
|
|
46
|
+
#{blank?(host_names_str) ? '-' : host_names_str}
|
|
47
|
+
Start #{blank?(start_topo_str) ? '-' : start_topo_str}
|
|
48
|
+
Start IP map:
|
|
49
|
+
#{blank?(start_ip_map) ? '-' : start_ip_map}
|
|
50
|
+
Current #{blank?(current_topo_str) ? '-' : current_topo_str}
|
|
51
|
+
Current IP map:
|
|
52
|
+
#{blank?(current_ip_map) ? '-' : current_ip_map}
|
|
53
|
+
all_start_topology_ip_changed: #{all_start_topology_ip_changed}
|
|
54
|
+
all_start_topology_endpoints_removed: #{all_start_topology_endpoints_removed}
|
|
55
|
+
all_topology_changed: #{all_topology_changed}
|
|
56
|
+
]
|
|
57
|
+
STATUS
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def hash
|
|
61
|
+
[blue_green_phase, version, port, all_start_topology_ip_changed,
|
|
62
|
+
all_start_topology_endpoints_removed, all_topology_changed,
|
|
63
|
+
host_names&.sort, topology_hash_str(start_topology),
|
|
64
|
+
topology_hash_str(current_topology), ip_map_hash_str(start_ip_addresses_by_host),
|
|
65
|
+
ip_map_hash_str(current_ip_addresses_by_host)].hash
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def format_map(map)
|
|
71
|
+
map&.map { |k, v| "#{k} -> #{v}" }&.join("\n ").to_s
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def log_topology(topology)
|
|
75
|
+
topology&.map { |h| "#{h.host_and_port} #{h.role}" }&.join(', ').to_s
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def topology_hash_str(topology)
|
|
79
|
+
topology&.map { |h| "#{h.host_and_port}#{h.role}" }&.sort&.join(',').to_s
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def ip_map_hash_str(map)
|
|
83
|
+
map&.map { |k, v| "#{k}#{v}" }&.sort&.join(',').to_s
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def blank?(str)
|
|
87
|
+
str.nil? || str.empty?
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
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 Plugins
|
|
19
|
+
module BlueGreen
|
|
20
|
+
module IntervalRate
|
|
21
|
+
BASELINE = :baseline
|
|
22
|
+
INCREASED = :increased
|
|
23
|
+
HIGH = :high
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
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 Plugins
|
|
19
|
+
module BlueGreen
|
|
20
|
+
class Phase
|
|
21
|
+
include Comparable
|
|
22
|
+
|
|
23
|
+
attr_reader :value, :name
|
|
24
|
+
|
|
25
|
+
def initialize(value, active_switchover_or_completed, name)
|
|
26
|
+
@value = value
|
|
27
|
+
@active_switchover_or_completed = active_switchover_or_completed
|
|
28
|
+
@name = name
|
|
29
|
+
end
|
|
30
|
+
private_class_method :new
|
|
31
|
+
|
|
32
|
+
NOT_CREATED = new(0, false, 'NOT_CREATED')
|
|
33
|
+
CREATED = new(1, false, 'CREATED')
|
|
34
|
+
PREPARATION = new(2, true, 'PREPARATION')
|
|
35
|
+
IN_PROGRESS = new(3, true, 'IN_PROGRESS')
|
|
36
|
+
POST = new(4, true, 'POST')
|
|
37
|
+
COMPLETED = new(5, true, 'COMPLETED')
|
|
38
|
+
|
|
39
|
+
STATUS_MAPPING = {
|
|
40
|
+
'AVAILABLE' => CREATED,
|
|
41
|
+
'SWITCHOVER_INITIATED' => PREPARATION,
|
|
42
|
+
'SWITCHOVER_IN_PROGRESS' => IN_PROGRESS,
|
|
43
|
+
'SWITCHOVER_IN_POST_PROCESSING' => POST,
|
|
44
|
+
'SWITCHOVER_COMPLETED' => COMPLETED
|
|
45
|
+
}.freeze
|
|
46
|
+
|
|
47
|
+
def self.parse_phase(value)
|
|
48
|
+
return NOT_CREATED if value.nil? || value.empty?
|
|
49
|
+
|
|
50
|
+
STATUS_MAPPING.fetch(value.upcase) do
|
|
51
|
+
raise ArgumentError, "Unknown Blue/Green status: #{value}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def active_switchover_or_completed?
|
|
56
|
+
@active_switchover_or_completed
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_s
|
|
60
|
+
@name
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def <=>(other)
|
|
64
|
+
value <=> other.value
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
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 'concurrent'
|
|
18
|
+
require_relative 'phase'
|
|
19
|
+
require_relative 'phase_time_info'
|
|
20
|
+
|
|
21
|
+
module AwsAdvancedRubyDriverWrapper
|
|
22
|
+
module Plugins
|
|
23
|
+
module BlueGreen
|
|
24
|
+
# Ordered log of timestamped events during a switchover (phase transitions, DNS changes,
|
|
25
|
+
# monitor resets). Used to produce the final switchover summary printed at completion.
|
|
26
|
+
class PhaseEventLog
|
|
27
|
+
def initialize
|
|
28
|
+
@entries = Concurrent::Hash.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Records an event with the current wall-clock and monotonic timestamps.
|
|
32
|
+
# Only the first occurrence of each label is kept (idempotent).
|
|
33
|
+
def record(label, rollback_suffix, phase: nil)
|
|
34
|
+
@entries["#{label}#{rollback_suffix}"] ||= PhaseTimeInfo.new(Time.now, nano_time, phase)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def clear
|
|
38
|
+
@entries.clear
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def any?(&)
|
|
42
|
+
@entries.any?(&)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Formats a human-readable switchover summary table with timestamps and ms offsets
|
|
46
|
+
# relative to the IN_PROGRESS (or PREPARATION for rollback) phase start time.
|
|
47
|
+
def summary(bgd_id, rollback)
|
|
48
|
+
left_pad = 5
|
|
49
|
+
default_field = 31
|
|
50
|
+
max_name_len = @entries.keys.map { |k| k.length + left_pad }.max || default_field
|
|
51
|
+
|
|
52
|
+
time_zero_phase = rollback ? Phase::PREPARATION : Phase::IN_PROGRESS
|
|
53
|
+
suffix = rollback ? ' (rollback)' : ''
|
|
54
|
+
time_zero = @entries["#{time_zero_phase}#{suffix}"] || (rollback && @entries[time_zero_phase.to_s])
|
|
55
|
+
|
|
56
|
+
divider = "---------------------------------------------------#{'-' * max_name_len}\n"
|
|
57
|
+
status_label = rollback ? 'ROLLED BACK' : 'COMPLETED'
|
|
58
|
+
|
|
59
|
+
header = "#{'timestamp'.ljust(28)} #{'time offset (ms)'.rjust(21)} #{'event'.rjust(max_name_len)}"
|
|
60
|
+
|
|
61
|
+
rows = @entries
|
|
62
|
+
.sort_by { |_, v| v.timestamp_nano }
|
|
63
|
+
.map do |k, v|
|
|
64
|
+
offset = time_zero ? ((v.timestamp_nano - time_zero.timestamp_nano) / 1_000_000) : ''
|
|
65
|
+
"#{v.timestamp.to_s.rjust(28)} #{offset.to_s.rjust(18)} ms #{k.rjust(max_name_len)}"
|
|
66
|
+
end
|
|
67
|
+
.join("\n")
|
|
68
|
+
|
|
69
|
+
"[bgd_id: '#{bgd_id}'] Blue/Green Deployment Switchover #{status_label}\n" \
|
|
70
|
+
"#{divider}#{header}\n#{divider}#{rows}\n#{divider}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_debug_s
|
|
74
|
+
@entries.map { |k, v| " #{k} -> #{v.timestamp}" }.join("\n")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def nano_time
|
|
80
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|