where_is_waldo 0.1.9 → 0.1.10
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/CHANGELOG.md +32 -0
- data/README.md +8 -0
- data/VERSION +1 -1
- data/app/channels/where_is_waldo/presence_channel.rb +13 -2
- data/lib/where_is_waldo/apm.rb +73 -0
- data/lib/where_is_waldo/configuration.rb +12 -0
- data/lib/where_is_waldo.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ce4ac18944bec996440772eed972216ff64975a1be13b4f7f3d0230e5cc0807f
|
|
4
|
+
data.tar.gz: 80e1f0ba820d24dc5f5d74f7ef4cc31e00ba3f183145d24de17dc24e5bd4b78b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 877c8e0adf85cb7c8e75f992939c372690d1a928ab3ef4b6a4d64479ba9801ccd74820235db112d4efcab473a0589097165b62edf07824247a1a6be82d6ba32a
|
|
7
|
+
data.tar.gz: d91ec5e623c3a8e8b7b149d5d9a80d6110ab5f4a8c81cc71bdaa73608afc593efe534bc2dc53c71df067837fb56c1cac9479df52a421f7364d1b545cc7657018
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
Notable changes to where_is_waldo. Format loosely follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
|
|
6
|
+
## 0.1.10
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Presence heartbeats are kept out of APM as their own transactions.**
|
|
11
|
+
`WhereIsWaldo::PresenceChannel#heartbeat` now runs inside
|
|
12
|
+
`WhereIsWaldo::Apm.ignoring_transaction`, so when a host app runs New Relic
|
|
13
|
+
the per-tab-per-interval heartbeat — the highest-volume, least-interesting
|
|
14
|
+
cable action — no longer surfaces as an APM transaction. Left unhandled it
|
|
15
|
+
inflates throughput and flatters average response time (heartbeats are
|
|
16
|
+
uniformly fast) while telling operators nothing the presence store can't
|
|
17
|
+
already answer.
|
|
18
|
+
|
|
19
|
+
- Guarded, no new dependency: `Apm` no-ops unless a supported agent
|
|
20
|
+
(New Relic today) is loaded, so consumers without APM pay nothing.
|
|
21
|
+
- Failures stay visible: an ignored New Relic transaction skips `commit!`,
|
|
22
|
+
where exceptions are normally harvested, so the wrapper hands any heartbeat
|
|
23
|
+
exception straight to the error collector and re-raises. Ignoring the
|
|
24
|
+
routine transaction never hides a heartbeat failure.
|
|
25
|
+
- Opt out with `config.ignore_heartbeat_apm = false` to report heartbeats
|
|
26
|
+
like any other action.
|
|
27
|
+
- Scope: New Relic only. The underlying exposure is agent-general — any APM
|
|
28
|
+
that opens a transaction per `perform_action` sees the same heartbeat
|
|
29
|
+
volume — but New Relic is the only agent seen in production so far.
|
|
30
|
+
Detection is isolated in `Apm.agent` so another vendor can be added there
|
|
31
|
+
without touching callers.
|
|
32
|
+
|
|
33
|
+
Note: the companion New Relic issue — the agent pinning the WebSocket upgrade
|
|
34
|
+
transaction onto pooled ActionCable worker threads — is a general
|
|
35
|
+
newrelic_rpm × ActionCable defect and stays a host-app shim; it is not
|
|
36
|
+
something the gem patches.
|
|
37
|
+
|
|
6
38
|
## 0.1.9
|
|
7
39
|
|
|
8
40
|
Security/maintenance release — dependency bumps only, no library code changes.
|
data/README.md
CHANGED
|
@@ -113,6 +113,14 @@ WhereIsWaldo.configure do |config|
|
|
|
113
113
|
config.timeout = 90 # seconds until offline
|
|
114
114
|
config.heartbeat_interval = 30
|
|
115
115
|
|
|
116
|
+
# Observability: when true (default) and the host app runs a supported APM
|
|
117
|
+
# agent (New Relic today), presence heartbeats are kept out of APM as their
|
|
118
|
+
# own transactions. Heartbeats are the highest-volume, least-interesting cable
|
|
119
|
+
# action, so reporting each one inflates throughput and flatters average
|
|
120
|
+
# response time without adding signal. Failures are still reported. Set false
|
|
121
|
+
# to report heartbeats like any other action.
|
|
122
|
+
config.ignore_heartbeat_apm = true
|
|
123
|
+
|
|
116
124
|
# Optional: custom subject data in presence hash. NOTE: with the roster
|
|
117
125
|
# enabled, these fields are broadcast to every member of the org — see
|
|
118
126
|
# Security.
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.10
|
|
@@ -32,7 +32,20 @@ module WhereIsWaldo
|
|
|
32
32
|
publish_roster_change
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# Kept out of APM as its own transaction by default — heartbeats are the
|
|
36
|
+
# highest-volume, least-interesting cable action, so reporting each one
|
|
37
|
+
# distorts throughput and average response time without adding signal. The
|
|
38
|
+
# wrapper no-ops unless the host runs a supported agent; see
|
|
39
|
+
# WhereIsWaldo::Apm and config.ignore_heartbeat_apm. The real work lives in
|
|
40
|
+
# perform_heartbeat, which is private so a client can't invoke it directly
|
|
41
|
+
# as a channel action (only public methods are processable actions).
|
|
35
42
|
def heartbeat(data)
|
|
43
|
+
WhereIsWaldo::Apm.ignoring_transaction { perform_heartbeat(data) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def perform_heartbeat(data)
|
|
36
49
|
return if presence_suppressed?
|
|
37
50
|
|
|
38
51
|
data = data.with_indifferent_access
|
|
@@ -58,8 +71,6 @@ module WhereIsWaldo
|
|
|
58
71
|
publish_roster_change
|
|
59
72
|
end
|
|
60
73
|
|
|
61
|
-
private
|
|
62
|
-
|
|
63
74
|
# Memoized per-subscription. Host apps configure suppress_presence_proc
|
|
64
75
|
# to gate WHICH subscriptions register presence — e.g. a support-user
|
|
65
76
|
# impersonation tab is a legitimate WS consumer but shouldn't count as
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WhereIsWaldo
|
|
4
|
+
# APM (New Relic) integration seam.
|
|
5
|
+
#
|
|
6
|
+
# The gem takes NO hard dependency on newrelic_rpm. Every method here no-ops
|
|
7
|
+
# unless a supported agent is loaded in the host app, so a consumer without
|
|
8
|
+
# New Relic pays nothing and nothing breaks.
|
|
9
|
+
#
|
|
10
|
+
# Why this exists: ActionCable dispatches each client action through New
|
|
11
|
+
# Relic's ActionCableSubscriber, which starts one APM transaction per
|
|
12
|
+
# `perform_action`. Presence heartbeats are the highest-volume, least
|
|
13
|
+
# interesting action Waldo has — one per open tab per heartbeat interval,
|
|
14
|
+
# uniformly fast — so left alone they dominate transaction throughput and
|
|
15
|
+
# flatten the host app's average response time while telling operators nothing
|
|
16
|
+
# the presence store can't already answer. `ignoring_transaction` keeps them
|
|
17
|
+
# out of APM's headline numbers without hiding failures.
|
|
18
|
+
#
|
|
19
|
+
# (The companion half of this problem — the agent pinning the WebSocket
|
|
20
|
+
# upgrade transaction onto pooled ActionCable worker threads — is a general
|
|
21
|
+
# newrelic_rpm x ActionCable defect, not Waldo's to fix here. Host apps carry
|
|
22
|
+
# that shim until the agent resets pooled-thread state itself.)
|
|
23
|
+
#
|
|
24
|
+
# Scope: this handles New Relic ONLY. The underlying exposure is agent-general
|
|
25
|
+
# — any APM/tracer that opens a transaction per `perform_action` (Datadog,
|
|
26
|
+
# Scout, AppSignal, Skylight, Sentry, OpenTelemetry) sees the same heartbeat
|
|
27
|
+
# flood, and several share the same pooled-thread context-leak. Waldo doesn't
|
|
28
|
+
# cause that (it spawns no threads and patches nothing global); its heartbeat
|
|
29
|
+
# volume amplifies whatever the host's agent already does. Detection is
|
|
30
|
+
# isolated in `agent` so another vendor can be added there without touching
|
|
31
|
+
# callers.
|
|
32
|
+
module Apm
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# The New Relic agent module, or nil when newrelic_rpm isn't loaded (or is
|
|
36
|
+
# too old to expose the API we use). Isolated behind one method so specs can
|
|
37
|
+
# drive both the present and absent paths without defining a global
|
|
38
|
+
# ::NewRelic constant.
|
|
39
|
+
def agent
|
|
40
|
+
return nil unless defined?(::NewRelic::Agent)
|
|
41
|
+
return nil unless ::NewRelic::Agent.respond_to?(:ignore_transaction)
|
|
42
|
+
|
|
43
|
+
::NewRelic::Agent
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Run a unit of work without letting it surface as its own APM transaction,
|
|
47
|
+
# and return the block's value.
|
|
48
|
+
#
|
|
49
|
+
# A plain `yield` when there's no agent, or when the host has opted out via
|
|
50
|
+
# `config.ignore_heartbeat_apm = false` — in either case the transaction is
|
|
51
|
+
# reported normally.
|
|
52
|
+
#
|
|
53
|
+
# New Relic's `Transaction#ignore!` makes `#finish` skip `commit!`, and
|
|
54
|
+
# `commit!` is where transaction-attached exceptions are harvested
|
|
55
|
+
# (`record_exceptions`). So ignoring on its own would ALSO swallow a
|
|
56
|
+
# heartbeat failure. We hand any exception straight to the error collector —
|
|
57
|
+
# which reports immediately rather than at commit — and re-raise, so the
|
|
58
|
+
# caller's own error handling is unchanged.
|
|
59
|
+
def ignoring_transaction
|
|
60
|
+
nr = agent
|
|
61
|
+
return yield unless nr && WhereIsWaldo.config.ignore_heartbeat_apm
|
|
62
|
+
|
|
63
|
+
nr.ignore_transaction
|
|
64
|
+
|
|
65
|
+
begin
|
|
66
|
+
yield
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
nr.instance&.error_collector&.notice_error(e)
|
|
69
|
+
raise
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -27,6 +27,15 @@ module WhereIsWaldo
|
|
|
27
27
|
# :heartbeat_interval - expected heartbeat frequency (default: 30)
|
|
28
28
|
attr_accessor :timeout, :heartbeat_interval
|
|
29
29
|
|
|
30
|
+
# Observability
|
|
31
|
+
# :ignore_heartbeat_apm - when true (default) and a supported APM agent
|
|
32
|
+
# (New Relic) is loaded in the host app, presence heartbeats are kept out
|
|
33
|
+
# of APM as their own transactions. Heartbeats are the highest-volume,
|
|
34
|
+
# least-interesting cable action, so reporting each one distorts
|
|
35
|
+
# throughput and average response time without adding signal. Set false
|
|
36
|
+
# to report them like any other action. See WhereIsWaldo::Apm.
|
|
37
|
+
attr_accessor :ignore_heartbeat_apm
|
|
38
|
+
|
|
30
39
|
# ActionCable settings
|
|
31
40
|
# :channel_name - defaults to 'WhereIsWaldo::PresenceChannel'
|
|
32
41
|
# :authenticate_proc - proc to authenticate connection, receives request
|
|
@@ -120,6 +129,9 @@ module WhereIsWaldo
|
|
|
120
129
|
@timeout = 90
|
|
121
130
|
@heartbeat_interval = 30
|
|
122
131
|
|
|
132
|
+
# Observability defaults
|
|
133
|
+
@ignore_heartbeat_apm = true
|
|
134
|
+
|
|
123
135
|
# ActionCable defaults
|
|
124
136
|
@channel_name = "WhereIsWaldo::PresenceChannel"
|
|
125
137
|
@authenticate_proc = nil
|
data/lib/where_is_waldo.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: where_is_waldo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Gibson
|
|
@@ -215,6 +215,7 @@ files:
|
|
|
215
215
|
- lib/generators/where_is_waldo/install/templates/migration.rb.tt
|
|
216
216
|
- lib/generators/where_is_waldo/install/templates/presence_channel.rb.tt
|
|
217
217
|
- lib/where_is_waldo.rb
|
|
218
|
+
- lib/where_is_waldo/apm.rb
|
|
218
219
|
- lib/where_is_waldo/configuration.rb
|
|
219
220
|
- lib/where_is_waldo/engine.rb
|
|
220
221
|
- lib/where_is_waldo/version.rb
|