detector 0.4.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2fdcd9559b7c9d9d40b8908d4ec47ef17ff63d62305832bbe7f69b10b05a35b
4
- data.tar.gz: 0560305f80f5d1f5aea6eb5959ef7ebb3cc225479492a69594bf227d618804f1
3
+ metadata.gz: 803cfd73b761e5b098dac4d15062b3a47e768d70d862c61301f3e8444c011762
4
+ data.tar.gz: f96d8885689baed71e8dfd8aa986ee24a6b0620c2745ff91a60e541d9324b781
5
5
  SHA512:
6
- metadata.gz: 82be9fdd64b14e11e3ff0ff0d927b2508129eb0a84d5a5a4340c7afd991273ddd83f114f6ffdfb1d2add0e84a9526ba8ccd727887ecf70d694f4fb8cc2a0e053
7
- data.tar.gz: 53d7ee213ff018c23a7df00df47d8a3cf8525bb475a9eafb4415ee8ba9ffc4db3c2c22dbc48711f06fa2a390ef1f92a3fbf215451a2460022d34e3f23174bf79
6
+ metadata.gz: ec4621a88022efbac688c988275d21077dc260139a630659fef4f9257f4baa1409805add6f8056fe6c5a53b2329db849abc685839a5ace170b309089b9a909b3
7
+ data.tar.gz: ec584db3cb9ea42c2ce979c17cbc2d0facd7831d08105ab5ed1de0559c5ec5ca6e80fd32734851142ebb45eac676317fd8ac2f8fa72cd44eac41a5189c39bfe1
data/bin/detector CHANGED
@@ -7,10 +7,23 @@ if ARGV.empty?
7
7
  puts "Detector v#{Detector::VERSION}"
8
8
  puts "Usage: detector <URI>"
9
9
  puts "Example: detector \"postgres://user:pass@host:port/dbname\""
10
+ puts "Additional options:"
11
+ puts " --table=TABLE_NAME [--database=DB_NAME] : Estimate row count for a specific table"
10
12
  exit 1
11
13
  end
12
14
 
13
15
  uri = ARGV[0]
16
+ options = {}
17
+
18
+ # Parse command-line options
19
+ ARGV[1..-1].each do |arg|
20
+ if arg.start_with?('--table=')
21
+ options[:table] = arg.split('=', 2)[1]
22
+ elsif arg.start_with?('--database=')
23
+ options[:database] = arg.split('=', 2)[1]
24
+ end
25
+ end
26
+
14
27
  detector = Detector.detect(uri)
15
28
 
16
29
  if detector.nil?
@@ -18,11 +31,28 @@ if detector.nil?
18
31
  exit 1
19
32
  end
20
33
 
34
+ # If table is specified, show row count estimate and exit
35
+ if options[:table]
36
+ count = detector.estimated_row_count(table: options[:table], database: options[:database])
37
+ if count
38
+ puts "Estimated row count for #{options[:table]}: #{count}"
39
+ else
40
+ puts "Unable to estimate row count for #{options[:table]}"
41
+ end
42
+ detector.close
43
+ exit 0
44
+ end
45
+
21
46
  puts "Detector v#{Detector::VERSION}"
22
47
  puts "Detected: #{detector.kind}"
23
48
  puts "Version: #{detector.version}"
24
49
  puts "Host: #{detector.host}:#{detector.port}"
25
50
 
51
+ if detector.connection_count && detector.connection_limit
52
+ usage = detector.connection_usage_percentage
53
+ puts "Connections: #{detector.connection_count}/#{detector.connection_limit} (#{usage}%)"
54
+ end
55
+
26
56
  if detector.respond_to?(:replication_available?) && !detector.replication_available?.nil?
27
57
  puts "Replication: #{detector.replication_available? ? 'Available' : 'Not available'}"
28
58
  end
@@ -71,4 +101,7 @@ if detector.databases?
71
101
  end
72
102
  end
73
103
  end
74
- end
104
+ end
105
+
106
+ # Make sure to close the connection
107
+ detector.close
@@ -108,6 +108,11 @@ module Detector
108
108
 
109
109
  access_level
110
110
  end
111
+
112
+ # MariaDB inherits the estimated_row_count method from MySQL, but we might want to override
113
+ # with MariaDB-specific optimizations or different statistics approaches in the future
114
+
115
+ # MariaDB inherits the close method from MySQL
111
116
  end
112
117
  end
113
118
 
@@ -187,6 +187,33 @@ module Detector
187
187
  nil
188
188
  end
189
189
  end
