rails_pulse 0.2.5.pre.4 → 0.2.5.pre.5

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: 35e4f8f6ee667606954a3948b27bcdfacbeeb7b501886123d9f9a1450f989626
4
- data.tar.gz: 57180a1906a9e80c996d971ac4162e95d2a5285179aa538df025172e8e6031a6
3
+ metadata.gz: 8b1d65c3afe59858af712a994ef9310c0851485cc6a2c09234fcf0b84d63179c
4
+ data.tar.gz: f5a6e525c6d605b6fa0edc882c8ea7fc672ea5b4bfd803bfbee1508d2c9ff9ec
5
5
  SHA512:
6
- metadata.gz: a9053fe88993c9b02487179e011267178a6a6db8a7ad2765943d451cdb160538311453193ff51e8348a2b9027970fc3593d39094cbd929330f9a3395f3897a2d
7
- data.tar.gz: 54c8b8e5de3c89fee04746edc777ef3db8f794ec08a2b615d078e663a03738b7e741fcadd611a9376759c8a7173b87b7d33644896de575a80239bda21aa8ebbf
6
+ metadata.gz: 6d391f448da6724d1e095df6534753febd8b696c62db69de9c75618e891889f9a1b8ae7b6dbcf983cf8caa033901023e4c16f868aa7896c62b90ae901388a5b3
7
+ data.tar.gz: e1f190bbf54846a915f875e2381ae5890c4ff9d97baccf9441d4cdf026ccbd55c36d6e2b8b987dc878376d42643884d37b0d8afa4ef713dae7c3cebd19de6f25
data/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
  **Real-time performance monitoring and debugging for Rails applications**
7
7
 
