railwatch 0.8.1 → 0.8.3
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6426edd02fd2bb306da28aa55723598581751003cce2b10371fdabb5d5a726c7
|
|
4
|
+
data.tar.gz: 44f256ceff92b14dd0534e0f260f6d8d8b9fb4294e357091c25ec3e0eb3b3ef3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bdb8ba9db502d460a3a653a52238728993807fe5b374823d08f8418b865a1a0d6c0a39f18809a9a00fe95c96952f6a3db4d0337bb3b544e85e63c835830b5e62
|
|
7
|
+
data.tar.gz: 0ef6f54e5da09ae2b3fc7048ae8b9b4d50fbda878dbb42220c98236345054db8b6b24134e41c7eedf8d13d13f843f4405b0f037d3dd8e6e6caa3cb5d2a6f6d0e
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@
|
|
|
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.3 (2026-09-23)
|
|
10
|
+
|
|
11
|
+
- Filtering jobs by `class:` seeks the class's group (a job's group is
|
|
12
|
+
the digest of its class name) instead of walking every job in the
|
|
13
|
+
window: 715 ms over a week on the platform's own tenant, now 9 ms, and
|
|
14
|
+
one MCP search took 49 s.
|
|
15
|
+
- The Scheduled tasks page and the missed-task check read each task's
|
|
16
|
+
newest schedule with one index seek per key. The single grouped query
|
|
17
|
+
they used read every scheduled run kept (10 s once, 185 ms warm).
|
|
18
|
+
|
|
19
|
+
## 0.8.2 (2026-09-23)
|
|
20
|
+
|
|
21
|
+
- The Profiles page's profiled share counts only the executions inside
|
|
22
|
+
its window. A window starting or ending mid-hour counted the whole of
|
|
23
|
+
its first and last hours, because a rollup holds its whole hour; the
|
|
24
|
+
partial hours are now counted from the rows.
|
|
25
|
+
|
|
9
26
|
## 0.8.1 (2026-09-23)
|
|
10
27
|
|
|
11
28
|
- The Tenants page reads only through the tenant-led indexes. Sparklines
|
|
@@ -30,16 +30,18 @@ module Railwatch
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
32
|
# How many executions the window holds, as the profiled share's
|
|
33
|
-
# denominator. Every kind is rolled up, so
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
33
|
+
# denominator. Every kind is rolled up, so whole hours come from a few
|
|
34
|
+
# hundred rollup rows rather than counting every execution (1.2 s over a
|
|
35
|
+
# week of the platform's own). A rollup holds its whole hour, so the
|
|
36
|
+
# partial hours at either end of the window are counted from the rows.
|
|
37
37
|
def executions_in(from, to)
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
first = from.beginning_of_hour == from ? from : from.beginning_of_hour + 1.hour
|
|
39
|
+
last = to.beginning_of_hour
|
|
40
|
+
return Telemetry::Execution.between(from, to).count if first >= last
|
|
40
41
|
|
|
41
|
-
Telemetry::Execution.where(occurred_at: from...
|
|
42
|
-
Telemetry::Rollup.for_type(Telemetry::Execution::KINDS).
|
|
42
|
+
Telemetry::Execution.where(occurred_at: from...first).count +
|
|
43
|
+
Telemetry::Rollup.for_type(Telemetry::Execution::KINDS).where(bucket: first...last).sum(:count) +
|
|
44
|
+
Telemetry::Execution.where(occurred_at: last..to).count
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
# Rows arrive newest-first, so the head of each group is the profile the
|
|
@@ -125,7 +125,19 @@ module Railwatch
|
|
|
125
125
|
outcome = fields["outcome"].presence || fields["status"].presence
|
|
126
126
|
scope = scope.where(queue: fields["queue"]) if fields["queue"].present?
|
|
127
127
|
scope = scope.where(outcome: outcome) if outcome
|
|
128
|
-
|
|
128
|
+
# A job's group is the digest of its class name, and group_hash is
|
|
129
|
+
# indexed with occurred_at; name is not, so class: alone walked every
|
|
130
|
+
# job in the window (49 s for a week through MCP). Not every group
|
|
131
|
+
# follows that rule -- Solid Queue's pruned attempts are named
|
|
132
|
+
# "(pruned)" under the SolidQueue::Pruned group -- so the window's
|
|
133
|
+
# rollups supply any other group recorded under the name, and the last
|
|
134
|
+
# FRESH_NAMES, which rollups have not caught up with, match by name.
|
|
135
|
+
if fields["class"].present?
|
|
136
|
+
name = fields["class"]
|
|
137
|
+
groups = [ Record.group_hash(name) ] |
|
|
138
|
+
Telemetry::Rollup.for_type("job_attempt").between(range.begin, range.end).where(name: name).distinct.pluck(:group_hash)
|
|
139
|
+
scope = scope.merge(Telemetry::Execution.in_groups_or_fresh("job_attempt", groups, range)).where(name: name)
|
|
140
|
+
end
|
|
129
141
|
scope = scope.where(job_id: fields["job_id"]) if fields["job_id"].present?
|
|
130
142
|
return scope unless text.present?
|
|
131
143
|
scope.merge(Telemetry::Execution.named_like("job_attempt", text, range, previews: true))
|
|
@@ -61,6 +61,19 @@ module Railwatch
|
|
|
61
61
|
.where(ids.map { |branch| "#{quoted_table_name}.id IN (#{branch.select(:id).to_sql})" }.join(" OR "))
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# Executions of `kind` in `range` that are in one of `groups`, or recent
|
|
65
|
+
# enough (FRESH_NAMES) that their rollup may not exist yet. Each branch
|
|
66
|
+
# names its index and the outer lookup is by rowid, as in named_like:
|
|
67
|
+
# an OR over the two conditions made SQLite walk the whole window.
|
|
68
|
+
def self.in_groups_or_fresh(kind, groups, range)
|
|
69
|
+
by_group = indexed_by("index_executions_on_group_hash_and_occurred_at").where(group_hash: groups, occurred_at: range, kind: kind)
|
|
70
|
+
fresh = where(kind: kind, occurred_at: [ range.begin, range.end - FRESH_NAMES ].max..range.end)
|
|
71
|
+
return where(kind: kind, occurred_at: range).where(group_hash: groups).or(fresh) unless TelemetryRecord.sqlite?
|
|
72
|
+
|
|
73
|
+
from("#{quoted_table_name} NOT INDEXED").where(kind: kind, occurred_at: range)
|
|
74
|
+
.where([ by_group, fresh ].map { |branch| "#{quoted_table_name}.id IN (#{branch.select(:id).to_sql})" }.join(" OR "))
|
|
75
|
+
end
|
|
76
|
+
|
|
64
77
|
def self.indexed_by(index) = unscoped.from("#{quoted_table_name} INDEXED BY #{index}")
|
|
65
78
|
private_class_method :indexed_by
|
|
66
79
|
|
|
@@ -74,14 +87,17 @@ module Railwatch
|
|
|
74
87
|
KINDS.flat_map { |kind| where(kind: kind, occurred_at: time..).distinct.pluck(:server) }.uniq
|
|
75
88
|
end
|
|
76
89
|
|
|
77
|
-
# The schedule string each task key last ran with
|
|
78
|
-
#
|
|
79
|
-
#
|
|
90
|
+
# The schedule string each task key last ran with: the newest
|
|
91
|
+
# scheduled_task row per key carries it in detail. A task's runs share
|
|
92
|
+
# its group (the digest of its key), so that row is the last entry of
|
|
93
|
+
# its group_hash index -- one seek per key, a few dozen keys. A single
|
|
94
|
+
# MAX(id) GROUP BY task_key read every scheduled run ever kept instead:
|
|
95
|
+
# 10 s on the platform's own tenant.
|
|
80
96
|
def self.latest_schedules(keys)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
keys.index_with do |key|
|
|
98
|
+
scheduled.where(group_hash: Record.group_hash(key), task_key: key).order(occurred_at: :desc)
|
|
99
|
+
.pick(Arel.sql("json_extract(detail, '$.schedule')"))
|
|
100
|
+
end.compact
|
|
85
101
|
end
|
|
86
102
|
|
|
87
103
|
CHILDREN = {
|
data/lib/railwatch/version.rb
CHANGED