mirror of
https://github.com/we-promise/sure.git
synced 2026-05-31 08:19:03 +00:00
fix(goals): clear state-dependent caches on AASM transition + harden sweep job
- Goal: `display_status` and `projection_summary` memoize a value that depends on the AASM state column. Without resetting them after a transition the same instance keeps returning the pre-transition value. Hook `after_all_transitions :reset_state_dependent_caches!` undoes the memos so post-`archive!` / post-`pause!` reads see the new state. - SweepExpiredGoalPledgesJob: the inner rescue covered per-pledge failures but not cursor-phase failures (DB blip, OOM mid-batch). Add an outer rescue that reports + re-raises so Sentry sees the failure and Sidekiq retries the job.
This commit is contained in:
@@ -3,6 +3,10 @@ class SweepExpiredGoalPledgesJob < ApplicationJob
|
||||
|
||||
# Per-record rescue so one bad pledge (lock contention, missing FK,
|
||||
# stale row) doesn't abort the sweep and leave the rest open forever.
|
||||
# The outer rescue catches query-phase failures (DB blip, OOM mid-cursor)
|
||||
# so a single bad batch surfaces to Sentry rather than disappearing into
|
||||
# Sidekiq's generic retry log. Re-raise after reporting so the retry
|
||||
# behaviour still kicks in.
|
||||
def perform
|
||||
GoalPledge.open_and_expired_now.find_each do |pledge|
|
||||
pledge.expire!
|
||||
@@ -10,5 +14,9 @@ class SweepExpiredGoalPledgesJob < ApplicationJob
|
||||
Rails.logger.error("SweepExpiredGoalPledgesJob: pledge ##{pledge.id} expire failed: #{e.class}: #{e.message}")
|
||||
Sentry.capture_exception(e) if defined?(Sentry)
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("SweepExpiredGoalPledgesJob: cursor failed: #{e.class}: #{e.message}")
|
||||
Sentry.capture_exception(e) if defined?(Sentry)
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,6 +36,8 @@ class Goal < ApplicationRecord
|
||||
end
|
||||
|
||||
aasm column: :state do
|
||||
after_all_transitions :reset_state_dependent_caches!
|
||||
|
||||
state :active, initial: true
|
||||
state :paused
|
||||
state :completed
|
||||
@@ -398,6 +400,16 @@ class Goal < ApplicationRecord
|
||||
end
|
||||
|
||||
private
|
||||
# Cleared after every AASM transition. The state column drives the
|
||||
# display_status / projection_summary memos; without this the same
|
||||
# instance keeps returning the pre-transition value if a controller
|
||||
# calls archive! / pause! and then renders without reload.
|
||||
def reset_state_dependent_caches!
|
||||
%i[@display_status @projection_summary].each do |ivar|
|
||||
remove_instance_variable(ivar) if instance_variable_defined?(ivar)
|
||||
end
|
||||
end
|
||||
|
||||
# K/M shorthand for narrow chart annotations (axis ticks, projection
|
||||
# short-form, pending-pledge badge). Locale-aware currency symbol via
|
||||
# Money so the chart matches the rest of the app for EUR/GBP families.
|
||||
|
||||
Reference in New Issue
Block a user