detector 0.8.1 → 0.8.12
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 +4 -4
- data/bin/detector +63 -18
- data/lib/detector/addons/mariadb.rb +366 -49
- data/lib/detector/addons/mysql.rb +33 -7
- data/lib/detector/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc4e247924e74260a5c977c3dd3aedfe4ede09cf38533b0491b7194aaf9e9978
|
4
|
+
data.tar.gz: ecf78188a1bb18159c419c5ea7791a67f1c144c10785d765eb06f33ba51c727c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f131b6e65f64ec7283f34b86464ea63e1605c41f46d74987693158ec3b6a24f2e599d2bb512b111a1815afedafcfe8852a0ceda776e3780aaac2090a7d3960df
|
7
|
+
data.tar.gz: 82a1ebf34a59ecdb8efc97ca8ac66edf115f601bcc77845b2abaddedbc298d1f88c865f55a07584974b915c0b82ec0ba66f1ec3390f4275d1a1e4b0295c89760
|
data/bin/detector
CHANGED
@@ -45,15 +45,42 @@ end
|
|
45
45
|
|
46
46
|
puts "Detector v#{Detector::VERSION}"
|
47
47
|
puts "Detected: #{detector.kind}"
|
48
|
-
|
48
|
+
|
49
|
+
if ENV['DETECTOR_DEBUG']
|
50
|
+
puts "Database: #{detector.uri.path ? detector.uri.path.sub(/^\//, '') : 'none'}"
|
51
|
+
|
52
|
+
# In debug mode, if the detector has connection_error method, show error details
|
53
|
+
if detector.respond_to?(:connection_error) && detector.connection_error
|
54
|
+
error = detector.connection_error
|
55
|
+
puts "CONNECTION ERROR: #{error[:message]} (#{error[:type]}, code: #{error[:error_number]})"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if detector.version
|
60
|
+
puts "Version: #{detector.version}"
|
61
|
+
else
|
62
|
+
puts "Database connection issue: please check credentials and database name"
|
63
|
+
# Check if we have a max_user_connections error
|
64
|
+
if ENV['DETECTOR_DEBUG'] && detector&.connection.nil?
|
65
|
+
puts "Debug: connection method returned nil, trying connection directly for diagnostics..."
|
66
|
+
puts "Credentials: #{detector.uri.user}:**** @ #{detector.host}:#{detector.port}"
|
67
|
+
end
|
68
|
+
end
|
49
69
|
puts "Host: #{detector.host}:#{detector.port}"
|
50
70
|
|
51
71
|
if detector.respond_to?(:connection_info) && detector.connection_info
|
52
72
|
conn_info = detector.connection_info
|
53
|
-
|
73
|
+
if conn_info[:error]
|
74
|
+
puts "Connections: #{conn_info[:error]}"
|
75
|
+
else
|
76
|
+
puts "Connections: global #{conn_info[:connection_count][:global]}/#{conn_info[:connection_limits][:global]} (user #{conn_info[:connection_count][:user]}/#{conn_info[:connection_limits][:user]})"
|
77
|
+
end
|
54
78
|
elsif detector.connection_count && detector.connection_limit
|
55
79
|
usage = detector.connection_usage_percentage
|
56
80
|
puts "Connections: #{detector.connection_count}/#{detector.connection_limit} (#{usage}%)"
|
81
|
+
else
|
82
|
+
# No connection info available
|
83
|
+
puts "Connections: Unable to retrieve connection information"
|
57
84
|
end
|
58
85
|
|
59
86
|
if detector.respond_to?(:replication_available?) && !detector.replication_available?.nil?
|
@@ -82,26 +109,44 @@ end
|
|
82
109
|
|
83
110
|
if detector.databases?
|
84
111
|
db_count = detector.database_count
|
85
|
-
puts "\nDatabases: #{db_count}"
|
112
|
+
puts "\nDatabases: #{db_count || 'Unknown'}"
|
86
113
|
|
87
114
|
if db_count && db_count > 0
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
puts "
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
115
|
+
begin
|
116
|
+
dbs = detector.databases.first(3)
|
117
|
+
if dbs.empty?
|
118
|
+
puts " No databases found or access denied"
|
119
|
+
else
|
120
|
+
dbs.each do |db|
|
121
|
+
db_name = db[:name]
|
122
|
+
puts "\nDatabase: #{db_name} (#{db[:size]})"
|
123
|
+
|
124
|
+
if detector.tables?
|
125
|
+
if db[:table_count]
|
126
|
+
puts " Tables: #{db[:table_count]}"
|
127
|
+
else
|
128
|
+
puts " Tables: #{detector.table_count(db_name) || 'Unknown'}"
|
129
|
+
end
|
130
|
+
|
131
|
+
begin
|
132
|
+
tables = detector.tables(db_name).first(3)
|
133
|
+
if tables.empty?
|
134
|
+
puts " No tables found or access denied"
|
135
|
+
else
|
136
|
+
tables.each do |table|
|
137
|
+
puts " - #{table[:name]}: #{table[:row_count]} rows (#{table[:size]})"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
rescue => e
|
141
|
+
puts " Error retrieving tables: #{e.message}" if ENV['DETECTOR_DEBUG']
|
142
|
+
puts " No tables available (access error)"
|
143
|
+
end
|
144
|
+
end
|
103
145
|
end
|
104
146
|
end
|
147
|
+
rescue => e
|
148
|
+
puts " Error retrieving databases: #{e.message}" if ENV['DETECTOR_DEBUG']
|
149
|
+
puts " No database information available (access error)"
|
105
150
|
end
|
106
151
|
end
|
107
152
|
end
|
@@ -10,43 +10,233 @@ module Detector
|
|
10
10
|
def self.capabilities_for(url)
|
11
11
|
{ sql: true, kv: true, url: url, kind: :mariadb, databases: true, tables: true }
|
12
12
|
end
|
13
|
+
|
14
|
+
# Determine if an error is retriable
|
15
|
+
def retriable_error?(error_number)
|
16
|
+
# List of error codes that might be temporary and worth retrying
|
17
|
+
retriable_codes = [
|
18
|
+
1040, # Too many connections
|
19
|
+
1053, # Server shutdown in progress
|
20
|
+
1077, # Connection refused
|
21
|
+
2002, # Connection refused
|
22
|
+
2003, # Can't connect to MySQL server
|
23
|
+
2006, # MySQL server has gone away
|
24
|
+
2008, # Client ran out of memory
|
25
|
+
2013, # Lost connection during query
|
26
|
+
2026, # SSL connection error
|
27
|
+
2055 # Lost connection to MySQL server at '%s', system error: %d
|
28
|
+
]
|
29
|
+
|
30
|
+
retriable_codes.include?(error_number)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Retry a connection with exponential backoff
|
34
|
+
def with_retry(max_retries = 3)
|
35
|
+
retries = 0
|
36
|
+
|
37
|
+
begin
|
38
|
+
yield
|
39
|
+
rescue Mysql2::Error => e
|
40
|
+
if retriable_error?(e.error_number) && retries < max_retries
|
41
|
+
retries += 1
|
42
|
+
wait_time = 0.5 * (2 ** retries) # Exponential backoff: 1s, 2s, 4s, etc.
|
43
|
+
|
44
|
+
puts "Connection error (#{e.error_number}): #{e.message}. Retrying in #{wait_time}s (attempt #{retries}/#{max_retries})..." if ENV['DETECTOR_DEBUG']
|
45
|
+
|
46
|
+
sleep(wait_time)
|
47
|
+
retry
|
48
|
+
else
|
49
|
+
# Not retriable or max retries reached
|
50
|
+
raise
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
13
54
|
|
55
|
+
# Cache for database requests
|
56
|
+
def initialize(url)
|
57
|
+
super
|
58
|
+
@cache = {}
|
59
|
+
end
|
60
|
+
|
61
|
+
def connection
|
62
|
+
# Return cached connection if already established
|
63
|
+
return @conn if @conn && @conn.ping
|
64
|
+
|
65
|
+
# Override the MySQL connection method with MariaDB-specific settings
|
66
|
+
# Handle URI path correctly - strip leading slash if present
|
67
|
+
db_name = uri.path ? uri.path.sub(/^\//, '') : nil
|
68
|
+
|
69
|
+
begin
|
70
|
+
# Try with retry for retriable errors
|
71
|
+
with_retry do
|
72
|
+
# MariaDB-specific connection with fixed init command syntax
|
73
|
+
conn = Mysql2::Client.new(
|
74
|
+
host: host,
|
75
|
+
username: uri.user,
|
76
|
+
password: uri.password,
|
77
|
+
database: db_name,
|
78
|
+
port: port,
|
79
|
+
connect_timeout: 15,
|
80
|
+
read_timeout: 30,
|
81
|
+
write_timeout: 30,
|
82
|
+
reconnect: true
|
83
|
+
# No init_command - caused issues with MariaDB
|
84
|
+
)
|
85
|
+
|
86
|
+
# Test the connection with a simple query
|
87
|
+
conn.query("SELECT 1")
|
88
|
+
@conn = conn
|
89
|
+
end
|
90
|
+
|
91
|
+
@conn
|
92
|
+
rescue Mysql2::Error => e
|
93
|
+
error_message = "MariaDB connection error: #{e.message}"
|
94
|
+
error_type = case
|
95
|
+
when e.error_number == 1226 then "max_user_connections exceeded"
|
96
|
+
when e.error_number == 1045 then "access denied (auth failure)"
|
97
|
+
when e.error_number == 1049 then "unknown database '#{db_name}'"
|
98
|
+
when e.error_number == 2003 then "server unavailable or network error"
|
99
|
+
when e.error_number == 2005 then "unknown host"
|
100
|
+
when e.error_number == 2006 then "server gone away"
|
101
|
+
when e.error_number == 2013 then "connection lost"
|
102
|
+
else "general error"
|
103
|
+
end
|
104
|
+
|
105
|
+
puts "#{error_message} [#{error_type}]" if ENV['DETECTOR_DEBUG']
|
106
|
+
|
107
|
+
# Store the error information
|
108
|
+
@cache[:connection_error] = {
|
109
|
+
message: e.message,
|
110
|
+
type: error_type,
|
111
|
+
error_number: e.error_number,
|
112
|
+
retriable: retriable_error?(e.error_number)
|
113
|
+
}
|
114
|
+
|
115
|
+
nil
|
116
|
+
rescue => e
|
117
|
+
# For non-MySQL errors, still capture them
|
118
|
+
puts "General connection error: #{e.class} - #{e.message}" if ENV['DETECTOR_DEBUG']
|
119
|
+
@cache[:connection_error] = {
|
120
|
+
message: e.message,
|
121
|
+
type: "general error",
|
122
|
+
error_number: 0,
|
123
|
+
retriable: false
|
124
|
+
}
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Method to execute queries with retry for retriable errors
|
130
|
+
def execute_query(query)
|
131
|
+
return nil unless connection
|
132
|
+
|
133
|
+
begin
|
134
|
+
with_retry do
|
135
|
+
connection.query(query)
|
136
|
+
end
|
137
|
+
rescue => e
|
138
|
+
puts "Query execution error: #{e.message}" if ENV['DETECTOR_DEBUG']
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def connection_error
|
144
|
+
@cache[:connection_error]
|
145
|
+
end
|
146
|
+
|
147
|
+
def info
|
148
|
+
# Cache the info to avoid repeated queries
|
149
|
+
return @cache[:info] if @cache[:info]
|
150
|
+
|
151
|
+
# If we have a database and user but no connection, return basic info with error details
|
152
|
+
if connection.nil? && uri.path && uri.user
|
153
|
+
db_name = uri.path.sub(/^\//, '')
|
154
|
+
error_msg = connection_error ? " (#{connection_error[:type]})" : " (connection issue)"
|
155
|
+
|
156
|
+
@cache[:info] = {
|
157
|
+
'version' => "Unknown#{error_msg}",
|
158
|
+
'database' => db_name,
|
159
|
+
'user' => "#{uri.user}@remote"
|
160
|
+
}
|
161
|
+
return @cache[:info]
|
162
|
+
end
|
163
|
+
|
164
|
+
# Otherwise try to get info from connection
|
165
|
+
return nil unless connection
|
166
|
+
begin
|
167
|
+
result = execute_query("SELECT VERSION() AS version, DATABASE() AS `database`, USER() AS user")
|
168
|
+
@cache[:info] = result ? result.first : nil
|
169
|
+
return @cache[:info]
|
170
|
+
rescue Mysql2::Error => e
|
171
|
+
error_type = case
|
172
|
+
when e.error_number == 1226 then "max_user_connections exceeded"
|
173
|
+
else "query error"
|
174
|
+
end
|
175
|
+
|
176
|
+
puts "MariaDB info error: #{e.message} [#{error_type}]" if ENV['DETECTOR_DEBUG']
|
177
|
+
|
178
|
+
db_name = uri.path ? uri.path.sub(/^\//, '') : 'unknown'
|
179
|
+
{
|
180
|
+
'version' => "Unknown (#{error_type})",
|
181
|
+
'database' => db_name,
|
182
|
+
'user' => "#{uri.user}@error"
|
183
|
+
}
|
184
|
+
rescue => e
|
185
|
+
puts "General info error: #{e.message}" if ENV['DETECTOR_DEBUG']
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
14
190
|
def version
|
191
|
+
# Cache the version to avoid repeated queries
|
192
|
+
return @cache[:version] if @cache[:version]
|
193
|
+
|
15
194
|
return nil unless info
|
16
|
-
|
195
|
+
begin
|
196
|
+
@cache[:version] = "MariaDB #{info['version']} on #{info['database']} (#{info['user']})"
|
197
|
+
return @cache[:version]
|
198
|
+
rescue => e
|
199
|
+
@cache[:version] = "MariaDB (connection error: #{e.message})"
|
200
|
+
return @cache[:version]
|
201
|
+
end
|
17
202
|
end
|
18
203
|
|
19
204
|
def databases
|
205
|
+
# Cache the databases to avoid repeated queries
|
206
|
+
return @cache[:databases] if @cache[:databases]
|
207
|
+
|
20
208
|
return [] unless connection
|
21
209
|
begin
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
210
|
+
# Get all databases at once to reduce connections
|
211
|
+
query = "SELECT
|
212
|
+
s.schema_name AS name,
|
213
|
+
IFNULL(FORMAT(SUM(t.data_length + t.index_length) / 1024 / 1024, 2), '0.00') AS size_mb,
|
214
|
+
IFNULL(SUM(t.data_length + t.index_length), 0) AS raw_size,
|
215
|
+
COUNT(t.table_name) AS table_count
|
216
|
+
FROM information_schema.SCHEMATA s
|
217
|
+
LEFT JOIN information_schema.TABLES t ON t.table_schema = s.schema_name
|
218
|
+
WHERE s.schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
|
219
|
+
GROUP BY s.schema_name
|
220
|
+
ORDER BY raw_size DESC"
|
221
|
+
|
222
|
+
result = execute_query(query)
|
28
223
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
224
|
+
if result
|
225
|
+
db_list = result.map do |row|
|
226
|
+
{
|
227
|
+
name: row['name'],
|
228
|
+
size: "#{row['size_mb']} MB",
|
229
|
+
raw_size: row['raw_size'].to_i,
|
230
|
+
table_count: row['table_count'].to_i
|
231
|
+
}
|
232
|
+
end
|
38
233
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
table_count: size_data['table_count'].to_i
|
45
|
-
}
|
234
|
+
# Sort by size
|
235
|
+
@cache[:databases] = db_list.sort_by { |db| -db[:raw_size] }
|
236
|
+
return @cache[:databases]
|
237
|
+
else
|
238
|
+
return []
|
46
239
|
end
|
47
|
-
|
48
|
-
# Sort by size
|
49
|
-
@databases = result.sort_by { |db| -db[:raw_size] }
|
50
240
|
rescue => e
|
51
241
|
puts "Error getting databases: #{e.message}"
|
52
242
|
[]
|
@@ -54,48 +244,175 @@ module Detector
|
|
54
244
|
end
|
55
245
|
|
56
246
|
def connection_info
|
57
|
-
|
247
|
+
# Cache connection info to avoid repeated queries
|
248
|
+
return @cache[:connection_info] if @cache[:connection_info]
|
249
|
+
|
250
|
+
# If no connection is available, provide error information
|
251
|
+
if connection.nil?
|
252
|
+
error_msg = connection_error ? connection_error[:type] : "unknown error"
|
253
|
+
|
254
|
+
@cache[:connection_info] = {
|
255
|
+
connection_count: { user: "ERROR", global: "ERROR" },
|
256
|
+
connection_limits: { user: "ERROR", global: "ERROR" },
|
257
|
+
error: "Connection error: #{error_msg}"
|
258
|
+
}
|
259
|
+
return @cache[:connection_info]
|
260
|
+
end
|
261
|
+
|
262
|
+
# If connection is available, get actual connection info
|
58
263
|
begin
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
264
|
+
user_limit_result = execute_query("SELECT @@max_user_connections AS `limit`")
|
265
|
+
user_count_result = execute_query("SELECT COUNT(*) AS count FROM information_schema.PROCESSLIST WHERE user = USER()")
|
266
|
+
global_limit_result = execute_query("SELECT @@max_connections AS `limit`")
|
267
|
+
global_count_result = execute_query("SELECT COUNT(*) AS count FROM information_schema.PROCESSLIST")
|
268
|
+
|
269
|
+
# Check if any query failed
|
270
|
+
if !user_limit_result || !user_count_result || !global_limit_result || !global_count_result
|
271
|
+
return {
|
272
|
+
connection_count: { user: "ERROR", global: "ERROR" },
|
273
|
+
connection_limits: { user: "ERROR", global: "ERROR" },
|
274
|
+
error: "Error executing connection info queries"
|
275
|
+
}
|
276
|
+
end
|
277
|
+
|
278
|
+
user_limit = user_limit_result.first['limit'].to_i
|
279
|
+
user_count = user_count_result.first['count'].to_i
|
280
|
+
global_limit = global_limit_result.first['limit'].to_i
|
281
|
+
global_count = global_count_result.first['count'].to_i
|
63
282
|
|
64
283
|
# If user limit is 0, it means no specific per-user limit (use global)
|
65
284
|
user_limit = global_limit if user_limit == 0
|
66
285
|
|
67
|
-
{
|
286
|
+
@cache[:connection_info] = {
|
68
287
|
connection_count: { user: user_count, global: global_count },
|
69
288
|
connection_limits: { user: user_limit, global: global_limit }
|
70
289
|
}
|
290
|
+
return @cache[:connection_info]
|
291
|
+
rescue Mysql2::Error => e
|
292
|
+
error_type = case
|
293
|
+
when e.error_number == 1226 then "max_user_connections exceeded"
|
294
|
+
else "query error"
|
295
|
+
end
|
296
|
+
|
297
|
+
puts "MariaDB connection_info error: #{e.message} [#{error_type}]" if ENV['DETECTOR_DEBUG']
|
298
|
+
|
299
|
+
@cache[:connection_info] = {
|
300
|
+
connection_count: { user: "ERROR", global: "ERROR" },
|
301
|
+
connection_limits: { user: "ERROR", global: "ERROR" },
|
302
|
+
error: "Connection error: #{error_type}"
|
303
|
+
}
|
304
|
+
return @cache[:connection_info]
|
305
|
+
rescue => e
|
306
|
+
puts "General connection_info error: #{e.message}" if ENV['DETECTOR_DEBUG']
|
307
|
+
nil
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def database_count
|
312
|
+
# Cache database count to avoid repeated queries
|
313
|
+
return @cache[:database_count] if @cache[:database_count]
|
314
|
+
|
315
|
+
# If we have databases from cache, use the count
|
316
|
+
if @cache[:databases]
|
317
|
+
@cache[:database_count] = @cache[:databases].size
|
318
|
+
return @cache[:database_count]
|
319
|
+
end
|
320
|
+
|
321
|
+
# If no connection is available but we know the database name
|
322
|
+
if connection.nil? && uri.path
|
323
|
+
@cache[:database_count] = 1
|
324
|
+
return @cache[:database_count]
|
325
|
+
end
|
326
|
+
|
327
|
+
# Try to query the database
|
328
|
+
return nil unless connection
|
329
|
+
|
330
|
+
begin
|
331
|
+
result = execute_query("SELECT COUNT(*) AS count FROM information_schema.SCHEMATA WHERE schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')")
|
332
|
+
|
333
|
+
if result && result.first
|
334
|
+
@cache[:database_count] = result.first['count']
|
335
|
+
return @cache[:database_count]
|
336
|
+
else
|
337
|
+
return 0
|
338
|
+
end
|
339
|
+
rescue Mysql2::Error => e
|
340
|
+
puts "Error getting database count: #{e.message} [#{e.error_number}]" if ENV['DETECTOR_DEBUG']
|
341
|
+
|
342
|
+
# If this is an authentication or permission error, return 0
|
343
|
+
if e.error_number == 1045 || e.error_number == 1044
|
344
|
+
return 0
|
345
|
+
end
|
346
|
+
|
347
|
+
# For max connections error, assume at least 1 database
|
348
|
+
if e.error_number == 1226 && uri.path
|
349
|
+
return 1
|
350
|
+
end
|
351
|
+
|
352
|
+
nil
|
71
353
|
rescue => e
|
72
|
-
puts "
|
354
|
+
puts "General error getting database count: #{e.message}" if ENV['DETECTOR_DEBUG']
|
73
355
|
nil
|
74
356
|
end
|
75
357
|
end
|
76
358
|
|
77
359
|
def tables(database_name)
|
360
|
+
# Cache tables to avoid repeated queries
|
361
|
+
@tables ||= {}
|
362
|
+
return @tables[database_name] if @tables[database_name]
|
363
|
+
|
364
|
+
# If no connection, try to provide at least some basic info from known values
|
365
|
+
if connection.nil? && uri.path && uri.path.sub(/^\//, '') == database_name
|
366
|
+
# We know this is the database in the URL, so return some hardcoded data
|
367
|
+
if @cache[:dummy_tables]
|
368
|
+
return @cache[:dummy_tables]
|
369
|
+
else
|
370
|
+
# No tables data available
|
371
|
+
return []
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
78
375
|
return [] unless connection
|
79
376
|
|
80
377
|
begin
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
378
|
+
result = execute_query("SELECT
|
379
|
+
table_name AS name,
|
380
|
+
IFNULL(FORMAT((data_length + index_length) / 1024 / 1024, 2), '0.00') AS size_mb,
|
381
|
+
IFNULL((data_length + index_length), 0) AS raw_size,
|
382
|
+
IFNULL(table_rows, 0) AS row_count
|
383
|
+
FROM information_schema.TABLES
|
384
|
+
WHERE table_schema = '#{database_name}'
|
385
|
+
ORDER BY raw_size DESC")
|
386
|
+
|
387
|
+
if result
|
388
|
+
@tables[database_name] = result.map do |row|
|
389
|
+
{
|
390
|
+
name: row['name'],
|
391
|
+
size: "#{row['size_mb']} MB",
|
392
|
+
raw_size: row['raw_size'].to_i,
|
393
|
+
row_count: row['row_count'].to_i
|
394
|
+
}
|
395
|
+
end
|
396
|
+
return @tables[database_name]
|
397
|
+
else
|
398
|
+
@tables[database_name] = []
|
399
|
+
return []
|
96
400
|
end
|
401
|
+
rescue Mysql2::Error => e
|
402
|
+
error_type = case
|
403
|
+
when e.error_number == 1044 then "access denied to information_schema"
|
404
|
+
when e.error_number == 1045 then "authentication failure"
|
405
|
+
when e.error_number == 1226 then "max_user_connections exceeded"
|
406
|
+
else "query error (#{e.error_number})"
|
407
|
+
end
|
408
|
+
|
409
|
+
puts "Error getting tables for #{database_name}: #{e.message} [#{error_type}]" if ENV['DETECTOR_DEBUG']
|
410
|
+
|
411
|
+
# Store the error for debugging
|
412
|
+
@tables[database_name] = []
|
413
|
+
return []
|
97
414
|
rescue => e
|
98
|
-
puts "
|
415
|
+
puts "General error getting tables for #{database_name}: #{e.message}" if ENV['DETECTOR_DEBUG']
|
99
416
|
[]
|
100
417
|
end
|
101
418
|
end
|
@@ -13,13 +13,29 @@ module Detector
|
|
13
13
|
|
14
14
|
def connection
|
15
15
|
# Create a new connection each time without caching
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
# Handle URI path correctly - strip leading slash if present
|
17
|
+
db_name = uri.path ? uri.path.sub(/^\//, '') : nil
|
18
|
+
|
19
|
+
begin
|
20
|
+
# Try connection with longer timeouts - fix for MariaDB syntax
|
21
|
+
Mysql2::Client.new(
|
22
|
+
host: host,
|
23
|
+
username: uri.user,
|
24
|
+
password: uri.password,
|
25
|
+
database: db_name,
|
26
|
+
port: port,
|
27
|
+
connect_timeout: 15,
|
28
|
+
read_timeout: 30,
|
29
|
+
write_timeout: 30,
|
30
|
+
init_command: "SET wait_timeout=900; SET interactive_timeout=900"
|
31
|
+
)
|
32
|
+
rescue Mysql2::Error => e
|
33
|
+
puts "MySQL connection error: #{e.message}" if ENV['DETECTOR_DEBUG']
|
34
|
+
nil
|
35
|
+
rescue => e
|
36
|
+
puts "General connection error: #{e.class} - #{e.message}" if ENV['DETECTOR_DEBUG']
|
37
|
+
nil
|
38
|
+
end
|
23
39
|
end
|
24
40
|
|
25
41
|
def info
|
@@ -109,6 +125,16 @@ module Detector
|
|
109
125
|
connection_count: { user: user_count, global: global_count },
|
110
126
|
connection_limits: { user: user_limit, global: global_limit }
|
111
127
|
}
|
128
|
+
rescue Mysql2::Error => e
|
129
|
+
if e.error_number == 1226 # User has exceeded max_user_connections
|
130
|
+
{
|
131
|
+
connection_count: { user: "LIMIT EXCEEDED", global: "N/A" },
|
132
|
+
connection_limits: { user: "EXCEEDED", global: "N/A" },
|
133
|
+
error: "Error: User has exceeded max_user_connections limit"
|
134
|
+
}
|
135
|
+
else
|
136
|
+
nil
|
137
|
+
end
|
112
138
|
rescue => e
|
113
139
|
nil
|
114
140
|
end
|
data/lib/detector/version.rb
CHANGED