190
+
191
+ def estimated_row_count(table:, database: nil)
192
+ return nil unless connection
193
+
194
+ # Use current database if none specified
195
+ db_name = database || info['database']
196
+ return nil unless db_name
197
+
198
+ begin
199
+ # Query information_schema.tables for the statistics-based row estimate
200
+ result = connection.query("SELECT table_rows AS estimate
201
+ FROM information_schema.tables
202
+ WHERE table_schema = '#{db_name}'
203
+ AND table_name = '#{table}'").first
204
+
205
+ result ? result['estimate'].to_i : nil
206
+ rescue => e
207
+ nil
208
+ end
209
+ end
210
+
211
+ def close
212
+ if @conn
213
+ @conn.close rescue nil
214
+ @conn = nil
215
+ end
216
+ end
190
217
  end
191
218
  end
192
219
 
@@ -204,6 +204,46 @@ module Detector
204
204
  nil
205
205
  end
206
206
  end
207
+
208
+ def estimated_row_count(table:, database: nil)
209
+ return nil unless connection
210
+
211
+ # Use the current database if none is specified
212
+ db_name = database || current_database
213
+
214
+ begin
215
+ # If we need to query a different database, temporarily connect to it
216
+ if db_name != current_database
217
+ # Create a temporary connection to the specified database
218
+ temp_conn = PG::Connection.new(host: host, port: port, user: uri.user,
219
+ password: uri.password, dbname: db_name) rescue nil
220
+ return nil unless temp_conn
221
+
222
+ # Use pg_class.reltuples for a fast, statistics-based row estimate
223
+ count = temp_conn.exec("SELECT reltuples::bigint AS estimate
224
+ FROM pg_class
225
+ WHERE relname = '#{table}'").first
226
+ temp_conn.close
227
+ return count ? count['estimate'].to_i : nil
228
+ end
229
+
230
+ # Query the current database using pg_class.reltuples
231
+ count = connection.exec("SELECT reltuples::bigint AS estimate
232
+ FROM pg_class
233
+ WHERE relname = '#{table}'").first
234
+
235
+ count ? count['estimate'].to_i : nil
236
+ rescue => e
237
+ nil
238
+ end
239
+ end
240
+
241
+ def close
242
+ if @conn
243
+ @conn.close rescue nil
244
+ @conn = nil
245
+ end
246
+ end
207
247
  end
208
248
  end
209
249
 
@@ -1,4 +1,5 @@
1
1
  require 'redis'
2
+ require 'timeout'
2
3
 
3
4
  module Detector
4
5
  module Addons
@@ -171,6 +172,52 @@ module Detector
171
172
  nil
172
173
  end
173
174
  end
175
+
176
+ def estimated_row_count(table:, database: nil)
177
+ return nil unless connection
178
+
179
+ # In Redis, the database is a number (0-15 typically) and "table" concept is closest to key patterns
180
+ # We'll interpret table parameter as a key pattern
181
+
182
+ begin
183
+ # Set the database if specified
184
+ if database
185
+ # Redis db numbers are integers
186
+ db_num = database.to_s.gsub(/[^0-9]/, '').to_i
187
+ connection.select(db_num) rescue nil
188
+ end
189
+
190
+ # Count keys matching the pattern (consider this a heuristic approximation)
191
+ # Use SCAN for larger datasets, as it doesn't block the server
192
+ count = 0
193
+ cursor = "0"
194
+
195
+ begin
196
+ # Timeout after a reasonable time to prevent long-running operations
197
+ Timeout.timeout(5) do
198
+ loop do
199
+ cursor, keys = connection.scan(cursor, match: table, count: 1000)
200
+ count += keys.size
201
+ break if cursor == "0"
202
+ end
203
+ end
204
+ rescue Timeout::Error
205
+ # If we time out, return the partial count with a note
206
+ return count
207
+ end
208
+
209
+ count
210
+ rescue => e
211
+ nil
212
+ end
213
+ end
214
+
215
+ def close
216
+ if @conn
217
+ @conn.quit rescue nil
218
+ @conn = nil
219
+ end
220
+ end
174
221
  end
175
222
  end
176
223
 
data/lib/detector/base.rb CHANGED
@@ -219,5 +219,27 @@ module Detector
219
219
  def replication_available?
220
220
  nil
221
221
  end
222
+
223
+ def connection_count
224
+ nil
225
+ end
226
+
227
+ def connection_limit
228
+ nil
229
+ end
230
+
231
+ def connection_usage_percentage
232
+ return nil unless connection_count && connection_limit && connection_limit > 0
233
+ (connection_count.to_f / connection_limit.to_f * 100).round(1)
234
+ end
235
+
236
+ def estimated_row_count(table:, database: nil)
237
+ nil
238
+ end
239
+
240
+ def close
241
+ # Default implementation does nothing
242
+ # Subclasses should override to close any open connections
243
+ end
222
244
  end
223
245
  end
@@ -1,3 +1,3 @@
1
1
  module Detector
2
- VERSION = "0.4.0"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: detector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Siegel