solid_observer 0.3.0 → 0.5.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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +195 -82
  4. data/app/assets/javascripts/solid_observer/live_poll.js +3 -1
  5. data/app/controllers/solid_observer/application_controller.rb +1 -0
  6. data/app/controllers/solid_observer/cable_dashboard_controller.rb +52 -0
  7. data/app/controllers/solid_observer/cable_operations_controller.rb +16 -0
  8. data/app/controllers/solid_observer/cache_dashboard_controller.rb +52 -0
  9. data/app/controllers/solid_observer/cache_operations_controller.rb +24 -0
  10. data/app/controllers/solid_observer/dashboard_controller.rb +38 -1
  11. data/app/controllers/solid_observer/storages_controller.rb +1 -1
  12. data/app/helpers/solid_observer/application_helper.rb +268 -5
  13. data/app/helpers/solid_observer/dashboard_helper.rb +30 -11
  14. data/app/models/solid_observer/cable_event.rb +13 -0
  15. data/app/models/solid_observer/cable_metric.rb +12 -0
  16. data/app/models/solid_observer/cache_event.rb +15 -0
  17. data/app/models/solid_observer/cache_metric.rb +13 -0
  18. data/app/models/solid_observer/storage_info.rb +4 -1
  19. data/app/views/layouts/solid_observer/application.html.erb +157 -19
  20. data/app/views/solid_observer/cable_dashboard/_charts.html.erb +31 -0
  21. data/app/views/solid_observer/cable_dashboard/_recent_events.html.erb +34 -0
  22. data/app/views/solid_observer/cable_dashboard/_summary.html.erb +34 -0
  23. data/app/views/solid_observer/cable_dashboard/index.html.erb +118 -0
  24. data/app/views/solid_observer/cache_dashboard/_charts.html.erb +40 -0
  25. data/app/views/solid_observer/cache_dashboard/_recent_events.html.erb +34 -0
  26. data/app/views/solid_observer/cache_dashboard/_summary.html.erb +39 -0
  27. data/app/views/solid_observer/cache_dashboard/index.html.erb +62 -0
  28. data/app/views/solid_observer/cache_operations/_confirm_clear.html.erb +6 -0
  29. data/app/views/solid_observer/cache_operations/index.html.erb +60 -0
  30. data/app/views/solid_observer/dashboard/_queue_table.html.erb +1 -0
  31. data/app/views/solid_observer/dashboard/index.html.erb +32 -5
  32. data/app/views/solid_observer/events/index.html.erb +1 -0
  33. data/app/views/solid_observer/jobs/index.html.erb +1 -0
  34. data/app/views/solid_observer/jobs/show.html.erb +3 -3
  35. data/app/views/solid_observer/storages/show.html.erb +90 -32
  36. data/config/routes.rb +7 -0
  37. data/db/migrate/20260601000001_create_solid_observer_cache_events.rb +22 -0
  38. data/db/migrate/20260601000002_create_solid_observer_cache_metrics.rb +18 -0
  39. data/db/migrate/20260602000001_add_component_to_solid_observer_storage_infos.rb +8 -0
  40. data/db/migrate/20260612000001_add_event_type_recorded_at_index_to_cache_events.rb +21 -0
  41. data/db/migrate/20260619000001_create_solid_observer_cable_events.rb +22 -0
  42. data/db/migrate/20260619000002_create_solid_observer_cable_metrics.rb +17 -0
  43. data/lib/generators/solid_observer/install_generator.rb +8 -1
  44. data/lib/generators/solid_observer/templates/initializer.rb.tt +20 -4
  45. data/lib/solid_observer/base_event.rb +1 -1
  46. data/lib/solid_observer/base_metric.rb +1 -1
  47. data/lib/solid_observer/base_record.rb +8 -0
  48. data/lib/solid_observer/cable_event_buffer.rb +28 -0
  49. data/lib/solid_observer/cable_metric_buffer.rb +230 -0
  50. data/lib/solid_observer/cable_subscriber.rb +57 -0
  51. data/lib/solid_observer/cache_event_buffer.rb +28 -0
  52. data/lib/solid_observer/cache_metric_buffer.rb +229 -0
  53. data/lib/solid_observer/cache_subscriber.rb +47 -0
  54. data/lib/solid_observer/chart_buffer.rb +84 -27
  55. data/lib/solid_observer/cli/storage.rb +16 -13
  56. data/lib/solid_observer/configuration.rb +67 -5
  57. data/lib/solid_observer/engine.rb +70 -15
  58. data/lib/solid_observer/event_buffer_core.rb +218 -0
  59. data/lib/solid_observer/queue_event_buffer.rb +9 -201
  60. data/lib/solid_observer/services/cable_operations.rb +74 -0
  61. data/lib/solid_observer/services/cable_stats.rb +385 -0
  62. data/lib/solid_observer/services/cache_operations.rb +115 -0
  63. data/lib/solid_observer/services/cache_stats.rb +346 -0
  64. data/lib/solid_observer/services/cleanup_storage.rb +98 -47
  65. data/lib/solid_observer/services/database_size.rb +13 -8
  66. data/lib/solid_observer/services/flush_cable_event_buffer.rb +54 -0
  67. data/lib/solid_observer/services/flush_cable_metrics.rb +54 -0
  68. data/lib/solid_observer/services/flush_cache_event_buffer.rb +54 -0
  69. data/lib/solid_observer/services/flush_cache_metrics.rb +56 -0
  70. data/lib/solid_observer/services/record_cable_event.rb +114 -0
  71. data/lib/solid_observer/services/record_cable_metric.rb +73 -0
  72. data/lib/solid_observer/services/record_cache_event.rb +165 -0
  73. data/lib/solid_observer/services/record_cache_metric.rb +66 -0
  74. data/lib/solid_observer/services/storage_info_snapshot.rb +216 -0
  75. data/lib/solid_observer/version.rb +1 -1
  76. data/lib/solid_observer.rb +36 -11
  77. data/lib/tasks/solid_observer.rake +111 -21
  78. metadata +47 -5
  79. data/bin/console +0 -11
  80. data/bin/quality_gate +0 -95
  81. data/bin/setup +0 -8
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "database_size"
4
+
5
+ module SolidObserver
6
+ module Services
7
+ class StorageInfoSnapshot
8
+ class RecordCount
9
+ def initialize(connection, table_name)
10
+ @connection = connection
11
+ @table_name = table_name
12
+ end
13
+
14
+ def solid_cache_count
15
+ case adapter_key
16
+ when :postgresql then postgresql_approximate_count
17
+ when :mysql then mysql_approximate_count
18
+ else
19
+ yield
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :connection, :table_name
26
+
27
+ def adapter_key
28
+ case connection.adapter_name.to_s.downcase
29
+ when /postgres|postgis/ then :postgresql
30
+ when "mysql2", "trilogy", "mysql" then :mysql
31
+ else
32
+ :other
33
+ end
34
+ end
35
+
36
+ def postgresql_approximate_count
37
+ quoted_table = connection.quote(table_name)
38
+
39
+ connection.query_value(<<~SQL.squish)&.to_i || 0
40
+ SELECT COALESCE(
41
+ (SELECT reltuples::bigint FROM pg_class WHERE oid = to_regclass(#{quoted_table})),
42
+ 0
43
+ )
44
+ SQL
45
+ end
46
+
47
+ def mysql_approximate_count
48
+ quoted_table = connection.quote(table_name)
49
+
50
+ connection.query_value(<<~SQL)&.to_i || 0
51
+ SELECT COALESCE(table_rows, 0)
52
+ FROM information_schema.tables
53
+ WHERE table_schema = DATABASE()
54
+ AND table_name = #{quoted_table}
55
+ SQL
56
+ end
57
+ end
58
+
59
+ Component = Struct.new(:key, :label, :record_label, :model, :enabled, keyword_init: true) do
60
+ def enabled?
61
+ enabled.call
62
+ end
63
+
64
+ def solid_cache?
65
+ key == "solid_cache"
66
+ end
67
+
68
+ def solid_cable?
69
+ key == "solid_cable"
70
+ end
71
+
72
+ def storage_model
73
+ model.call
74
+ end
75
+
76
+ def snapshot
77
+ return unless enabled?
78
+ return unavailable_snapshot(reason: "SolidCache is unavailable") if unavailable_solid_cache?
79
+ return unavailable_snapshot(reason: "SolidCable is unavailable") if solid_cable? && !defined?(::SolidCable::Message)
80
+
81
+ existing_data_source_snapshot
82
+ rescue *StorageInfoSnapshot::CONNECTION_ERRORS, TypeError
83
+ unavailable_snapshot(reason: "Storage unavailable")
84
+ end
85
+
86
+ def available_snapshot(db_size_bytes:, event_count:, **extras)
87
+ {
88
+ component: key,
89
+ label: label,
90
+ available: true,
91
+ db_size_bytes: db_size_bytes,
92
+ event_count: event_count,
93
+ record_label: record_label,
94
+ recorded_at: Time.current,
95
+ unavailable_reason: nil
96
+ }.merge(extras)
97
+ end
98
+
99
+ def unavailable_snapshot(reason:)
100
+ {
101
+ component: key,
102
+ label: label,
103
+ available: false,
104
+ db_size_bytes: nil,
105
+ event_count: nil,
106
+ record_label: record_label,
107
+ recorded_at: nil,
108
+ unavailable_reason: reason
109
+ }
110
+ end
111
+
112
+ private
113
+
114
+ def unavailable_solid_cache?
115
+ solid_cache? && !defined?(::SolidCache::Entry)
116
+ end
117
+
118
+ def existing_data_source_snapshot
119
+ record_model = storage_model
120
+ connection = record_model.connection
121
+ table_name = record_model.table_name.to_s
122
+ return unavailable_snapshot(reason: "Table unavailable") unless data_source_available?(connection, table_name)
123
+
124
+ build_available_snapshot(record_model, connection, table_name)
125
+ end
126
+
127
+ def build_available_snapshot(record_model, connection, table_name)
128
+ count = -> { record_model.count }
129
+ event_count = solid_cache? ? RecordCount.new(connection, table_name).solid_cache_count(&count) : count.call
130
+
131
+ available_snapshot(
132
+ db_size_bytes: DatabaseSize.call(connection: connection, table_name: table_name),
133
+ event_count: event_count,
134
+ **extras_for(record_model)
135
+ )
136
+ end
137
+
138
+ def extras_for(record_model)
139
+ solid_cable? ? solid_cable_extras(record_model) : {}
140
+ end
141
+
142
+ def data_source_available?(connection, table_name)
143
+ !table_name.empty? && connection.data_source_exists?(table_name)
144
+ end
145
+
146
+ def solid_cable_extras(record_model)
147
+ {
148
+ trimmable_count: safe_query { record_model.trimmable.count },
149
+ oldest_message_age_seconds: safe_query { (Time.current - record_model.minimum(:created_at).to_time).to_i }
150
+ }
151
+ end
152
+
153
+ def safe_query
154
+ yield
155
+ rescue *StorageInfoSnapshot::CONNECTION_ERRORS, TypeError, NoMethodError
156
+ nil
157
+ end
158
+ end
159
+
160
+ COMPONENTS = [
161
+ Component.new(
162
+ key: "queue_observer",
163
+ label: "Queue observer",
164
+ record_label: "observer events",
165
+ model: -> { SolidObserver::QueueEvent },
166
+ enabled: -> { SolidObserver.config.solid_queue_enabled? }
167
+ ),
168
+ Component.new(
169
+ key: "cache_observer",
170
+ label: "Cache observer",
171
+ record_label: "observer events",
172
+ model: -> { SolidObserver::CacheEvent },
173
+ enabled: -> { SolidObserver.config.solid_cache_enabled? }
174
+ ),
175
+ Component.new(
176
+ key: "solid_cache",
177
+ label: "SolidCache",
178
+ record_label: "cache rows",
179
+ model: -> { ::SolidCache::Entry },
180
+ enabled: -> { SolidObserver.config.solid_cache_enabled? }
181
+ ),
182
+ Component.new(
183
+ key: "cable_observer",
184
+ label: "Cable telemetry",
185
+ record_label: "observer events",
186
+ model: -> { SolidObserver::CableEvent },
187
+ enabled: -> { SolidObserver.config.solid_cable_enabled? }
188
+ ),
189
+ Component.new(
190
+ key: "solid_cable",
191
+ label: "Solid Cable messages",
192
+ record_label: "messages",
193
+ model: -> { ::SolidCable::Message },
194
+ enabled: -> { SolidObserver.config.solid_cable_enabled? }
195
+ )
196
+ ].freeze
197
+
198
+ CONNECTION_ERRORS = [
199
+ ActiveRecord::ConnectionNotEstablished,
200
+ ActiveRecord::StatementInvalid,
201
+ *([PG::ConnectionBad] if defined?(PG::ConnectionBad)),
202
+ *([Mysql2::Error::ConnectionError] if defined?(Mysql2::Error::ConnectionError)),
203
+ *([Trilogy::Error] if defined?(Trilogy::Error)),
204
+ *([SQLite3::CantOpenException] if defined?(SQLite3::CantOpenException))
205
+ ].freeze
206
+
207
+ def self.call
208
+ new.call
209
+ end
210
+
211
+ def call
212
+ COMPONENTS.filter_map(&:snapshot)
213
+ end
214
+ end
215
+ end
216
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidObserver
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  RUBY_MINIMUM_VERSION = "3.2.0"
6
6
  RAILS_MINIMUM_VERSION = "8.0"
7
7
  end
@@ -1,30 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/lazy_load_hooks"
4
+
3
5
  require_relative "solid_observer/version"
4
6
  require_relative "solid_observer/configuration"
5
7
  require_relative "solid_observer/correlation_id_resolver"
6
- require_relative "solid_observer/base_event" if defined?(ActiveRecord)
7
- require_relative "solid_observer/base_metric" if defined?(ActiveRecord)
8
8
  require_relative "solid_observer/params/jobs_filter"
9
9
  require_relative "solid_observer/params/events_filter"
10
- require_relative "solid_observer/queries/job_executions_query" if defined?(ActiveRecord)
11
- require_relative "solid_observer/queries/events_query" if defined?(ActiveRecord)
12
- require_relative "solid_observer/queries/execution_finder" if defined?(ActiveRecord)
13
- require_relative "solid_observer/services/record_event" if defined?(ActiveRecord)
14
- require_relative "solid_observer/services/flush_event_buffer" if defined?(ActiveRecord)
15
- require_relative "solid_observer/services/cleanup_storage" if defined?(ActiveRecord)
16
10
  require_relative "solid_observer/services/ui_auth_check"
17
- require_relative "solid_observer/queue_event_buffer" if defined?(ActiveRecord)
18
- require_relative "solid_observer/subscriber" if defined?(ActiveSupport)
11
+ require_relative "solid_observer/subscriber"
19
12
  require_relative "solid_observer/cli/base"
20
13
  require_relative "solid_observer/cli/status"
21
14
  require_relative "solid_observer/cli/storage"
22
15
  require_relative "solid_observer/cli/jobs"
23
16
  require_relative "solid_observer/queue_stats"
24
- require_relative "../app/presenters/solid_observer/execution_presenter"
25
17
  require_relative "solid_observer/engine" if defined?(Rails::Engine)
26
18
 
19
+ ActiveSupport.on_load(:active_record) do
20
+ require_relative "solid_observer/base_record"
21
+ require_relative "solid_observer/base_event"
22
+ require_relative "solid_observer/base_metric"
23
+ require_relative "solid_observer/queries/job_executions_query"
24
+ require_relative "solid_observer/queries/events_query"
25
+ require_relative "solid_observer/queries/execution_finder"
26
+ require_relative "solid_observer/services/record_event"
27
+ require_relative "solid_observer/services/flush_event_buffer"
28
+ require_relative "solid_observer/services/cleanup_storage"
29
+ require_relative "solid_observer/queue_event_buffer"
30
+ require_relative "solid_observer/cache_event_buffer"
31
+ require_relative "solid_observer/services/flush_cache_metrics"
32
+ require_relative "solid_observer/cache_metric_buffer"
33
+ require_relative "solid_observer/cache_subscriber"
34
+ require_relative "solid_observer/services/record_cache_event"
35
+ require_relative "solid_observer/services/record_cache_metric"
36
+ require_relative "solid_observer/services/flush_cache_event_buffer"
37
+ require_relative "solid_observer/services/cache_stats"
38
+ require_relative "solid_observer/services/cache_operations"
39
+ require_relative "solid_observer/cable_event_buffer"
40
+ require_relative "solid_observer/services/flush_cable_metrics"
41
+ require_relative "solid_observer/cable_metric_buffer"
42
+ require_relative "solid_observer/cable_subscriber"
43
+ require_relative "solid_observer/services/record_cable_event"
44
+ require_relative "solid_observer/services/record_cable_metric"
45
+ require_relative "solid_observer/services/flush_cable_event_buffer"
46
+ require_relative "solid_observer/services/cable_stats"
47
+ require_relative "solid_observer/services/cable_operations"
48
+ end
49
+
27
50
  module SolidObserver
51
+ autoload :ExecutionPresenter, File.expand_path("../app/presenters/solid_observer/execution_presenter", __dir__)
52
+
28
53
  class Error < StandardError; end
29
54
 
30
55
  class << self
@@ -40,31 +40,90 @@ namespace :solid_observer do
40
40
  end
41
41
 
42
42
  namespace :buffer do
43
- desc "Flush the event buffer to the database"
43
+ desc "Flush the event buffers to the database"
44
44
  task flush: :environment do
45
- buffer = SolidObserver::QueueEventBuffer.instance
46
- buffer_size = buffer.size
45
+ buffers = [
46
+ {label: "queue events", buffer: SolidObserver::QueueEventBuffer.instance},
47
+ {label: "cache events", buffer: SolidObserver::CacheEventBuffer.instance},
48
+ {label: "cache metric buckets", buffer: SolidObserver::CacheMetricBuffer.instance}
49
+ ]
50
+ buffer_sizes = buffers.to_h { |entry| [entry, entry[:buffer].size] }
51
+
52
+ if buffer_sizes.values.sum.zero?
53
+ puts "Buffers are empty, nothing to flush"
54
+ else
55
+ buffers.each do |entry|
56
+ size = buffer_sizes.fetch(entry)
57
+ next if size.zero?
47
58
 
48
- if buffer_size.zero?
49
- puts "Buffer is empty, nothing to flush"
59
+ puts "Flushing #{size} #{entry[:label]} from buffer..."
60
+ entry[:buffer].flush!
61
+ end
62
+ puts "✓ Buffers flushed successfully"
63
+ end
64
+ end
65
+
66
+ desc "Clear the event buffers without flushing to database"
67
+ task clear: :environment do
68
+ buffers = [
69
+ {label: "queue events", buffer: SolidObserver::QueueEventBuffer.instance},
70
+ {label: "cache events", buffer: SolidObserver::CacheEventBuffer.instance},
71
+ {label: "cache metric buckets", buffer: SolidObserver::CacheMetricBuffer.instance}
72
+ ]
73
+ buffer_sizes = buffers.to_h { |entry| [entry, entry[:buffer].size] }
74
+
75
+ if buffer_sizes.values.sum.zero?
76
+ puts "Buffers are already empty"
50
77
  else
51
- puts "Flushing #{buffer_size} events from buffer..."
52
- buffer.flush!
53
- puts "✓ Buffer flushed successfully"
78
+ buffers.each do |entry|
79
+ size = buffer_sizes.fetch(entry)
80
+ next if size.zero?
81
+
82
+ entry[:buffer].clear
83
+ puts "✓ Cleared #{size} #{entry[:label]} from buffer (not saved to database)"
84
+ end
54
85
  end
55
86
  end
87
+ end
56
88
 
57
- desc "Clear the event buffer without flushing to database"
89
+ namespace :cache do
90
+ desc "Clear all SolidCache entries after confirmation"
58
91
  task clear: :environment do
59
- buffer = SolidObserver::QueueEventBuffer.instance
60
- buffer_size = buffer.size
92
+ if !SolidObserver::Services::CacheOperations.available?
93
+ puts SolidObserver::Services::CacheOperations.unavailable_message
94
+ next
95
+ end
61
96
 
62
- if buffer_size.zero?
63
- puts "Buffer is already empty"
97
+ print "#{SolidObserver::Services::CacheOperations.message(:clear, :confirmation)} (y/N) "
98
+ $stdout.flush
99
+
100
+ if $stdin.gets&.strip&.downcase == "y"
101
+ puts SolidObserver::Services::CacheOperations.clear[:message]
64
102
  else
65
- buffer.clear
66
- puts "✓ Cleared #{buffer_size} events from buffer (not saved to database)"
103
+ puts "Aborted"
104
+ end
105
+ end
106
+
107
+ desc "Prune expired SolidCache entries"
108
+ task prune: :environment do
109
+ if !SolidObserver::Services::CacheOperations.available?
110
+ puts SolidObserver::Services::CacheOperations.unavailable_message
111
+ next
112
+ end
113
+
114
+ puts SolidObserver::Services::CacheOperations.prune[:message]
115
+ end
116
+ end
117
+
118
+ namespace :cable do
119
+ desc "Trim expired Solid Cable messages"
120
+ task trim: :environment do
121
+ if !SolidObserver::Services::CableOperations.available?
122
+ puts SolidObserver::Services::CableOperations.unavailable_message
123
+ next
67
124
  end
125
+
126
+ puts SolidObserver::Services::CableOperations.trim[:message]
68
127
  end
69
128
  end
70
129
 
@@ -75,7 +134,7 @@ namespace :solid_observer do
75
134
  puts "Running storage cleanup (retention: #{retention.inspect})..."
76
135
 
77
136
  deleted_count = SolidObserver::Services::CleanupStorage.call
78
- puts "✓ Cleanup complete: #{deleted_count} old events deleted"
137
+ puts "✓ Cleanup complete: #{deleted_count} old telemetry rows deleted"
79
138
  end
80
139
 
81
140
  desc "Purge ALL SolidObserver storage data (use with caution!)"
@@ -85,10 +144,30 @@ namespace :solid_observer do
85
144
 
86
145
  confirmation = $stdin.gets&.strip&.downcase
87
146
  if confirmation == "y"
88
- event_count = SolidObserver::QueueEvent.count
147
+ data_source_exists = lambda do |model|
148
+ table_name = model.table_name.to_s
149
+ !table_name.empty? && model.connection.data_source_exists?(table_name)
150
+ rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::StatementInvalid, TypeError
151
+ false
152
+ end
153
+
154
+ telemetry_models = [
155
+ {label: :queue_events, model: SolidObserver::QueueEvent},
156
+ {label: :cache_events, model: SolidObserver::CacheEvent},
157
+ {label: :cache_metrics, model: SolidObserver::CacheMetric}
158
+ ]
159
+
160
+ telemetry_counts = telemetry_models.to_h do |entry|
161
+ count = data_source_exists.call(entry[:model]) ? entry[:model].count : 0
162
+ [entry[:label], count]
163
+ end
89
164
  snapshot_count = SolidObserver::StorageInfo.count
90
165
 
91
- SolidObserver::QueueEvent.delete_all
166
+ telemetry_models.each do |entry|
167
+ next unless data_source_exists.call(entry[:model])
168
+
169
+ entry[:model].delete_all
170
+ end
92
171
  SolidObserver::StorageInfo.delete_all
93
172
 
94
173
  connection = SolidObserver::QueueEvent.connection
@@ -97,12 +176,23 @@ namespace :solid_observer do
97
176
  connection.execute("VACUUM")
98
177
  puts "✓ Database vacuumed to reclaim disk space"
99
178
  when "postgresql"
100
- connection.execute("ANALYZE solid_observer_queue_events")
101
- connection.execute("ANALYZE solid_observer_storage_info")
179
+ [
180
+ SolidObserver::QueueEvent.table_name,
181
+ SolidObserver::StorageInfo.table_name,
182
+ (SolidObserver::CacheEvent.table_name if data_source_exists.call(SolidObserver::CacheEvent)),
183
+ (SolidObserver::CacheMetric.table_name if data_source_exists.call(SolidObserver::CacheMetric))
184
+ ].compact.each do |table_name|
185
+ connection.execute("ANALYZE #{table_name}")
186
+ end
102
187
  puts "✓ Database tables analyzed"
103
188
  end
104
189
 
105
- puts "✓ Purged #{event_count} events and #{snapshot_count} storage snapshots"
190
+ puts(
191
+ "✓ Purged #{telemetry_counts.fetch(:queue_events)} queue events, " \
192
+ "#{telemetry_counts.fetch(:cache_events)} cache events, " \
193
+ "#{telemetry_counts.fetch(:cache_metrics)} cache metrics, " \
194
+ "and #{snapshot_count} storage snapshots"
195
+ )
106
196
  else
107
197
  puts "Aborted"
108
198
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_observer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - BartOz
@@ -54,6 +54,10 @@ files:
54
54
  - app/controllers/concerns/solid_observer/require_persistence_mode.rb
55
55
  - app/controllers/concerns/solid_observer/require_solid_queue.rb
56
56
  - app/controllers/solid_observer/application_controller.rb
57
+ - app/controllers/solid_observer/cable_dashboard_controller.rb
58
+ - app/controllers/solid_observer/cable_operations_controller.rb
59
+ - app/controllers/solid_observer/cache_dashboard_controller.rb
60
+ - app/controllers/solid_observer/cache_operations_controller.rb
57
61
  - app/controllers/solid_observer/dashboard_controller.rb
58
62
  - app/controllers/solid_observer/events_controller.rb
59
63
  - app/controllers/solid_observer/jobs_controller.rb
@@ -61,11 +65,25 @@ files:
61
65
  - app/helpers/solid_observer/application_helper.rb
62
66
  - app/helpers/solid_observer/dashboard_helper.rb
63
67
  - app/jobs/solid_observer/cleanup_job.rb
68
+ - app/models/solid_observer/cable_event.rb
69
+ - app/models/solid_observer/cable_metric.rb
70
+ - app/models/solid_observer/cache_event.rb
71
+ - app/models/solid_observer/cache_metric.rb
64
72
  - app/models/solid_observer/queue_event.rb
65
73
  - app/models/solid_observer/queue_metric.rb
66
74
  - app/models/solid_observer/storage_info.rb
67
75
  - app/presenters/solid_observer/execution_presenter.rb
68
76
  - app/views/layouts/solid_observer/application.html.erb
77
+ - app/views/solid_observer/cable_dashboard/_charts.html.erb
78
+ - app/views/solid_observer/cable_dashboard/_recent_events.html.erb
79
+ - app/views/solid_observer/cable_dashboard/_summary.html.erb
80
+ - app/views/solid_observer/cable_dashboard/index.html.erb
81
+ - app/views/solid_observer/cache_dashboard/_charts.html.erb
82
+ - app/views/solid_observer/cache_dashboard/_recent_events.html.erb
83
+ - app/views/solid_observer/cache_dashboard/_summary.html.erb
84
+ - app/views/solid_observer/cache_dashboard/index.html.erb
85
+ - app/views/solid_observer/cache_operations/_confirm_clear.html.erb
86
+ - app/views/solid_observer/cache_operations/index.html.erb
69
87
  - app/views/solid_observer/dashboard/_chart.html.erb
70
88
  - app/views/solid_observer/dashboard/_live_state.html.erb
71
89
  - app/views/solid_observer/dashboard/_queue_table.html.erb
@@ -81,19 +99,29 @@ files:
81
99
  - app/views/solid_observer/shared/_pagination.html.erb
82
100
  - app/views/solid_observer/shared/_stat_card.html.erb
83
101
  - app/views/solid_observer/storages/show.html.erb
84
- - bin/console
85
- - bin/quality_gate
86
- - bin/setup
87
102
  - config/routes.rb
88
103
  - db/migrate/20260115000001_create_solid_observer_queue_events.rb
89
104
  - db/migrate/20260115000002_create_solid_observer_metrics.rb
90
105
  - db/migrate/20260115000003_create_solid_observer_storage_info.rb
91
106
  - db/migrate/20260424000001_add_composite_indexes_to_queue_events.rb
107
+ - db/migrate/20260601000001_create_solid_observer_cache_events.rb
108
+ - db/migrate/20260601000002_create_solid_observer_cache_metrics.rb
109
+ - db/migrate/20260602000001_add_component_to_solid_observer_storage_infos.rb
110
+ - db/migrate/20260612000001_add_event_type_recorded_at_index_to_cache_events.rb
111
+ - db/migrate/20260619000001_create_solid_observer_cable_events.rb
112
+ - db/migrate/20260619000002_create_solid_observer_cable_metrics.rb
92
113
  - lib/generators/solid_observer/install_generator.rb
93
114
  - lib/generators/solid_observer/templates/initializer.rb.tt
94
115
  - lib/solid_observer.rb
95
116
  - lib/solid_observer/base_event.rb
96
117
  - lib/solid_observer/base_metric.rb
118
+ - lib/solid_observer/base_record.rb
119
+ - lib/solid_observer/cable_event_buffer.rb
120
+ - lib/solid_observer/cable_metric_buffer.rb
121
+ - lib/solid_observer/cable_subscriber.rb
122
+ - lib/solid_observer/cache_event_buffer.rb
123
+ - lib/solid_observer/cache_metric_buffer.rb
124
+ - lib/solid_observer/cache_subscriber.rb
97
125
  - lib/solid_observer/chart_buffer.rb
98
126
  - lib/solid_observer/cli/base.rb
99
127
  - lib/solid_observer/cli/jobs.rb
@@ -102,6 +130,7 @@ files:
102
130
  - lib/solid_observer/configuration.rb
103
131
  - lib/solid_observer/correlation_id_resolver.rb
104
132
  - lib/solid_observer/engine.rb
133
+ - lib/solid_observer/event_buffer_core.rb
105
134
  - lib/solid_observer/params/events_filter.rb
106
135
  - lib/solid_observer/params/jobs_filter.rb
107
136
  - lib/solid_observer/queries/events_query.rb
@@ -109,11 +138,24 @@ files:
109
138
  - lib/solid_observer/queries/job_executions_query.rb
110
139
  - lib/solid_observer/queue_event_buffer.rb
111
140
  - lib/solid_observer/queue_stats.rb
141
+ - lib/solid_observer/services/cable_operations.rb
142
+ - lib/solid_observer/services/cable_stats.rb
143
+ - lib/solid_observer/services/cache_operations.rb
144
+ - lib/solid_observer/services/cache_stats.rb
112
145
  - lib/solid_observer/services/cleanup_storage.rb
113
146
  - lib/solid_observer/services/database_size.rb
147
+ - lib/solid_observer/services/flush_cable_event_buffer.rb
148
+ - lib/solid_observer/services/flush_cable_metrics.rb
149
+ - lib/solid_observer/services/flush_cache_event_buffer.rb
150
+ - lib/solid_observer/services/flush_cache_metrics.rb
114
151
  - lib/solid_observer/services/flush_event_buffer.rb
115
152
  - lib/solid_observer/services/install_migrations.rb
153
+ - lib/solid_observer/services/record_cable_event.rb
154
+ - lib/solid_observer/services/record_cable_metric.rb
155
+ - lib/solid_observer/services/record_cache_event.rb
156
+ - lib/solid_observer/services/record_cache_metric.rb
116
157
  - lib/solid_observer/services/record_event.rb
158
+ - lib/solid_observer/services/storage_info_snapshot.rb
117
159
  - lib/solid_observer/services/ui_auth_check.rb
118
160
  - lib/solid_observer/subscriber.rb
119
161
  - lib/solid_observer/version.rb
@@ -141,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
183
  - !ruby/object:Gem::Version
142
184
  version: '0'
143
185
  requirements: []
144
- rubygems_version: 4.0.3
186
+ rubygems_version: 4.0.6
145
187
  specification_version: 4
146
188
  summary: Observability for the Rails 8's Solid Stack
147
189
  test_files: []
data/bin/console DELETED
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "solid_observer"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- require "irb"
11
- IRB.start(__FILE__)