pg_insights 0.1.0 → 0.2.1
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/README.md +144 -45
- data/app/controllers/pg_insights/timeline_controller.rb +263 -0
- data/app/jobs/pg_insights/database_snapshot_job.rb +101 -0
- data/app/models/pg_insights/health_check_result.rb +151 -0
- data/app/services/pg_insights/health_check_service.rb +159 -3
- data/app/views/layouts/pg_insights/application.html.erb +1 -0
- data/app/views/pg_insights/timeline/compare.html.erb +997 -0
- data/app/views/pg_insights/timeline/index.html.erb +797 -0
- data/app/views/pg_insights/timeline/show.html.erb +1004 -0
- data/config/routes.rb +9 -5
- data/lib/generators/pg_insights/install_generator.rb +70 -19
- data/lib/generators/pg_insights/templates/db/migrate/create_pg_insights_health_check_results.rb +16 -0
- data/lib/generators/pg_insights/templates/db/migrate/create_pg_insights_queries.rb +12 -0
- data/lib/pg_insights/version.rb +1 -1
- data/lib/pg_insights.rb +24 -10
- data/lib/tasks/pg_insights.rake +419 -33
- metadata +9 -2
data/lib/tasks/pg_insights.rake
CHANGED
@@ -79,43 +79,15 @@ namespace :pg_insights do
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
-
desc "
|
83
|
-
task clear_data: :environment do
|
84
|
-
puts "Clearing PgInsights stored data..."
|
85
|
-
|
86
|
-
# Remove data from database
|
87
|
-
begin
|
88
|
-
if defined?(PgInsights::Query)
|
89
|
-
query_count = PgInsights::Query.count
|
90
|
-
PgInsights::Query.destroy_all
|
91
|
-
puts "Removed #{query_count} stored queries"
|
92
|
-
end
|
93
|
-
rescue => e
|
94
|
-
puts "Could not remove queries: #{e.message}"
|
95
|
-
end
|
96
|
-
|
97
|
-
begin
|
98
|
-
if defined?(PgInsights::HealthCheckResult)
|
99
|
-
result_count = PgInsights::HealthCheckResult.count
|
100
|
-
PgInsights::HealthCheckResult.destroy_all
|
101
|
-
puts "Removed #{result_count} cached health check results"
|
102
|
-
end
|
103
|
-
rescue => e
|
104
|
-
puts "Could not remove health check results: #{e.message}"
|
105
|
-
end
|
106
|
-
|
107
|
-
puts "PgInsights data cleared successfully!"
|
108
|
-
puts "To completely uninstall PgInsights (remove migrations and routes), run: rails generate pg_insights:clean"
|
109
|
-
end
|
110
|
-
|
111
|
-
desc "Reset PgInsights data (clears all stored queries and health check results)"
|
82
|
+
desc "Reset all PgInsights data (clears stored queries and health check results)"
|
112
83
|
task reset: :environment do
|
113
84
|
puts "Resetting PgInsights data..."
|
114
85
|
|
115
86
|
begin
|
116
87
|
if defined?(PgInsights::Query)
|
88
|
+
query_count = PgInsights::Query.count
|
117
89
|
PgInsights::Query.destroy_all
|
118
|
-
puts "Cleared
|
90
|
+
puts "Cleared #{query_count} stored queries"
|
119
91
|
end
|
120
92
|
rescue => e
|
121
93
|
puts "Could not clear queries: #{e.message}"
|
@@ -123,16 +95,21 @@ namespace :pg_insights do
|
|
123
95
|
|
124
96
|
begin
|
125
97
|
if defined?(PgInsights::HealthCheckResult)
|
98
|
+
result_count = PgInsights::HealthCheckResult.count
|
126
99
|
PgInsights::HealthCheckResult.destroy_all
|
127
|
-
puts "Cleared
|
100
|
+
puts "Cleared #{result_count} health check results"
|
128
101
|
end
|
129
102
|
rescue => e
|
130
103
|
puts "Could not clear health check results: #{e.message}"
|
131
104
|
end
|
132
105
|
|
133
|
-
puts "PgInsights data reset completed!"
|
106
|
+
puts "✅ PgInsights data reset completed!"
|
107
|
+
puts "To completely uninstall PgInsights (remove migrations and routes), run: rails generate pg_insights:clean"
|
134
108
|
end
|
135
109
|
|
110
|
+
# Alias for backward compatibility
|
111
|
+
task clear_data: :reset
|
112
|
+
|
136
113
|
desc "Show PgInsights statistics"
|
137
114
|
task stats: :environment do
|
138
115
|
puts "PgInsights Statistics:"
|
@@ -169,4 +146,413 @@ namespace :pg_insights do
|
|
169
146
|
puts "Health check results table not available: #{e.message}"
|
170
147
|
end
|
171
148
|
end
|
149
|
+
|
150
|
+
desc "Collect a database snapshot immediately"
|
151
|
+
task collect_snapshot: :environment do
|
152
|
+
puts "Collecting Database Snapshot"
|
153
|
+
puts "==========================="
|
154
|
+
|
155
|
+
unless PgInsights.snapshots_available?
|
156
|
+
puts "❌ Snapshots are not enabled"
|
157
|
+
puts " Add to config/initializers/pg_insights.rb:"
|
158
|
+
puts " PgInsights.configure { |c| c.enable_snapshots = true }"
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
|
162
|
+
start_time = Time.current
|
163
|
+
|
164
|
+
begin
|
165
|
+
PgInsights::HealthCheckService.execute_and_cache_check("database_snapshot")
|
166
|
+
execution_time = Time.current - start_time
|
167
|
+
|
168
|
+
puts "✅ Snapshot collected successfully in #{execution_time.round(2)} seconds"
|
169
|
+
|
170
|
+
latest = PgInsights::HealthCheckResult.latest_snapshot
|
171
|
+
if latest
|
172
|
+
puts " Snapshot ID: #{latest.id}"
|
173
|
+
puts " Collected at: #{latest.executed_at}"
|
174
|
+
puts " Parameters tracked: #{latest.result_data['parameters']&.keys&.count || 0}"
|
175
|
+
puts " Metrics collected: #{latest.result_data['metrics']&.keys&.count || 0}"
|
176
|
+
end
|
177
|
+
|
178
|
+
puts "Visit /pg_insights/timeline to view the snapshot in the timeline"
|
179
|
+
rescue => e
|
180
|
+
puts "❌ Snapshot collection failed: #{e.message}"
|
181
|
+
exit 1
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
desc "Start recurring snapshot collection"
|
186
|
+
task start_snapshots: :environment do
|
187
|
+
puts "Starting Recurring Database Snapshots"
|
188
|
+
puts "====================================="
|
189
|
+
|
190
|
+
unless PgInsights.snapshots_available?
|
191
|
+
puts "❌ Snapshots are not enabled"
|
192
|
+
exit 1
|
193
|
+
end
|
194
|
+
|
195
|
+
unless PgInsights.background_jobs_available?
|
196
|
+
puts "❌ Background jobs are not available"
|
197
|
+
puts " PgInsights needs ActiveJob to schedule recurring snapshots"
|
198
|
+
exit 1
|
199
|
+
end
|
200
|
+
|
201
|
+
validation = PgInsights::DatabaseSnapshotJob.validate_configuration
|
202
|
+
unless validation[:valid]
|
203
|
+
puts "❌ Configuration issues found:"
|
204
|
+
validation[:issues].each { |issue| puts " - #{issue}" }
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
|
208
|
+
begin
|
209
|
+
if PgInsights::DatabaseSnapshotJob.start_recurring_snapshots
|
210
|
+
puts "✅ Recurring snapshots started successfully"
|
211
|
+
puts " Frequency: #{PgInsights.snapshot_frequency}"
|
212
|
+
puts " Queue: #{PgInsights.background_job_queue}"
|
213
|
+
puts " Retention: #{PgInsights.snapshot_retention_days} days"
|
214
|
+
puts ""
|
215
|
+
puts "Snapshots will be collected automatically based on the configured frequency."
|
216
|
+
puts "Visit /pg_insights/timeline to monitor collection progress."
|
217
|
+
else
|
218
|
+
puts "❌ Failed to start recurring snapshots"
|
219
|
+
end
|
220
|
+
rescue => e
|
221
|
+
puts "❌ Error starting snapshots: #{e.message}"
|
222
|
+
exit 1
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
desc "Show snapshot configuration and status"
|
227
|
+
task snapshot_status: :environment do
|
228
|
+
puts "Database Snapshot Status"
|
229
|
+
puts "======================="
|
230
|
+
|
231
|
+
# Configuration
|
232
|
+
puts "Configuration:"
|
233
|
+
puts " Enabled: #{PgInsights.enable_snapshots ? 'Yes' : 'No'}"
|
234
|
+
puts " Collection Enabled: #{PgInsights.snapshot_collection_enabled ? 'Yes' : 'No'}"
|
235
|
+
puts " Frequency: #{PgInsights.snapshot_frequency}"
|
236
|
+
puts " Retention: #{PgInsights.snapshot_retention_days} days"
|
237
|
+
puts ""
|
238
|
+
|
239
|
+
# Availability check
|
240
|
+
if PgInsights.snapshots_available?
|
241
|
+
puts "✅ Snapshots are available"
|
242
|
+
else
|
243
|
+
puts "❌ Snapshots are not available"
|
244
|
+
return
|
245
|
+
end
|
246
|
+
|
247
|
+
# Database stats
|
248
|
+
begin
|
249
|
+
if defined?(PgInsights::HealthCheckResult)
|
250
|
+
snapshot_count = PgInsights::HealthCheckResult.snapshots.count
|
251
|
+
puts "Statistics:"
|
252
|
+
puts " Total snapshots: #{snapshot_count}"
|
253
|
+
|
254
|
+
latest = PgInsights::HealthCheckResult.latest_snapshot
|
255
|
+
if latest
|
256
|
+
puts " Latest snapshot: #{latest.executed_at}"
|
257
|
+
puts " Latest status: #{latest.status}"
|
258
|
+
else
|
259
|
+
puts " Latest snapshot: None"
|
260
|
+
end
|
261
|
+
|
262
|
+
# Recent snapshots
|
263
|
+
recent = PgInsights::HealthCheckResult.snapshots.limit(5)
|
264
|
+
if recent.any?
|
265
|
+
puts ""
|
266
|
+
puts "Recent snapshots:"
|
267
|
+
recent.each do |snapshot|
|
268
|
+
puts " #{snapshot.executed_at.strftime('%Y-%m-%d %H:%M')} - #{snapshot.status}"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Parameter changes
|
273
|
+
changes = PgInsights::HealthCheckResult.detect_parameter_changes_since(7)
|
274
|
+
puts ""
|
275
|
+
puts "Parameter changes (last 7 days): #{changes.count}"
|
276
|
+
if changes.any?
|
277
|
+
changes.first(3).each do |change_event|
|
278
|
+
puts " #{change_event[:detected_at].strftime('%Y-%m-%d %H:%M')} - #{change_event[:changes].keys.join(', ')}"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
rescue => e
|
283
|
+
puts "Could not retrieve snapshot statistics: #{e.message}"
|
284
|
+
end
|
285
|
+
|
286
|
+
# Background job validation
|
287
|
+
puts ""
|
288
|
+
validation = PgInsights::DatabaseSnapshotJob.validate_configuration
|
289
|
+
if validation[:valid]
|
290
|
+
puts "✅ #{validation[:message]}"
|
291
|
+
else
|
292
|
+
puts "❌ Configuration issues:"
|
293
|
+
validation[:issues].each { |issue| puts " - #{issue}" }
|
294
|
+
end
|
295
|
+
|
296
|
+
puts ""
|
297
|
+
puts "Commands:"
|
298
|
+
puts " Collect snapshot now: rails pg_insights:collect_snapshot"
|
299
|
+
puts " Start recurring: rails pg_insights:start_snapshots"
|
300
|
+
puts " Change frequency: Set PgInsights.snapshot_frequency in initializer"
|
301
|
+
end
|
302
|
+
|
303
|
+
desc "Clean up old snapshots"
|
304
|
+
task cleanup_snapshots: :environment do
|
305
|
+
puts "Cleaning up old snapshots..."
|
306
|
+
|
307
|
+
begin
|
308
|
+
deleted_count = PgInsights::HealthCheckResult.cleanup_old_snapshots
|
309
|
+
puts "✅ Cleaned up #{deleted_count} old snapshots"
|
310
|
+
puts " Retention period: #{PgInsights.snapshot_retention_days} days"
|
311
|
+
rescue => e
|
312
|
+
puts "❌ Cleanup failed: #{e.message}"
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
desc "Clean up old health check results (keeps recent results, removes old ones)"
|
317
|
+
task cleanup: :environment do
|
318
|
+
puts "🧹 Cleaning up old PgInsights health check results..."
|
319
|
+
|
320
|
+
# Keep results from last 30 days by default
|
321
|
+
retention_days = 30
|
322
|
+
cutoff_date = retention_days.days.ago
|
323
|
+
|
324
|
+
begin
|
325
|
+
deleted_count = PgInsights::HealthCheckResult.where("executed_at < ?", cutoff_date).delete_all
|
326
|
+
puts "✅ Cleaned up #{deleted_count} old health check results (older than #{retention_days} days)"
|
327
|
+
rescue => e
|
328
|
+
puts "❌ Cleanup failed: #{e.message}"
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
|
333
|
+
|
334
|
+
desc "Generate fake timeline data for testing (90 days)"
|
335
|
+
task seed_timeline: :environment do
|
336
|
+
puts "Generating fake timeline data for testing..."
|
337
|
+
|
338
|
+
end_date = Date.current
|
339
|
+
start_date = end_date - 90.days
|
340
|
+
|
341
|
+
puts "📅 Generating data from #{start_date.strftime('%B %d, %Y')} to #{end_date.strftime('%B %d, %Y')}"
|
342
|
+
|
343
|
+
PgInsights::HealthCheckResult.where(check_type: "database_snapshot").delete_all
|
344
|
+
puts "🗑️ Cleared existing timeline data"
|
345
|
+
|
346
|
+
base_config = {
|
347
|
+
"shared_buffers" => "256MB",
|
348
|
+
"work_mem" => "4MB",
|
349
|
+
"max_connections" => "100",
|
350
|
+
"effective_cache_size" => "1GB",
|
351
|
+
"checkpoint_completion_target" => "0.9",
|
352
|
+
"wal_buffers" => "16MB",
|
353
|
+
"default_statistics_target" => "100",
|
354
|
+
"random_page_cost" => "4.0",
|
355
|
+
"effective_io_concurrency" => "1",
|
356
|
+
"maintenance_work_mem" => "64MB"
|
357
|
+
}
|
358
|
+
|
359
|
+
config_changes = [
|
360
|
+
{ date: start_date + 15.days, changes: { "shared_buffers" => "512MB", "effective_cache_size" => "2GB" } },
|
361
|
+
{ date: start_date + 30.days, changes: { "work_mem" => "8MB", "max_connections" => "150" } },
|
362
|
+
{ date: start_date + 45.days, changes: { "checkpoint_completion_target" => "0.7" } },
|
363
|
+
{ date: start_date + 60.days, changes: { "shared_buffers" => "1GB", "maintenance_work_mem" => "128MB" } },
|
364
|
+
{ date: start_date + 75.days, changes: { "work_mem" => "6MB", "default_statistics_target" => "150" } }
|
365
|
+
]
|
366
|
+
|
367
|
+
current_config = base_config.dup
|
368
|
+
|
369
|
+
(start_date..end_date).each_with_index do |date, index|
|
370
|
+
config_changes.each do |change|
|
371
|
+
if date >= change[:date] && date < change[:date] + 1.day
|
372
|
+
current_config.merge!(change[:changes])
|
373
|
+
puts "⚙️ Config change on #{date.strftime('%m/%d/%Y')}: #{change[:changes].keys.join(', ')}"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
snapshots_per_day = rand(1..3)
|
378
|
+
|
379
|
+
snapshots_per_day.times do |snapshot_index|
|
380
|
+
hour = case snapshot_index
|
381
|
+
when 0 then rand(6..10)
|
382
|
+
when 1 then rand(11..15)
|
383
|
+
when 2 then rand(16..22)
|
384
|
+
end
|
385
|
+
minute = rand(0..59)
|
386
|
+
|
387
|
+
timestamp = date.beginning_of_day + hour.hours + minute.minutes
|
388
|
+
|
389
|
+
base_cache_hit_rate = 92.0 + Math.sin(index * 0.1) * 5 + rand(-2.0..2.0)
|
390
|
+
base_cache_hit_rate = [ [ base_cache_hit_rate, 75.0 ].max, 99.5 ].min
|
391
|
+
|
392
|
+
base_query_time = 15.0 + Math.sin(index * 0.05) * 10 + rand(-5.0..5.0)
|
393
|
+
base_query_time = [ base_query_time, 1.0 ].max
|
394
|
+
|
395
|
+
base_connections = 25 + Math.sin(index * 0.08) * 15 + rand(-5..5)
|
396
|
+
base_connections = [ [ base_connections, 5 ].max, 200 ].min
|
397
|
+
|
398
|
+
if rand(100) < 5
|
399
|
+
base_cache_hit_rate *= 0.8
|
400
|
+
base_query_time *= 2.5
|
401
|
+
base_connections = [ base_connections * 1.5, 300 ].min
|
402
|
+
end
|
403
|
+
|
404
|
+
metrics = {
|
405
|
+
"cache_hit_rate" => base_cache_hit_rate.round(2),
|
406
|
+
"avg_query_time" => base_query_time.round(2),
|
407
|
+
"total_connections" => base_connections.to_i,
|
408
|
+
"bloated_tables" => rand(0..8),
|
409
|
+
"unused_indexes" => rand(0..12),
|
410
|
+
"missing_indexes" => rand(0..5),
|
411
|
+
"slow_queries_count" => rand(0..25),
|
412
|
+
"dead_tuples_percent" => rand(0.0..5.0).round(2),
|
413
|
+
"index_usage_ratio" => (85.0 + rand(-10.0..10.0)).round(2),
|
414
|
+
"checkpoint_frequency" => rand(30..300),
|
415
|
+
"wal_size_mb" => rand(50..500),
|
416
|
+
"temp_files_count" => rand(0..50),
|
417
|
+
"lock_waits_count" => rand(0..10)
|
418
|
+
}
|
419
|
+
|
420
|
+
extensions = [ "pg_stat_statements", "uuid-ossp", "hstore", "postgis", "pg_trgm" ]
|
421
|
+
selected_extensions = extensions.sample(rand(3..5))
|
422
|
+
|
423
|
+
metadata = {
|
424
|
+
"postgresql_version" => "15.4",
|
425
|
+
"database_size" => "#{rand(500..2000)}MB",
|
426
|
+
"table_count" => rand(50..200),
|
427
|
+
"index_count" => rand(100..400),
|
428
|
+
"extensions" => selected_extensions,
|
429
|
+
"uptime_hours" => rand(100..8760),
|
430
|
+
"max_wal_size" => "1GB",
|
431
|
+
"archive_mode" => "on",
|
432
|
+
"log_statement" => "none",
|
433
|
+
"timezone" => "UTC"
|
434
|
+
}
|
435
|
+
|
436
|
+
result_data = {
|
437
|
+
"metrics" => metrics,
|
438
|
+
"parameters" => current_config,
|
439
|
+
"metadata" => metadata,
|
440
|
+
"snapshot_id" => SecureRandom.uuid,
|
441
|
+
"collection_method" => "automated"
|
442
|
+
}
|
443
|
+
|
444
|
+
PgInsights::HealthCheckResult.create!(
|
445
|
+
check_type: "database_snapshot",
|
446
|
+
status: "success",
|
447
|
+
result_data: result_data,
|
448
|
+
execution_time_ms: rand(50..300),
|
449
|
+
executed_at: timestamp
|
450
|
+
)
|
451
|
+
end
|
452
|
+
|
453
|
+
print "." if index % 10 == 0
|
454
|
+
end
|
455
|
+
|
456
|
+
puts "\n"
|
457
|
+
|
458
|
+
total_snapshots = PgInsights::HealthCheckResult.where(check_type: "database_snapshot").count
|
459
|
+
date_range = PgInsights::HealthCheckResult.where(check_type: "database_snapshot")
|
460
|
+
.pluck(:executed_at)
|
461
|
+
.minmax
|
462
|
+
|
463
|
+
puts "✅ Generated #{total_snapshots} timeline snapshots"
|
464
|
+
puts "📊 Date range: #{date_range[0].strftime('%B %d, %Y')} to #{date_range[1].strftime('%B %d, %Y')}"
|
465
|
+
puts "⚙️ Configuration changes: #{config_changes.count} parameter updates"
|
466
|
+
puts ""
|
467
|
+
puts "🚀 Timeline feature is ready for testing!"
|
468
|
+
puts " Visit: /pg_insights/timeline"
|
469
|
+
puts ""
|
470
|
+
puts "📈 Sample metrics generated:"
|
471
|
+
latest = PgInsights::HealthCheckResult.where(check_type: "database_snapshot").last
|
472
|
+
if latest
|
473
|
+
puts " • Cache Hit Rate: #{latest.result_data.dig('metrics', 'cache_hit_rate')}%"
|
474
|
+
puts " • Avg Query Time: #{latest.result_data.dig('metrics', 'avg_query_time')}ms"
|
475
|
+
puts " • Total Connections: #{latest.result_data.dig('metrics', 'total_connections')}"
|
476
|
+
puts " • Database Size: #{latest.result_data.dig('metadata', 'database_size')}"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
desc "Generate sample data for development"
|
481
|
+
task sample_data: :environment do
|
482
|
+
puts "🎭 Generating sample health check data..."
|
483
|
+
|
484
|
+
# Generate some basic health check results
|
485
|
+
check_types = %w[
|
486
|
+
unused_indexes
|
487
|
+
missing_indexes
|
488
|
+
bloated_tables
|
489
|
+
slow_queries
|
490
|
+
cache_hit_ratio
|
491
|
+
connection_stats
|
492
|
+
]
|
493
|
+
|
494
|
+
check_types.each do |check_type|
|
495
|
+
3.times do |i|
|
496
|
+
PgInsights::HealthCheckResult.create!(
|
497
|
+
check_type: check_type,
|
498
|
+
status: %w[success error].sample,
|
499
|
+
result_data: generate_sample_result_data(check_type),
|
500
|
+
execution_time_ms: rand(10..500),
|
501
|
+
executed_at: rand(1..72).hours.ago
|
502
|
+
)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
puts "✅ Generated sample health check data"
|
507
|
+
end
|
508
|
+
|
509
|
+
private
|
510
|
+
|
511
|
+
def self.generate_sample_result_data(check_type)
|
512
|
+
case check_type
|
513
|
+
when "unused_indexes"
|
514
|
+
{
|
515
|
+
"indexes" => [
|
516
|
+
{ "table" => "users", "index" => "idx_users_old_email", "size" => "45MB", "scans" => 0 },
|
517
|
+
{ "table" => "orders", "index" => "idx_orders_temp", "size" => "23MB", "scans" => 2 }
|
518
|
+
],
|
519
|
+
"total_wasted_space" => "68MB"
|
520
|
+
}
|
521
|
+
when "missing_indexes"
|
522
|
+
{
|
523
|
+
"suggestions" => [
|
524
|
+
{ "table" => "posts", "column" => "created_at", "seq_scans" => 1250, "estimated_benefit" => "High" },
|
525
|
+
{ "table" => "comments", "column" => "user_id", "seq_scans" => 890, "estimated_benefit" => "Medium" }
|
526
|
+
]
|
527
|
+
}
|
528
|
+
when "bloated_tables"
|
529
|
+
{
|
530
|
+
"tables" => [
|
531
|
+
{ "table" => "logs", "bloat_percent" => 45.2, "wasted_space" => "156MB" },
|
532
|
+
{ "table" => "events", "bloat_percent" => 32.1, "wasted_space" => "89MB" }
|
533
|
+
]
|
534
|
+
}
|
535
|
+
when "slow_queries"
|
536
|
+
{
|
537
|
+
"queries" => [
|
538
|
+
{ "query" => "SELECT * FROM users WHERE email LIKE...", "avg_time" => 1250.5, "calls" => 45 },
|
539
|
+
{ "query" => "UPDATE posts SET view_count...", "avg_time" => 890.2, "calls" => 123 }
|
540
|
+
]
|
541
|
+
}
|
542
|
+
when "cache_hit_ratio"
|
543
|
+
{
|
544
|
+
"buffer_cache_hit_ratio" => rand(85.0..99.9).round(2),
|
545
|
+
"index_cache_hit_ratio" => rand(90.0..99.9).round(2)
|
546
|
+
}
|
547
|
+
when "connection_stats"
|
548
|
+
{
|
549
|
+
"total_connections" => rand(10..100),
|
550
|
+
"active_connections" => rand(5..50),
|
551
|
+
"idle_connections" => rand(5..30),
|
552
|
+
"max_connections" => 100
|
553
|
+
}
|
554
|
+
else
|
555
|
+
{ "data" => "sample data", "timestamp" => Time.current }
|
556
|
+
end
|
557
|
+
end
|
172
558
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_insights
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mezbah Alam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -62,9 +62,11 @@ files:
|
|
62
62
|
- app/controllers/pg_insights/health_controller.rb
|
63
63
|
- app/controllers/pg_insights/insights_controller.rb
|
64
64
|
- app/controllers/pg_insights/queries_controller.rb
|
65
|
+
- app/controllers/pg_insights/timeline_controller.rb
|
65
66
|
- app/helpers/pg_insights/application_helper.rb
|
66
67
|
- app/helpers/pg_insights/insights_helper.rb
|
67
68
|
- app/jobs/pg_insights/application_job.rb
|
69
|
+
- app/jobs/pg_insights/database_snapshot_job.rb
|
68
70
|
- app/jobs/pg_insights/health_check_job.rb
|
69
71
|
- app/jobs/pg_insights/health_check_scheduler_job.rb
|
70
72
|
- app/jobs/pg_insights/recurring_health_checks_job.rb
|
@@ -87,10 +89,15 @@ files:
|
|
87
89
|
- app/views/pg_insights/insights/_table_controls.html.erb
|
88
90
|
- app/views/pg_insights/insights/_table_view.html.erb
|
89
91
|
- app/views/pg_insights/insights/index.html.erb
|
92
|
+
- app/views/pg_insights/timeline/compare.html.erb
|
93
|
+
- app/views/pg_insights/timeline/index.html.erb
|
94
|
+
- app/views/pg_insights/timeline/show.html.erb
|
90
95
|
- config/default_queries.yml
|
91
96
|
- config/routes.rb
|
92
97
|
- lib/generators/pg_insights/clean_generator.rb
|
93
98
|
- lib/generators/pg_insights/install_generator.rb
|
99
|
+
- lib/generators/pg_insights/templates/db/migrate/create_pg_insights_health_check_results.rb
|
100
|
+
- lib/generators/pg_insights/templates/db/migrate/create_pg_insights_queries.rb
|
94
101
|
- lib/pg_insights.rb
|
95
102
|
- lib/pg_insights/engine.rb
|
96
103
|
- lib/pg_insights/version.rb
|