8
8
  ![Gem Version](https://img.shields.io/gem/v/rails_pulse)
9
- ![Rails Version](https://img.shields.io/badge/Rails-7.2+-blue)
9
+ ![Rails Version](https://img.shields.io/badge/Rails-7.2+-8.1-blue)
10
10
  ![License](https://img.shields.io/badge/License-MIT-green)
11
- ![Ruby Version](https://img.shields.io/badge/Ruby-3.3+-red)
11
+ ![Ruby Version](https://img.shields.io/badge/Ruby-3.0+-red)
12
12
  </div>
13
13
 
14
14
  ---
@@ -786,7 +786,7 @@ rails test:matrix
786
786
 
787
787
  This command tests all combinations locally:
788
788
  - **Databases**: SQLite3, PostgreSQL, MySQL2 (local testing only)
789
- - **Rails versions**: 7.2, 8.0
789
+ - **Rails versions**: 7.2, 8.0, 8.1
790
790
 
791
791
  **Note**: CI only tests SQLite3 + PostgreSQL for reliability. MySQL is available for local testing but excluded from CI due to flakiness.
792
792
 
@@ -840,7 +840,7 @@ DB=mysql2 FORCE_DB_CONFIG=true rails test:all
840
840
 
841
841
  GitHub Actions CI automatically tests:
842
842
  - **Databases**: SQLite3, PostgreSQL only (MySQL excluded for reliability)
843
- - **Rails versions**: 7.2, 8.0
843
+ - **Rails versions**: 7.2, 8.0, 8.1
844
844
  - **Environment**: Uses memory SQLite and PostgreSQL service
845
845
 
846
846
  **Local vs CI differences**:
data/Rakefile CHANGED
@@ -189,7 +189,7 @@ end
189
189
  desc "Test all database and Rails version combinations"
190
190
  task :test_matrix do
191
191
  databases = %w[sqlite3 postgresql mysql2]
192
- rails_versions = %w[rails-7-2 rails-8-0]
192
+ rails_versions = %w[rails-7-2 rails-8-0 rails-8-1]
193
193
 
194
194
  failed_combinations = []
195
195
  total_combinations = databases.size * rails_versions.size
@@ -33,17 +33,21 @@ module ZoomRangeConcern
33
33
  private
34
34
 
35
35
  def normalize_column_time(column_time, main_start_time, main_end_time)
36
+ # Convert from JavaScript milliseconds to Unix seconds
37
+ # Chart data uses milliseconds (timestamp * 1000), so divide by 1000 to get seconds
38
+ column_time_seconds = column_time / 1000
39
+
36
40
  # Determine period type based on main time range (same logic as ChartTableConcern)
37
41
  time_diff_hours = (main_end_time - main_start_time) / 3600.0
38
42
 
39
43
  if time_diff_hours <= 25
40
44
  # Hourly period - normalize to beginning/end of hour
41
- column_time_obj = Time.zone&.at(column_time) || Time.at(column_time)
45
+ column_time_obj = Time.zone&.at(column_time_seconds) || Time.at(column_time_seconds)
42
46
  start_time = column_time_obj&.beginning_of_hour || column_time_obj
43
47
  end_time = column_time_obj&.end_of_hour || column_time_obj
44
48
  else
45
49
  # Daily period - normalize to beginning/end of day
46
- column_time_obj = Time.zone&.at(column_time) || Time.at(column_time)
50
+ column_time_obj = Time.zone&.at(column_time_seconds) || Time.at(column_time_seconds)
47
51
  start_time = column_time_obj&.beginning_of_day || column_time_obj
48
52
  end_time = column_time_obj&.end_of_day || column_time_obj
49
53
  end
@@ -52,16 +56,21 @@ module ZoomRangeConcern
52
56
  end
53
57
 
54
58
  def normalize_zoom_times(start_time, end_time)
55
- time_diff = (end_time - start_time) / 3600.0
59
+ # Convert from JavaScript milliseconds to Unix seconds
60
+ # Chart data uses milliseconds (timestamp * 1000), so divide by 1000 to get seconds
61
+ start_time_seconds = start_time / 1000
62
+ end_time_seconds = end_time / 1000
63
+
64
+ time_diff = (end_time_seconds - start_time_seconds) / 3600.0
56
65
 
57
66
  if time_diff <= 25
58
- start_time_obj = Time.zone&.at(start_time) || Time.at(start_time)
59
- end_time_obj = Time.zone&.at(end_time) || Time.at(end_time)
67
+ start_time_obj = Time.zone&.at(start_time_seconds) || Time.at(start_time_seconds)
68
+ end_time_obj = Time.zone&.at(end_time_seconds) || Time.at(end_time_seconds)
60
69
  start_time = start_time_obj&.beginning_of_hour || start_time_obj
61
70
  end_time = end_time_obj&.end_of_hour || end_time_obj
62
71
  else
63
- start_time_obj = Time.zone&.at(start_time) || Time.at(start_time)
64
- end_time_obj = Time.zone&.at(end_time) || Time.at(end_time)
72
+ start_time_obj = Time.zone&.at(start_time_seconds) || Time.at(start_time_seconds)
73
+ end_time_obj = Time.zone&.at(end_time_seconds) || Time.at(end_time_seconds)
65
74
  start_time = start_time_obj&.beginning_of_day || start_time_obj
66
75
  end_time = end_time_obj&.end_of_day || end_time_obj
67
76
  end
@@ -4,14 +4,14 @@ module RailsPulse
4
4
  if time_diff_hours <= 25
5
5
  <<~JS
6
6
  function(value) {
7
- const date = new Date(value * 1000);
7
+ const date = new Date(value);
8
8
  return date.getHours().toString().padStart(2, '0') + ':00';
9
9
  }
10
10
  JS
11
11
  else
12
12
  <<~JS
13
13
  function(value) {
14
- const date = new Date(value * 1000);
14
+ const date = new Date(value);
15
15
  return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
16
16
  }
17
17
  JS
@@ -23,7 +23,7 @@ module RailsPulse
23
23
  <<~JS
24
24
  function(params) {
25
25
  const data = params[0];
26
- const date = new Date(data.axisValue * 1000);
26
+ const date = new Date(data.axisValue);
27
27
  const dateString = date.getHours().toString().padStart(2, '0') + ':00';
28
28
  return `${dateString} <br /> ${data.marker} ${parseInt(data.data)} ms`;
29
29
  }
@@ -32,7 +32,7 @@ module RailsPulse
32
32
  <<~JS
33
33
  function(params) {
34
34
  const data = params[0];
35
- const date = new Date(data.axisValue * 1000);
35
+ const date = new Date(data.axisValue);
36
36
  const dateString = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
37
37
  return `${dateString} <br /> ${data.marker} ${parseInt(data.data)} ms`;
38
38
  }
@@ -213,20 +213,35 @@ export default class extends Controller {
213
213
  return value
214
214
  },
215
215
 
216
- // Date only
216
+ // Date only (formatted as "Mon DD" to match Rails Pulse formatters)
217
217
  'date': (value) => {
218
218
  if (typeof value === 'number' || typeof value === 'string') {
219
- const date = new Date(value)
220
- return date.toLocaleDateString()
219
+ // Convert to number if string
220
+ const numValue = typeof value === 'string' ? parseInt(value) : value
221
+ const date = new Date(numValue)
222
+
223
+ if (isNaN(date.getTime())) {
224
+ return value.toString()
225
+ }
226
+
227
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
221
228
  }
222
229
  return value
223
230
  },
224
231
 
225
- // Time only
232
+ // Time only (formatted as "HH:00" to match Rails Pulse formatters)
226
233
  'time': (value) => {
227
234
  if (typeof value === 'number' || typeof value === 'string') {
228
- const date = new Date(value)
229
- return date.toLocaleTimeString()
235
+ // Convert to number if string
236
+ const numValue = typeof value === 'string' ? parseInt(value) : value
237
+ const date = new Date(numValue)
238
+
239
+ const hours = date.getHours()
240
+ if (isNaN(hours)) {
241
+ return value.toString()
242
+ }
243
+
244
+ return hours.toString().padStart(2, '0') + ':00'
230
245
  }
231
246
  return value
232
247
  },
@@ -248,33 +263,41 @@ export default class extends Controller {
248
263
  }
249
264
  }
250
265
 
251
- // Try to match the formatter string to a known safe pattern
252
- for (const [key, formatter] of Object.entries(SAFE_FORMATTERS)) {
253
- if (formatterString.includes(key) ||
254
- formatterString.includes(key.replace('_', ''))) {
255
- return formatter
256
- }
266
+ // IMPORTANT: Check for SPECIFIC patterns FIRST before generic keyword matching
267
+ // The order matters! More specific patterns should be checked before generic ones.
268
+
269
+ // Check for specific function calls that uniquely identify the formatter type
270
+ if (formatterString.includes('getHours')) {
271
+ return SAFE_FORMATTERS.time
257
272
  }
258
273
 
259
- // Check for specific safe patterns in the function string
260
- if (formatterString.includes('toFixed(2)') && formatterString.includes('ms')) {
261
- return SAFE_FORMATTERS.duration_ms
274
+ if (formatterString.includes('toLocaleDateString')) {
275
+ return SAFE_FORMATTERS.date
276
+ }
277
+
278
+ if (formatterString.includes('toLocaleTimeString')) {
279
+ return SAFE_FORMATTERS.time
262
280
  }
263
281
 
264
282
  if (formatterString.includes('toLocaleString')) {
265
283
  return SAFE_FORMATTERS.number_delimited
266
284
  }
267
285
 
268
- if (formatterString.includes('toLocaleDateString')) {
269
- return SAFE_FORMATTERS.date
286
+ if (formatterString.includes('toFixed(2)') && formatterString.includes('ms')) {
287
+ return SAFE_FORMATTERS.duration_ms
270
288
  }
271
289
 
272
- if (formatterString.includes('toLocaleTimeString')) {
273
- return SAFE_FORMATTERS.time
290
+ // Try to match the formatter string to a known safe pattern by key name
291
+ // This is less specific and should come after the function call checks above
292
+ for (const [key, formatter] of Object.entries(SAFE_FORMATTERS)) {
293
+ if (formatterString.includes(key) ||
294
+ formatterString.includes(key.replace('_', ''))) {
295
+ return formatter
296
+ }
274
297
  }
275
298
 
276
299
  // Default: return a safe identity function that just returns the value
277
- console.warn('[RailsPulse] Unknown formatter pattern, using identity function:', formatterString)
300
+ console.warn('[RailsPulse] Unknown formatter pattern, using identity function:', formatterString.substring(0, 100))
278
301
  return (value) => value
279
302
  }
280
303
 
@@ -24,10 +24,12 @@ module RailsPulse
24
24
  .transform_keys(&:to_i)
25
25
 
26
26
  # Pad missing data points with zeros
27
+ # Convert timestamps to milliseconds for JavaScript Date compatibility
27
28
  step = @period_type == :hour ? 1.hour : 1.day
28
29
  data = {}
29
30
  (@start_time.to_i..@end_time.to_i).step(step) do |timestamp|
30
- data[timestamp.to_i] = summaries[timestamp.to_i].to_f.round(2)
31
+ # Multiply by 1000 to convert Unix seconds to JavaScript milliseconds
32
+ data[timestamp.to_i * 1000] = summaries[timestamp.to_i].to_f.round(2)
31
33
  end
32
34
  data
33
35
  end
@@ -29,10 +29,12 @@ module RailsPulse
29
29
  .transform_keys(&:to_i)
30
30
 
31
31
  # Pad missing data points with zeros
32
+ # Convert timestamps to milliseconds for JavaScript Date compatibility
32
33
  step = @period_type == :hour ? 1.hour : 1.day
33
34
  data = {}
34
35
  (@start_time.to_i..@end_time.to_i).step(step) do |timestamp|
35
- data[timestamp.to_i] = summaries[timestamp.to_i].to_f.round(2)
36
+ # Multiply by 1000 to convert Unix seconds to JavaScript milliseconds
37
+ data[timestamp.to_i * 1000] = summaries[timestamp.to_i].to_f.round(2)
36
38
  end
37
39
  data
38
40
  end
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  <% end %>
28
28
 
29
- <% if @has_data %>
29
+ <% unless turbo_frame_request? %>
30
30
  <% if @chart_data && @chart_data.values.any? { |v| v > 0 } %>
31
31
  <div
32
32
  class="chart-container chart-container--slim"
@@ -52,14 +52,16 @@
52
52
  ) %>
53
53
  </div>
54
54
  <% end %>
55
+ <% end %>
55
56
 
56
- <%= turbo_frame_tag :index_table, data: { rails_pulse__index_target: "indexTable" } do %>
57
+ <%= turbo_frame_tag :index_table, data: { rails_pulse__index_target: "indexTable" } do %>
58
+ <% if @table_data && @table_data.any? %>
57
59
  <%= render 'rails_pulse/queries/show_table' %>
60
+ <% else %>
61
+ <%= render 'rails_pulse/components/empty_state',
62
+ title: 'No query responses found for the selected filters.',
63
+ description: 'Try adjusting your time range or filters to see results.' %>
58
64
  <% end %>
59
- <% else %>
60
- <%= render 'rails_pulse/components/empty_state',
61
- title: 'No query responses found for the selected filters.',
62
- description: 'Try adjusting your time range or filters to see results.' %>
63
65
  <% end %>
64
66
  <% end %>
65
67
  </div>
@@ -29,7 +29,7 @@
29
29
  </div>
30
30
  <% end %>
31
31
 
32
- <% if @has_data %>
32
+ <% unless turbo_frame_request? %>
33
33
  <% if @chart_data && @chart_data.values.any? { |v| v > 0 } %>
34
34
  <div
35
35
  class="chart-container chart-container--slim"
@@ -55,14 +55,16 @@
55
55
  ) %>
56
56
  </div>
57
57
  <% end %>
58
+ <% end %>
58
59
 
59
- <%= turbo_frame_tag :index_table, data: { rails_pulse__index_target: "indexTable" } do %>
60
+ <%= turbo_frame_tag :index_table, data: { rails_pulse__index_target: "indexTable" } do %>
61
+ <% if @table_data && @table_data.any? %>
60
62
  <%= render 'rails_pulse/routes/requests_table' %>
63
+ <% else %>
64
+ <%= render 'rails_pulse/components/empty_state',
65
+ title: 'No route requests found for the selected filters.',
66
+ description: 'Try adjusting your time range or filters to see results.' %>
61
67
  <% end %>
62
- <% else %>
63
- <%= render 'rails_pulse/components/empty_state',
64
- title: 'No route requests found for the selected filters.',
65
- description: 'Try adjusting your time range or filters to see results.' %>
66
68
  <% end %>
67
69
  <% end %>
68
70
  </div>
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ class OptimizeRailsPulseIndexes < ActiveRecord::Migration[7.0]
4
+ def up
5
+ # Remove redundant indexes that are covered by composite indexes
6
+ # These were identified by PgHero as being redundant
7
+
8
+ # Operations table - remove 3 redundant indexes
9
+ if index_exists?(:rails_pulse_operations, :created_at, name: "idx_operations_created_at")
10
+ remove_index :rails_pulse_operations, :created_at, name: :idx_operations_created_at, **index_options
11
+ end
12
+
13
+ if index_exists?(:rails_pulse_operations, :occurred_at, name: "index_rails_pulse_operations_on_occurred_at")
14
+ remove_index :rails_pulse_operations, :occurred_at, name: :index_rails_pulse_operations_on_occurred_at, **index_options
15
+ end
16
+
17
+ if index_exists?(:rails_pulse_operations, :query_id, name: "index_rails_pulse_operations_on_query_id")
18
+ remove_index :rails_pulse_operations, :query_id, name: :index_rails_pulse_operations_on_query_id, **index_options
19
+ end
20
+
21
+ # Requests table - remove 2 redundant indexes
22
+ if index_exists?(:rails_pulse_requests, :created_at, name: "idx_requests_created_at")
23
+ remove_index :rails_pulse_requests, :created_at, name: :idx_requests_created_at, **index_options
24
+ end
25
+
26
+ if index_exists?(:rails_pulse_requests, :route_id, name: "index_rails_pulse_requests_on_route_id")
27
+ remove_index :rails_pulse_requests, :route_id, name: :index_rails_pulse_requests_on_route_id, **index_options
28
+ end
29
+
30
+ # Summaries table - remove 1 redundant index
31
+ if index_exists?(:rails_pulse_summaries, [ :summarizable_type, :summarizable_id ], name: "index_rails_pulse_summaries_on_summarizable")
32
+ remove_index :rails_pulse_summaries, [ :summarizable_type, :summarizable_id ], name: :index_rails_pulse_summaries_on_summarizable, **index_options
33
+ end
34
+
35
+ # Add missing indexes for better query performance
36
+ unless index_exists?(:rails_pulse_summaries, :summarizable_id, name: "index_rails_pulse_summaries_on_summarizable_id")
37
+ add_index :rails_pulse_summaries, :summarizable_id, name: :index_rails_pulse_summaries_on_summarizable_id, **index_options
38
+ end
39
+
40
+ unless index_exists?(:rails_pulse_routes, :path, name: "index_rails_pulse_routes_on_path")
41
+ add_index :rails_pulse_routes, :path, name: :index_rails_pulse_routes_on_path, **index_options
42
+ end
43
+
44
+ unless index_exists?(:rails_pulse_summaries, :period_start, name: "index_rails_pulse_summaries_on_period_start")
45
+ add_index :rails_pulse_summaries, :period_start, name: :index_rails_pulse_summaries_on_period_start, **index_options
46
+ end
47
+ end
48
+
49
+ def down
50
+ # Restore the removed indexes
51
+ unless index_exists?(:rails_pulse_operations, :created_at, name: "idx_operations_created_at")
52
+ add_index :rails_pulse_operations, :created_at, name: :idx_operations_created_at, **index_options
53
+ end
54
+
55
+ unless index_exists?(:rails_pulse_operations, :occurred_at, name: "index_rails_pulse_operations_on_occurred_at")
56
+ add_index :rails_pulse_operations, :occurred_at, name: :index_rails_pulse_operations_on_occurred_at, **index_options
57
+ end
58
+
59
+ unless index_exists?(:rails_pulse_operations, :query_id, name: "index_rails_pulse_operations_on_query_id")
60
+ add_index :rails_pulse_operations, :query_id, name: :index_rails_pulse_operations_on_query_id, **index_options
61
+ end
62
+
63
+ unless index_exists?(:rails_pulse_requests, :created_at, name: "idx_requests_created_at")
64
+ add_index :rails_pulse_requests, :created_at, name: :idx_requests_created_at, **index_options
65
+ end
66
+
67
+ unless index_exists?(:rails_pulse_requests, :route_id, name: "index_rails_pulse_requests_on_route_id")
68
+ add_index :rails_pulse_requests, :route_id, name: :index_rails_pulse_requests_on_route_id, **index_options
69
+ end
70
+
71
+ unless index_exists?(:rails_pulse_summaries, [ :summarizable_type, :summarizable_id ], name: "index_rails_pulse_summaries_on_summarizable")
72
+ add_index :rails_pulse_summaries, [ :summarizable_type, :summarizable_id ], name: :index_rails_pulse_summaries_on_summarizable, **index_options
73
+ end
74
+
75
+ # Remove the added indexes
76
+ if index_exists?(:rails_pulse_summaries, :summarizable_id, name: "index_rails_pulse_summaries_on_summarizable_id")
77
+ remove_index :rails_pulse_summaries, :summarizable_id, name: :index_rails_pulse_summaries_on_summarizable_id, **index_options
78
+ end
79
+
80
+ if index_exists?(:rails_pulse_routes, :path, name: "index_rails_pulse_routes_on_path")
81
+ remove_index :rails_pulse_routes, :path, name: :index_rails_pulse_routes_on_path, **index_options
82
+ end
83
+
84
+ if index_exists?(:rails_pulse_summaries, :period_start, name: "index_rails_pulse_summaries_on_period_start")
85
+ remove_index :rails_pulse_summaries, :period_start, name: :index_rails_pulse_summaries_on_period_start, **index_options
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def index_options
92
+ # Use concurrent indexing for PostgreSQL, standard for others
93
+ if postgresql?
94
+ { algorithm: :concurrently }
95
+ else
96
+ {}
97
+ end
98
+ end
99
+
100
+ def postgresql?
101
+ connection.adapter_name.downcase.include?("postgres")
102
+ end
103
+ end
@@ -35,6 +35,7 @@ RailsPulse::Schema = lambda do |connection|
35
35
  end
36
36
 
37
37
  connection.add_index :rails_pulse_routes, [ :method, :path ], unique: true, name: "index_rails_pulse_routes_on_method_and_path"
38
+ connection.add_index :rails_pulse_routes, :path, name: "index_rails_pulse_routes_on_path"
38
39
  end
39
40
 
40
41
  unless connection.table_exists?(:rails_pulse_queries)
@@ -121,7 +122,7 @@ RailsPulse::Schema = lambda do |connection|
121
122
  connection.create_table :rails_pulse_operations do |t|
122
123
  t.references :request, null: true, foreign_key: { to_table: :rails_pulse_requests }, comment: "Link to the request"
123
124
  t.references :job_run, null: true, foreign_key: { to_table: :rails_pulse_job_runs }, comment: "Link to a background job execution"
124
- t.references :query, foreign_key: { to_table: :rails_pulse_queries }, index: true, comment: "Link to the normalized SQL query"
125
+ t.references :query, foreign_key: { to_table: :rails_pulse_queries }, index: false, comment: "Link to the normalized SQL query"
125
126
  t.string :operation_type, null: false, comment: "Type of operation (e.g., database, view, gem_call)"
126
127
  t.string :label, null: false, comment: "Descriptive name (e.g., SELECT FROM users WHERE id = 1, render layout)"
127
128
  t.decimal :duration, precision: 15, scale: 6, null: false, comment: "Operation duration in milliseconds"
@@ -132,7 +133,6 @@ RailsPulse::Schema = lambda do |connection|
132
133
  end
133
134
 
134
135
  connection.add_index :rails_pulse_operations, :operation_type, name: "index_rails_pulse_operations_on_operation_type"
135
- connection.add_index :rails_pulse_operations, :occurred_at, name: "index_rails_pulse_operations_on_occurred_at"
136
136
  connection.add_index :rails_pulse_operations, [ :query_id, :occurred_at ], name: "index_rails_pulse_operations_on_query_and_time"
137
137
  connection.add_index :rails_pulse_operations, [ :query_id, :duration, :occurred_at ], name: "index_rails_pulse_operations_query_performance"
138
138
  connection.add_index :rails_pulse_operations, [ :occurred_at, :duration, :operation_type ], name: "index_rails_pulse_operations_on_time_duration_type"
@@ -184,6 +184,8 @@ RailsPulse::Schema = lambda do |connection|
184
184
  name: "idx_pulse_summaries_unique"
185
185
  connection.add_index :rails_pulse_summaries, [ :period_type, :period_start ], name: "index_rails_pulse_summaries_on_period"
186
186
  connection.add_index :rails_pulse_summaries, :created_at, name: "index_rails_pulse_summaries_on_created_at"
187
+ connection.add_index :rails_pulse_summaries, :summarizable_id, name: "index_rails_pulse_summaries_on_summarizable_id"
188
+ connection.add_index :rails_pulse_summaries, :period_start, name: "index_rails_pulse_summaries_on_period_start"
187
189
  end
188
190
 
189
191
  # Add indexes to existing tables for efficient aggregation
@@ -191,18 +193,10 @@ RailsPulse::Schema = lambda do |connection|
191
193
  connection.add_index :rails_pulse_requests, [ :created_at, :route_id ], name: "idx_requests_for_aggregation"
192
194
  end
193
195
 
194
- unless connection.index_exists?(:rails_pulse_requests, :created_at, name: "idx_requests_created_at")
195
- connection.add_index :rails_pulse_requests, :created_at, name: "idx_requests_created_at"
196
- end
197
-
198
196
  unless connection.index_exists?(:rails_pulse_operations, [ :created_at, :query_id ], name: "idx_operations_for_aggregation")
199
197
  connection.add_index :rails_pulse_operations, [ :created_at, :query_id ], name: "idx_operations_for_aggregation"
200
198
  end
201
199
 
202
- unless connection.index_exists?(:rails_pulse_operations, :created_at, name: "idx_operations_created_at")
203
- connection.add_index :rails_pulse_operations, :created_at, name: "idx_operations_created_at"
204
- end
205
-
206
200
  # Log successful creation
207
201
  created_tables = required_tables.select { |table| connection.table_exists?(table) }
208
202
  newly_created = created_tables - existing_tables
@@ -173,8 +173,7 @@ module RailsPulse
173
173
  say "\nMigrations copied successfully!", :green
174
174
  say "\nNext steps:", :green
175
175
  say "1. Run migrations for the rails_pulse database:"
176
- say " rails db:migrate (will run migrations for all databases)"
177
- say " OR manually run the migration files in db/rails_pulse_migrate/"
176
+ say " rails db:migrate:rails_pulse"
178
177
  say "2. Restart your Rails server"
179
178
  else
180
179
  # Fall back to detecting missing columns
@@ -203,8 +202,7 @@ module RailsPulse
203
202
 
204
203
  Next steps:
205
204
  1. Run migrations for the rails_pulse database:
206
- rails db:migrate (will run migrations for all databases)
207
- OR manually run the migration files in db/rails_pulse_migrate/
205
+ rails db:migrate:rails_pulse
208
206
  2. Restart your Rails server
209
207
 
210
208
  This migration will add: #{missing_columns.keys.join(', ')}
@@ -31,8 +31,8 @@ module RailsPulse
31
31
  return result
32
32
  end
33
33
 
34
- # Clear any previous request data
35
- RequestStore.store[:rails_pulse_request_id] = nil
34
+ # Clear any previous request data and set a placeholder ID
35
+ RequestStore.store[:rails_pulse_request_id] = SecureRandom.uuid
36
36
  RequestStore.store[:rails_pulse_operations] = []
37
37
 
38
38
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -1,3 +1,3 @@
1
1
  module RailsPulse
2
- VERSION = "0.2.5.pre.4"
2
+ VERSION = "0.2.5.pre.5"
3
3
  end