From 97e7bc7f8c3f668b5a3677e136d28e607957ff26 Mon Sep 17 00:00:00 2001 From: Arek Swidrak Date: Thu, 20 Aug 2026 11:32:21 +0300 Subject: [PATCH 1/2] fix(yes-core): restore the failed-subscription Sentry notifier (B2BY-5189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit yes-core's Railtie is a port of yousty-eventsourcing's — same before_configuration, same middlewares — but the failed_subscription_notifier block was dropped in the port. That hook is pg_eventstore's only death signal: it fires once when a subscription exhausts its restarts and stays permanently dead; per-failure errors are only recorded on the subscription row and never reach Sentry, so without the hook the death is completely silent. Consequence in production: during the W6 past-applications migration, AMS process managers were killed and stayed dead overnight with no alert, while company-manager's identical death alerted — CM had re-added the block by hand in its own initializer, AMS had not. Every yousty-eventsourcing consumer was covered by that gem's Railtie all along; the gap existed exactly for yes-core consumers, and only CM's local patch hid it. Extracted as Yes::Core::FailedSubscriptionNotifier rather than an inline proc: Sentry is not a dependency of this gem (the Railtie registers the notifier only when the host app loaded Sentry), so an inline block could not be specced — the class can, with the constant stubbed. Two details preserved from the original: hint: { ignore_exclusions: true }, so a death report can never be swallowed by excluded_exceptions, and the failed_subscription_notifier tag that Sentry alert routing keys on. Once released and adopted, company-manager's and AMS's local initializer blocks become redundant (they set the same notifier; last write wins, both identical). Specs not run locally — the Gemfile pins ruby 3.4.5, not installed here; CI runs the suite. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Y1QwAKdjduR28wJdkc6idM --- .../yes/core/failed_subscription_notifier.rb | 29 +++++++++++++++++ yes-core/lib/yes/core/railtie.rb | 5 +++ .../core/failed_subscription_notifier_spec.rb | 31 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 yes-core/lib/yes/core/failed_subscription_notifier.rb create mode 100644 yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb diff --git a/yes-core/lib/yes/core/failed_subscription_notifier.rb b/yes-core/lib/yes/core/failed_subscription_notifier.rb new file mode 100644 index 0000000..94f95f8 --- /dev/null +++ b/yes-core/lib/yes/core/failed_subscription_notifier.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Yes + module Core + # Reports a permanently dead pg_eventstore subscription to Sentry. + # + # pg_eventstore calls its +failed_subscription_notifier+ exactly once, when a + # subscription exhausts its restarts and stays dead — it is the gem's only + # death signal, and without it that death is silent (B2BY-5189). Per-failure + # errors are only recorded on the subscription row, never raised into Sentry. + class FailedSubscriptionNotifier + # @param subscription [PgEventstore::Subscription] + # @param error [StandardError] + # @return [void] + def call(subscription, error) + Sentry.with_scope do |scope| + scope.set_tags(failed_subscription_notifier: true) # used in Sentry Alerts + + Sentry.capture_exception( + error, + # a death report must never be swallowed by Sentry's excluded_exceptions + hint: { ignore_exclusions: true }, + extra: { id: subscription.id, set: subscription.set, name: subscription.name } + ) + end + end + end + end +end diff --git a/yes-core/lib/yes/core/railtie.rb b/yes-core/lib/yes/core/railtie.rb index f37bf12..3974674 100644 --- a/yes-core/lib/yes/core/railtie.rb +++ b/yes-core/lib/yes/core/railtie.rb @@ -16,6 +16,11 @@ class Railtie < Rails::Railtie with_indifferent_access: Yes::Core::Middlewares::WithIndifferentAccess.new, timestamp: Yes::Core::Middlewares::Timestamp.new } + + # pg_eventstore's only death signal — without it a subscription that + # exhausts its restarts dies silently (B2BY-5189). Present in + # yousty-eventsourcing's Railtie; dropped when it was ported here. + config.failed_subscription_notifier = FailedSubscriptionNotifier.new if defined?(Sentry) end end diff --git a/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb b/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb new file mode 100644 index 0000000..2f3a1cd --- /dev/null +++ b/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +RSpec.describe Yes::Core::FailedSubscriptionNotifier do + subject(:notify) { described_class.new.call(subscription, error) } + + let(:subscription) { PgEventstore::Subscription.new(id: 42, set: 'Yes', name: 'SomeProcessManager') } + let(:error) { StandardError.new('handler exploded') } + let(:sentry) { class_double('Sentry') } + let(:scope) { double('Sentry::Scope', set_tags: nil) } # rubocop:disable RSpec/VerifiedDoubles + + before do + # Sentry is not a dependency of this gem — the railtie only registers the + # notifier when the host app loaded it, so the constant is stubbed here. + stub_const('Sentry', sentry) + allow(sentry).to receive(:with_scope).and_yield(scope) + allow(sentry).to receive(:capture_exception) + end + + it 'reports the death to Sentry with the subscription identity' do + notify + + aggregate_failures do + expect(scope).to have_received(:set_tags).with(failed_subscription_notifier: true) + expect(sentry).to have_received(:capture_exception).with( + error, + hint: { ignore_exclusions: true }, + extra: { id: 42, set: 'Yes', name: 'SomeProcessManager' } + ) + end + end +end From f3e7ddfd20e28388ddb5c609a452fb8f4404d723 Mon Sep 17 00:00:00 2001 From: Arek Swidrak Date: Thu, 20 Aug 2026 11:36:46 +0300 Subject: [PATCH 2/2] style: drop a disable directive for a cop this repo does not load Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Y1QwAKdjduR28wJdkc6idM --- yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb b/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb index 2f3a1cd..e8cdda6 100644 --- a/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb +++ b/yes-core/spec/lib/yes/core/failed_subscription_notifier_spec.rb @@ -6,7 +6,7 @@ let(:subscription) { PgEventstore::Subscription.new(id: 42, set: 'Yes', name: 'SomeProcessManager') } let(:error) { StandardError.new('handler exploded') } let(:sentry) { class_double('Sentry') } - let(:scope) { double('Sentry::Scope', set_tags: nil) } # rubocop:disable RSpec/VerifiedDoubles + let(:scope) { double('Sentry::Scope', set_tags: nil) } before do # Sentry is not a dependency of this gem — the railtie only registers the