railwatch 0.8.4 → 0.8.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: f52b89a567e0ac0a05281db9b77499961ee7abf9cbfbf663070600d8885818a3
4
- data.tar.gz: 05cdb2e7d19bf73edb2dd80550b1a59bf39c83ad24b951c7f7d43b3ddee9a850
3
+ metadata.gz: ec770e9d790b50b269a6bfde1e37d453ff6deb312216ab05d8106a50bb72d9b2
4
+ data.tar.gz: 9b9bff09f0821fefd39ae31d7c2ac826b3a6044d899aaf53a4e8e712a093d8f2
5
5
  SHA512:
6
- metadata.gz: d3d4c4f9698de7d6f522d4c171962e959efccd4435ff07e036505ced415aaa43e4b134df949f18c61952c4b22738d7074f49afcc1209cf1088fe41ea6cae564b
7
- data.tar.gz: 8f4c96092fc3a62f3538667c4d4f2e40de420e9f2ccacf7305b0b4b4fd99ca9b098cb830cced5b9def8a618d3d1e16e24e478d4461f2599e0047121b79e2df2e
6
+ metadata.gz: eb2fc4d6a744ca775895c0bbcb61b15321df7c3be1669c42c8e83b36563ff76fdd2c4aad2308af5eeeaa17bba68833e61a8e7c9c726adcc5b0199b502ee68c5b
7
+ data.tar.gz: e0685c8d6b3b9564728239e1cb6978e4eaaf888797fdf000deff1fbfcf642849424baca6e11d0d2b2d58afc40cebe23d20b14415e802835fe294dc5c2aa55e41
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@
6
6
  filled in by the release commit, which is also the only commit that
7
7
  touches lib/railwatch/version.rb and Gemfile.lock. See CONTRIBUTING.md. -->
8
8
 
9
+ ## 0.8.5 (2026-09-25)
10
+
11
+ - A new migration adds a partial covering index for the per-tenant sums
12
+ on the Tenants page and in MCP's `list_tenants`. Over 30 days of
13
+ rebulk-system's 358,000 tagged requests they took 8.1 s, reading every
14
+ row from the table; they now take 131 ms. It holds only tagged rows,
15
+ so an app without tenants gets an empty index. It took 1.6 s to build
16
+ on a 16 GB file.
17
+
9
18
  ## 0.8.4 (2026-09-23)
10
19
 
11
20
  - A `class:` filter for a job class with many attempts in the window
@@ -120,8 +120,18 @@ module Railwatch
120
120
 
121
121
  # -- Aggregation steps -----------------------------------------------------
122
122
 
123
+ # The per-tenant sums read through idx_executions_tenant_summary, which
124
+ # holds every column they need for tagged rows only. SQLite prefers the
125
+ # narrower (app_tenant, occurred_at) index on its own estimate and then
126
+ # fetches every row from the table (8 s against 131 ms over 30 days).
127
+ def self.summary_scope(kind)
128
+ scope = Telemetry::Execution
129
+ scope = scope.from("#{scope.quoted_table_name} INDEXED BY idx_executions_tenant_summary") if TelemetryRecord.sqlite?
130
+ scope.where(kind: kind).where.not(app_tenant: nil)
131
+ end
132
+
123
133
  def self.absorb_requests(rows, from, to, q)
124
- filtered(Telemetry::Execution.requests.between(from, to), q).group(:app_tenant)
134
+ filtered(summary_scope("request").between(from, to), q).group(:app_tenant)
125
135
  .pluck(:app_tenant, COUNT_SQL, ERRORS_SQL, Arel.sql("AVG(duration)"), Arel.sql("MAX(duration)"), USERS_SQL)
126
136
  .each do |tenant, count, errors, avg, max, users|
127
137
  rows[tenant].merge!(requests: count, errors: errors, avg: ms(avg), max: ms(max), users: users)
@@ -129,7 +139,7 @@ module Railwatch
129
139
  end
130
140
 
131
141
  def self.absorb_jobs(rows, from, to, q)
132
- filtered(Telemetry::Execution.jobs.between(from, to), q).group(:app_tenant)
142
+ filtered(summary_scope("job_attempt").between(from, to), q).group(:app_tenant)
133
143
  .pluck(:app_tenant, COUNT_SQL, FAILED_SQL)
134
144
  .each { |tenant, count, failed| rows[tenant].merge!(jobs: count, failed_jobs: failed) }
135
145
  end
@@ -139,7 +149,7 @@ module Railwatch
139
149
  end
140
150
 
141
151
  def self.absorb_last_seen(rows, from, to, q)
142
- filtered(Telemetry::Execution.between(from, to), q).group(:app_tenant).maximum(:occurred_at)
152
+ filtered(summary_scope(Telemetry::Execution::KINDS).between(from, to), q).group(:app_tenant).maximum(:occurred_at)
143
153
  .each { |tenant, at| rows[tenant][:last_seen_at] = at }
144
154
  end
145
155
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Tenants page and MCP's list_tenants sum every tenant-tagged request
4
+ # and job in the window per tenant. Through (app_tenant, occurred_at)
5
+ # SQLite finds the rows but fetches each from the table for kind, status,
6
+ # outcome, duration and user_ref: 8.1 s over 30 days on rebulk-system
7
+ # (358,000 tagged requests), and 24 s once in production, which held the
8
+ # web process -- and every ingest request queued behind it -- the whole
9
+ # time. This index carries every column those aggregates read, only for
10
+ # tagged rows: 131 ms for the same query, 41 MB, 1.6 s to build on a copy
11
+ # of that 16 GB file. An untagged app gets an empty index.
12
+ class AddTenantSummaryIndex < ActiveRecord::Migration[8.1]
13
+ def change
14
+ add_index :executions, [ :kind, :app_tenant, :occurred_at, :status, :outcome, :duration, :user_ref ],
15
+ name: "idx_executions_tenant_summary", where: "app_tenant IS NOT NULL"
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Railwatch
4
- VERSION = "0.8.4"
4
+ VERSION = "0.8.5"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: railwatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.4
4
+ version: 0.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cole Robertson
@@ -405,6 +405,7 @@ files:
405
405
  - db/railwatch_telemetry_migrate/20260919000100_create_export_queue.rb
406
406
  - db/railwatch_telemetry_migrate/20260922000000_add_covering_indexes_for_dashboard_aggregates.rb
407
407
  - db/railwatch_telemetry_migrate/20260923000000_add_people_index.rb
408
+ - db/railwatch_telemetry_migrate/20260925000000_add_tenant_summary_index.rb
408
409
  - docs/ai-and-mcp.md
409
410
  - docs/configuration.md
410
411
  - docs/embedded.md