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,267 @@
|
|
|
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 '../db_dialects/aurora_mysql_dialect'
|
|
18
|
+
require_relative '../db_dialects/aurora_pg_dialect'
|
|
19
|
+
require_relative '../db_dialects/dialect_codes'
|
|
20
|
+
require_relative '../db_dialects/global_mysql_dialect'
|
|
21
|
+
require_relative '../db_dialects/global_pg_dialect'
|
|
22
|
+
require_relative '../db_dialects/multi_az_cluster_mysql_dialect'
|
|
23
|
+
require_relative '../db_dialects/multi_az_cluster_pg_dialect'
|
|
24
|
+
require_relative '../db_dialects/mysql_dialect'
|
|
25
|
+
require_relative '../db_dialects/pg_dialect'
|
|
26
|
+
require_relative '../db_dialects/rds_mysql_dialect'
|
|
27
|
+
require_relative '../db_dialects/rds_pg_dialect'
|
|
28
|
+
require_relative '../db_dialects/unknown_dialect'
|
|
29
|
+
require_relative '../driver_dialects/driver_dialect_manager'
|
|
30
|
+
require_relative '../errors'
|
|
31
|
+
require_relative '../property_definition'
|
|
32
|
+
require_relative '../utils/rds_url_type'
|
|
33
|
+
require_relative '../utils/rds_utils'
|
|
34
|
+
require_relative '../utils/storage/expiration_cache'
|
|
35
|
+
|
|
36
|
+
module AwsAdvancedRubyDriverWrapper
|
|
37
|
+
module Services
|
|
38
|
+
class DialectService
|
|
39
|
+
attr_reader :driver_dialect, :db_dialect
|
|
40
|
+
|
|
41
|
+
ENDPOINT_CACHE_EXPIRATION = 86_400 # 24 hours in seconds
|
|
42
|
+
|
|
43
|
+
@known_endpoint_dialects = Utils::Storage::ExpirationCache.new(ttl: ENDPOINT_CACHE_EXPIRATION)
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
attr_reader :known_endpoint_dialects
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
KNOWN_DIALECT_CLASSES = {
|
|
50
|
+
DialectCodes::MYSQL => DbDialects::MysqlDialect,
|
|
51
|
+
DialectCodes::RDS_MYSQL => DbDialects::RdsMysqlDialect,
|
|
52
|
+
DialectCodes::AURORA_MYSQL => DbDialects::AuroraMysqlDialect,
|
|
53
|
+
DialectCodes::GLOBAL_AURORA_MYSQL => DbDialects::GlobalMysqlDialect,
|
|
54
|
+
DialectCodes::MULTI_AZ_MYSQL_CLUSTER => DbDialects::MultiAzClusterMysqlDialect,
|
|
55
|
+
DialectCodes::PG => DbDialects::PgDialect,
|
|
56
|
+
DialectCodes::RDS_PG => DbDialects::RdsPgDialect,
|
|
57
|
+
DialectCodes::AURORA_PG => DbDialects::AuroraPgDialect,
|
|
58
|
+
DialectCodes::GLOBAL_AURORA_PG => DbDialects::GlobalPgDialect,
|
|
59
|
+
DialectCodes::MULTI_AZ_PG_CLUSTER => DbDialects::MultiAzClusterPgDialect,
|
|
60
|
+
DialectCodes::UNKNOWN => DbDialects::UnknownDialect
|
|
61
|
+
}.freeze
|
|
62
|
+
|
|
63
|
+
# @param connection_service [ConnectionService]
|
|
64
|
+
# @param driver_name [Symbol] :mysql2 or :postgresql
|
|
65
|
+
def initialize(connection_service, driver_name)
|
|
66
|
+
@connection_service = connection_service
|
|
67
|
+
@dialect_cache = {}
|
|
68
|
+
@can_update = false
|
|
69
|
+
@driver_dialect = DriverDialects::DriverDialectManager.get_dialect(driver_name)
|
|
70
|
+
@error_handler = AwsAdvancedRubyDriverWrapper.config.custom_error_handler ||
|
|
71
|
+
DriverDialects::DriverDialectManager.get_error_handler(driver_name)
|
|
72
|
+
@db_dialect = init_dialect
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [Boolean] whether the dialect is known for certain and will not be swapped. This
|
|
76
|
+
# is the true when the dialect can no longer be updated, either because it was set
|
|
77
|
+
# explicitly / resolved from an unambiguous URL (such as a global writer cluster endpoint)
|
|
78
|
+
# or because it has already been confirmed via live connection queries. When true, topology
|
|
79
|
+
# fetches may proceed.
|
|
80
|
+
def dialect_final?
|
|
81
|
+
!@can_update
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Lazily instantiates and caches a dialect by code.
|
|
85
|
+
#
|
|
86
|
+
# @param code [String] the dialect code
|
|
87
|
+
# @return [Object, nil] the dialect instance or nil if unknown
|
|
88
|
+
def dialect_for_code(code)
|
|
89
|
+
@dialect_cache[code] ||= begin
|
|
90
|
+
known_class = KNOWN_DIALECT_CLASSES[code]
|
|
91
|
+
known_class&.new(@driver_dialect)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Refines the dialect after a connection is established by querying the server
|
|
96
|
+
# (e.g. checking for Aurora-specific functions/tables).
|
|
97
|
+
#
|
|
98
|
+
# @param connection [Object] the live database connection
|
|
99
|
+
# @return [Object] the updated database dialect
|
|
100
|
+
def update_dialect(connection)
|
|
101
|
+
original_dialect_code = @dialect_code
|
|
102
|
+
|
|
103
|
+
if @can_update
|
|
104
|
+
host = @connection_service.initial_host_info&.host
|
|
105
|
+
host_url = @connection_service.initial_host_info&.url
|
|
106
|
+
|
|
107
|
+
candidates = @db_dialect.dialect_update_candidates
|
|
108
|
+
candidates&.each do |candidate_code|
|
|
109
|
+
candidate = dialect_for_code(candidate_code)
|
|
110
|
+
raise Errors::AwsError, "Unknown dialect code: #{candidate_code}" unless candidate
|
|
111
|
+
|
|
112
|
+
next unless candidate.dialect?(connection)
|
|
113
|
+
|
|
114
|
+
@can_update = false
|
|
115
|
+
@dialect_code = candidate_code
|
|
116
|
+
@db_dialect = candidate
|
|
117
|
+
|
|
118
|
+
self.class.known_endpoint_dialects.put(host, candidate_code) if host
|
|
119
|
+
self.class.known_endpoint_dialects.put(host_url, candidate_code) if host_url
|
|
120
|
+
|
|
121
|
+
break
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if @can_update
|
|
125
|
+
# No candidate matched
|
|
126
|
+
raise Errors::AwsError, 'Unable to determine dialect' if @dialect_code == DialectCodes::UNKNOWN
|
|
127
|
+
|
|
128
|
+
@can_update = false
|
|
129
|
+
self.class.known_endpoint_dialects.put(host, @dialect_code) if host
|
|
130
|
+
self.class.known_endpoint_dialects.put(host_url, @dialect_code) if host_url
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
swap_host_list_provider if @dialect_code != original_dialect_code
|
|
135
|
+
@service_container.host_service.refresh_host_list
|
|
136
|
+
@db_dialect
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Creates the initial host list provider from the URL-guessed dialect.
|
|
140
|
+
# Called after the service container is fully assembled.
|
|
141
|
+
#
|
|
142
|
+
# @param service_container [ServiceContainer]
|
|
143
|
+
def setup_initial_provider(service_container)
|
|
144
|
+
@service_container = service_container
|
|
145
|
+
provider = @db_dialect.create_host_list_provider(service_container)
|
|
146
|
+
service_container.host_service.host_list_provider = provider if provider
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @param error [Exception]
|
|
150
|
+
# @return [Boolean]
|
|
151
|
+
def network_error?(error)
|
|
152
|
+
@error_handler.network_error?(error)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @param error [Exception]
|
|
156
|
+
# @return [Boolean]
|
|
157
|
+
def login_error?(error)
|
|
158
|
+
@error_handler.login_error?(error)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# @param error [Exception]
|
|
162
|
+
# @return [Boolean]
|
|
163
|
+
def read_only_error?(error)
|
|
164
|
+
@error_handler.read_only_error?(error)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
# Resolves the initial database dialect from the initial connection info.
|
|
170
|
+
# Uses RdsUtils to classify the host (Aurora cluster, RDS instance, etc.)
|
|
171
|
+
# and selects the appropriate dialect.
|
|
172
|
+
#
|
|
173
|
+
# @return [Object] the resolved database dialect
|
|
174
|
+
def init_dialect
|
|
175
|
+
@can_update = false
|
|
176
|
+
@db_dialect = nil
|
|
177
|
+
|
|
178
|
+
# Check for a custom dialect object injected via config
|
|
179
|
+
custom = AwsAdvancedRubyDriverWrapper.config.custom_dialect
|
|
180
|
+
if custom
|
|
181
|
+
@dialect_code = :custom
|
|
182
|
+
@db_dialect = custom
|
|
183
|
+
@can_update = false
|
|
184
|
+
return @db_dialect
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
user_dialect_setting = PropertyDefinition::DIALECT.get(@connection_service.wrapper_props)&.to_s
|
|
188
|
+
user_dialect_setting = user_dialect_setting&.strip&.downcase
|
|
189
|
+
host = @connection_service.initial_host_info&.host
|
|
190
|
+
|
|
191
|
+
dialect_code = if user_dialect_setting.nil? || user_dialect_setting.empty?
|
|
192
|
+
self.class.known_endpoint_dialects.get(host) unless host.nil?
|
|
193
|
+
else
|
|
194
|
+
user_dialect_setting
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
if dialect_code
|
|
198
|
+
dialect = dialect_for_code(dialect_code)
|
|
199
|
+
raise Errors::AwsError, "Unknown dialect code: #{dialect_code}" unless dialect
|
|
200
|
+
|
|
201
|
+
@dialect_code = dialect_code
|
|
202
|
+
@db_dialect = dialect
|
|
203
|
+
return @db_dialect
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
rds_type = Utils::RdsUtils.identify_rds_type(host)
|
|
207
|
+
|
|
208
|
+
@dialect_code = if @driver_dialect == DriverDialects::DriverDialectManager::MYSQL_DIALECT
|
|
209
|
+
resolve_mysql_dialect(rds_type)
|
|
210
|
+
elsif @driver_dialect == DriverDialects::DriverDialectManager::PG_DIALECT
|
|
211
|
+
resolve_pg_dialect(rds_type)
|
|
212
|
+
else
|
|
213
|
+
DialectCodes::UNKNOWN
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
@db_dialect = dialect_for_code(@dialect_code)
|
|
217
|
+
@db_dialect
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def resolve_mysql_dialect(rds_type)
|
|
221
|
+
if rds_type == Utils::RdsUrlType::RDS_GLOBAL_WRITER_CLUSTER
|
|
222
|
+
@can_update = false
|
|
223
|
+
DialectCodes::GLOBAL_AURORA_MYSQL
|
|
224
|
+
elsif rds_type.rds_cluster?
|
|
225
|
+
@can_update = true
|
|
226
|
+
DialectCodes::AURORA_MYSQL
|
|
227
|
+
elsif rds_type.rds?
|
|
228
|
+
@can_update = true
|
|
229
|
+
DialectCodes::RDS_MYSQL
|
|
230
|
+
else
|
|
231
|
+
@can_update = true
|
|
232
|
+
DialectCodes::MYSQL
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def resolve_pg_dialect(rds_type)
|
|
237
|
+
if rds_type == Utils::RdsUrlType::RDS_GLOBAL_WRITER_CLUSTER
|
|
238
|
+
@can_update = false
|
|
239
|
+
DialectCodes::GLOBAL_AURORA_PG
|
|
240
|
+
elsif rds_type == Utils::RdsUrlType::RDS_AURORA_LIMITLESS_DB_SHARD_GROUP
|
|
241
|
+
@can_update = false
|
|
242
|
+
DialectCodes::AURORA_PG
|
|
243
|
+
elsif rds_type.rds_cluster?
|
|
244
|
+
@can_update = true
|
|
245
|
+
DialectCodes::AURORA_PG
|
|
246
|
+
elsif rds_type.rds?
|
|
247
|
+
@can_update = true
|
|
248
|
+
DialectCodes::RDS_PG
|
|
249
|
+
else
|
|
250
|
+
@can_update = true
|
|
251
|
+
DialectCodes::PG
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def swap_host_list_provider
|
|
256
|
+
return unless @service_container
|
|
257
|
+
|
|
258
|
+
host_service = @service_container.host_service
|
|
259
|
+
old_provider = host_service.host_list_provider
|
|
260
|
+
old_provider&.stop_monitor
|
|
261
|
+
|
|
262
|
+
new_provider = @db_dialect.create_host_list_provider(@service_container)
|
|
263
|
+
host_service.host_list_provider = new_provider
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
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/map'
|
|
18
|
+
require_relative '../host/random_host_selector'
|
|
19
|
+
|
|
20
|
+
module AwsAdvancedRubyDriverWrapper
|
|
21
|
+
module Services
|
|
22
|
+
class HostService
|
|
23
|
+
DEFAULT_HOST_SELECTORS = {
|
|
24
|
+
Host::RandomHostSelector::STRATEGY_NAME => Host::RandomHostSelector.new
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
attr_accessor :host_list_provider
|
|
28
|
+
|
|
29
|
+
@host_id_cache = Concurrent::Map.new
|
|
30
|
+
@strategies = Concurrent::Map.new
|
|
31
|
+
DEFAULT_HOST_SELECTORS.each { |name, selector| @strategies[name] = selector }
|
|
32
|
+
|
|
33
|
+
def initialize(service_container)
|
|
34
|
+
@service_container = service_container
|
|
35
|
+
@all_hosts = []
|
|
36
|
+
@availability_cache = Utils::Storage::ExpirationCache.new
|
|
37
|
+
@host_list_provider = nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
attr_reader :host_id_cache
|
|
42
|
+
|
|
43
|
+
def clear_id_cache
|
|
44
|
+
@host_id_cache.clear
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Register a non-default host selector. The selector is shared by every HostService in the process,
|
|
48
|
+
# so it must be safe to call from multiple threads.
|
|
49
|
+
#
|
|
50
|
+
# @param name [String] strategy name
|
|
51
|
+
# @param selector [#select_host] any object responding to select_host(hosts, role, props)
|
|
52
|
+
def register_host_selector(name, selector)
|
|
53
|
+
raise Errors::AwsError, "Cannot override default host selection strategy: '#{name}'" if DEFAULT_HOST_SELECTORS.key?(name)
|
|
54
|
+
|
|
55
|
+
@strategies[name] = selector
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @param name [String] strategy name
|
|
59
|
+
# @return [#select_host, nil] the registered selector, or nil if the name is unknown
|
|
60
|
+
def host_selector(name)
|
|
61
|
+
@strategies[name]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Removes every non-default host selector. For testing only.
|
|
65
|
+
# @api private
|
|
66
|
+
def reset_host_selectors
|
|
67
|
+
# Snapshot the names first rather than iterating the map while deleting from it.
|
|
68
|
+
custom_names = @strategies.keys.reject { |name| DEFAULT_HOST_SELECTORS.key?(name) }
|
|
69
|
+
custom_names.each { |name| @strategies.delete(name) }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @param hosts [Array<Host::HostInfo>]
|
|
74
|
+
# @param role [Symbol, nil]
|
|
75
|
+
# @param strategy [String]
|
|
76
|
+
# @param props [Hash, nil]
|
|
77
|
+
# @return [Host::HostInfo]
|
|
78
|
+
def select_host(hosts, role, strategy, props = nil)
|
|
79
|
+
normalized = strategy.to_s.strip.downcase
|
|
80
|
+
selector = self.class.host_selector(normalized)
|
|
81
|
+
raise Errors::AwsError, "Unsupported host selection strategy: '#{strategy}'" if selector.nil?
|
|
82
|
+
|
|
83
|
+
selector.select_host(hosts, role, props)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Array<Host::HostInfo>] all hosts in the topology, including blocked/unavailable
|
|
87
|
+
attr_reader :all_hosts
|
|
88
|
+
|
|
89
|
+
# @return [Array<Host::HostInfo>] hosts filtered by allowed/blocked rules from the custom endpoint plugin
|
|
90
|
+
def hosts
|
|
91
|
+
rules = @service_container.storage_service.get_if_registered(
|
|
92
|
+
:custom_endpoint_allowed_blocked,
|
|
93
|
+
@service_container.connection_service.initial_host_info&.url,
|
|
94
|
+
register_access: false
|
|
95
|
+
)
|
|
96
|
+
return @all_hosts if rules.nil?
|
|
97
|
+
|
|
98
|
+
allowed = rules[:allowed]
|
|
99
|
+
blocked = rules[:blocked]
|
|
100
|
+
required_role = rules[:required_role]
|
|
101
|
+
|
|
102
|
+
hosts = @all_hosts
|
|
103
|
+
hosts = hosts.select { |h| allowed.include?(h.id) } if allowed
|
|
104
|
+
hosts = hosts.reject { |h| h.id && blocked.include?(h.id) } if blocked
|
|
105
|
+
hosts = hosts.select { |h| h.role == required_role } if required_role
|
|
106
|
+
hosts
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Updates the availability of a host in the internal host list.
|
|
110
|
+
#
|
|
111
|
+
# @param host_info [HostInfo] the host whose availability has been determined
|
|
112
|
+
# @param availability [Symbol] the new availability status, e.g. :available or :unavailable
|
|
113
|
+
def set_availability(host_info, availability)
|
|
114
|
+
host = @all_hosts.find { |h| h.id == host_info.id || h.host.casecmp?(host_info.host) }
|
|
115
|
+
return if host.nil?
|
|
116
|
+
|
|
117
|
+
host.availability = availability
|
|
118
|
+
@availability_cache.put(host_info.url, availability)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Refresh the host list from the host list provider.
|
|
122
|
+
def refresh_host_list
|
|
123
|
+
updated_hosts = @host_list_provider&.refresh
|
|
124
|
+
return if updated_hosts.nil? || updated_hosts == @all_hosts
|
|
125
|
+
|
|
126
|
+
apply_cached_availability(updated_hosts)
|
|
127
|
+
@all_hosts = updated_hosts
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Force a refresh of the host list, bypassing any caching.
|
|
131
|
+
#
|
|
132
|
+
# @param verify_writer [Boolean]
|
|
133
|
+
# @param timeout_sec [Float]
|
|
134
|
+
# @return [Boolean] whether the refresh was successful
|
|
135
|
+
def force_refresh_host_list?(verify_writer: false, timeout_sec: 5.0)
|
|
136
|
+
updated_hosts = @host_list_provider&.force_refresh(verify_writer, timeout_sec)
|
|
137
|
+
return false if updated_hosts.nil?
|
|
138
|
+
|
|
139
|
+
if updated_hosts != @all_hosts
|
|
140
|
+
apply_cached_availability(updated_hosts)
|
|
141
|
+
@all_hosts = updated_hosts
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Identify which host in the topology a given connection belongs to.
|
|
148
|
+
#
|
|
149
|
+
# @param connection [Object]
|
|
150
|
+
# @param connection_host_info [Host::HostInfo, nil] the host info used to establish the connection
|
|
151
|
+
# @return [Host::HostInfo, nil]
|
|
152
|
+
def identify_host(connection, connection_host_info = nil)
|
|
153
|
+
return find_host(*query_id_and_name(connection)) if connection_host_info.nil?
|
|
154
|
+
|
|
155
|
+
url_type = Utils::RdsUtils.identify_rds_type(connection_host_info&.host)
|
|
156
|
+
case url_type
|
|
157
|
+
when Utils::RdsUrlType::RDS_INSTANCE
|
|
158
|
+
connection_host_info
|
|
159
|
+
when Utils::RdsUrlType::IP_ADDRESS, Utils::RdsUrlType::OTHER
|
|
160
|
+
get_cached_host_info(connection, connection_host_info)
|
|
161
|
+
else
|
|
162
|
+
find_host(*query_id_and_name(connection))
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def apply_cached_availability(hosts)
|
|
169
|
+
hosts.each do |host|
|
|
170
|
+
availability = @availability_cache.get(host.url)
|
|
171
|
+
next if availability.nil?
|
|
172
|
+
|
|
173
|
+
host.availability = availability
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def get_cached_host_info(connection, connection_host_info)
|
|
178
|
+
host = connection_host_info.host
|
|
179
|
+
instance_id, instance_name = self.class.host_id_cache.compute_if_absent(host) do
|
|
180
|
+
query_id_and_name(connection)
|
|
181
|
+
end
|
|
182
|
+
find_host(instance_id, instance_name)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def query_id_and_name(connection)
|
|
186
|
+
@service_container.dialect_service.db_dialect.instance_identity(connection)
|
|
187
|
+
rescue StandardError
|
|
188
|
+
[nil, nil]
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def find_host(instance_id, instance_name)
|
|
192
|
+
topology = @host_list_provider&.refresh
|
|
193
|
+
return nil if topology.nil? || topology.empty?
|
|
194
|
+
|
|
195
|
+
topology.find { |h| h.id == instance_id || h.host == instance_name }
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
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 '../monitoring/monitor_state'
|
|
18
|
+
require_relative '../logging'
|
|
19
|
+
require_relative '../utils/storage/sliding_expiration_cache'
|
|
20
|
+
require_relative '../utils/events/data_access_event'
|
|
21
|
+
|
|
22
|
+
module AwsAdvancedRubyDriverWrapper
|
|
23
|
+
module Services
|
|
24
|
+
# Manages monitor lifecycle: registration, deduplication, expiration, and cleanup.
|
|
25
|
+
# Monitors are grouped by type (symbol) and keyed within each type.
|
|
26
|
+
# Subscribes to DataAccessEvent to extend monitor TTLs.
|
|
27
|
+
class MonitorService
|
|
28
|
+
include Logging
|
|
29
|
+
|
|
30
|
+
CLEANUP_INTERVAL_SEC = 60.0
|
|
31
|
+
|
|
32
|
+
# Internal container grouping a cache for a monitor type.
|
|
33
|
+
CacheContainer = Data.define(:cache, :produced_data_type)
|
|
34
|
+
|
|
35
|
+
# @param event_publisher [#subscribe] the event publisher to subscribe to.
|
|
36
|
+
def initialize(event_publisher:)
|
|
37
|
+
@caches = {}
|
|
38
|
+
@lock = Mutex.new
|
|
39
|
+
@running = true
|
|
40
|
+
@cleanup_thread = start_cleanup_thread
|
|
41
|
+
event_publisher.subscribe(
|
|
42
|
+
self,
|
|
43
|
+
Set[Utils::Events::DataAccessEvent]
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Registers a monitor type. No-op if already registered.
|
|
48
|
+
# @param monitor_type [Symbol] identifier for the monitor type.
|
|
49
|
+
# @param expiration_timeout_sec [Numeric] how long an unused monitor lives before expiring.
|
|
50
|
+
# @param produced_data_type [Symbol, nil] the data type this monitor produces (for DataAccessEvent linking).
|
|
51
|
+
def register_type(monitor_type, expiration_timeout_sec:, produced_data_type: nil)
|
|
52
|
+
@lock.synchronize do
|
|
53
|
+
return if @caches.key?(monitor_type)
|
|
54
|
+
|
|
55
|
+
cache = Utils::Storage::SlidingExpirationCache.new(ttl: expiration_timeout_sec)
|
|
56
|
+
@caches[monitor_type] = CacheContainer.new(cache:, produced_data_type:)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns or creates a monitor for the given type and key.
|
|
61
|
+
# If the monitor already exists, extends its expiration.
|
|
62
|
+
# @param monitor_type [Symbol] the registered monitor type.
|
|
63
|
+
# @param key [Object] unique key for this monitor instance.
|
|
64
|
+
# @param service_container [Object] passed to the initializer block.
|
|
65
|
+
# @yield [service_container] block to create the monitor if absent.
|
|
66
|
+
# @return [Object] the monitor instance.
|
|
67
|
+
def run_if_absent(monitor_type, key, service_container)
|
|
68
|
+
container = @lock.synchronize { @caches[monitor_type] }
|
|
69
|
+
raise ArgumentError, "Monitor type not registered: #{monitor_type}" unless container
|
|
70
|
+
|
|
71
|
+
container.cache.compute_if_absent(key) do
|
|
72
|
+
monitor = yield(service_container)
|
|
73
|
+
monitor.start
|
|
74
|
+
monitor
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Retrieves a monitor by type and key. Returns nil if absent or expired.
|
|
79
|
+
# @param monitor_type [Symbol] the monitor type.
|
|
80
|
+
# @param key [Object] the monitor key.
|
|
81
|
+
# @return [Object, nil]
|
|
82
|
+
def get(monitor_type, key)
|
|
83
|
+
container = @lock.synchronize { @caches[monitor_type] }
|
|
84
|
+
container&.cache&.get(key)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Removes a monitor without stopping it.
|
|
88
|
+
# @param monitor_type [Symbol] the monitor type.
|
|
89
|
+
# @param key [Object] the monitor key.
|
|
90
|
+
# @return [Object, nil] the removed monitor.
|
|
91
|
+
def remove(monitor_type, key)
|
|
92
|
+
container = @lock.synchronize { @caches[monitor_type] }
|
|
93
|
+
container&.cache&.remove(key)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Stops and removes a monitor.
|
|
97
|
+
# @param monitor_type [Symbol] the monitor type.
|
|
98
|
+
# @param key [Object] the monitor key.
|
|
99
|
+
def stop_and_remove(monitor_type, key)
|
|
100
|
+
monitor = remove(monitor_type, key)
|
|
101
|
+
monitor&.stop
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Stops and removes all monitors across all types.
|
|
105
|
+
def stop_and_remove_all
|
|
106
|
+
@lock.synchronize { @caches.values }.each do |container|
|
|
107
|
+
container.cache.entries.each_key do |key|
|
|
108
|
+
monitor = container.cache.remove(key)
|
|
109
|
+
monitor&.stop
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def shutdown(grace_period:)
|
|
115
|
+
@running = false
|
|
116
|
+
begin
|
|
117
|
+
@cleanup_thread&.wakeup
|
|
118
|
+
rescue ThreadError
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
@cleanup_thread&.join([grace_period, 5].min)
|
|
122
|
+
stop_and_remove_all
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Processes events from the event publisher.
|
|
126
|
+
# @param event [Event] the event to process.
|
|
127
|
+
def process_event(event)
|
|
128
|
+
return unless event == Utils::Events::DataAccessEvent
|
|
129
|
+
|
|
130
|
+
handle_data_access_event(event)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def handle_data_access_event(event)
|
|
136
|
+
@lock.synchronize { @caches.values }.each do |container|
|
|
137
|
+
next unless container.produced_data_type == event.data_type
|
|
138
|
+
|
|
139
|
+
container.cache.extend_expiration(event.key)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def start_cleanup_thread
|
|
144
|
+
thread = Thread.new do
|
|
145
|
+
while @running
|
|
146
|
+
sleep(CLEANUP_INTERVAL_SEC)
|
|
147
|
+
run_cleanup
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
thread.name = 'monitor-service-cleanup'
|
|
151
|
+
thread
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def run_cleanup
|
|
155
|
+
@lock.synchronize { @caches.values }.each do |container|
|
|
156
|
+
cleanup_container(container)
|
|
157
|
+
end
|
|
158
|
+
rescue StandardError
|
|
159
|
+
# Cleanup must not crash the thread.
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def cleanup_container(container)
|
|
163
|
+
container.cache.entries.each_key do |key|
|
|
164
|
+
# Remove stopped monitors
|
|
165
|
+
removed = container.cache.remove_if(key) { |m| m.state == Monitoring::MonitorState::STOPPED }
|
|
166
|
+
next if removed
|
|
167
|
+
|
|
168
|
+
# Stop and remove errored monitors
|
|
169
|
+
removed = container.cache.remove_if(key) { |m| m.state == Monitoring::MonitorState::ERROR }
|
|
170
|
+
if removed
|
|
171
|
+
logger.debug("Removed monitor in error state: #{key}")
|
|
172
|
+
removed.stop
|
|
173
|
+
next
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Remove expired monitors that can be disposed
|
|
177
|
+
removed = container.cache.remove_if_expired(key)
|
|
178
|
+
if removed
|
|
179
|
+
logger.debug("Removed expired monitor: #{key}")
|
|
180
|
+
removed.stop
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|