diff --git a/app/listeners/reporting_event_listener.rb b/app/listeners/reporting_event_listener.rb index a216bd174..40aa0b0ac 100644 --- a/app/listeners/reporting_event_listener.rb +++ b/app/listeners/reporting_event_listener.rb @@ -21,7 +21,7 @@ class ReportingEventListener < BaseListener create_bot_resolved_event(conversation, reporting_event) reporting_event.save! - ReportingEvents::RollupService.perform(reporting_event) + safe_rollup(reporting_event) end def first_reply_created(event) @@ -43,7 +43,7 @@ class ReportingEventListener < BaseListener ) reporting_event.save! - ReportingEvents::RollupService.perform(reporting_event) + safe_rollup(reporting_event) end def reply_created(event) @@ -68,7 +68,7 @@ class ReportingEventListener < BaseListener event_end_time: message.created_at ) reporting_event.save! - ReportingEvents::RollupService.perform(reporting_event) + safe_rollup(reporting_event) end def conversation_bot_handoff(event) @@ -95,7 +95,7 @@ class ReportingEventListener < BaseListener event_end_time: event_end_time ) reporting_event.save! - ReportingEvents::RollupService.perform(reporting_event) + safe_rollup(reporting_event) end def conversation_captain_inference_resolved(event) @@ -172,6 +172,16 @@ class ReportingEventListener < BaseListener bot_resolved_event = reporting_event.dup bot_resolved_event.name = 'conversation_bot_resolved' bot_resolved_event.save! - ReportingEvents::RollupService.perform(bot_resolved_event) + safe_rollup(bot_resolved_event) + end + + def safe_rollup(reporting_event) + # Rollups are derived from the raw reporting event. If a transient rollup write + # failure bubbles out here, Sidekiq retries the dispatcher job and can insert the + # same raw event again. That can temporarily under-report rollups, but the source + # event is preserved and rollup data can be rebuilt or re-applied later. + ReportingEvents::RollupService.perform(reporting_event) + rescue StandardError => e + ChatwootExceptionTracker.new(e, account: reporting_event.account).capture_exception end end diff --git a/spec/listeners/reporting_event_listener_spec.rb b/spec/listeners/reporting_event_listener_spec.rb index 34b65e7ab..a497d7dd8 100644 --- a/spec/listeners/reporting_event_listener_spec.rb +++ b/spec/listeners/reporting_event_listener_spec.rb @@ -18,6 +18,23 @@ describe ReportingEventListener do expect(account.reporting_events.where(name: 'conversation_resolved').count).to be 1 end + context 'when rollup creation fails' do + let(:event) { Events::Base.new('conversation.resolved', Time.zone.now, conversation: conversation) } + let(:error) { StandardError.new('rollup failed') } + let(:exception_tracker) { instance_double(ChatwootExceptionTracker, capture_exception: true) } + + before do + allow(ReportingEvents::RollupService).to receive(:perform).and_raise(error) + allow(ChatwootExceptionTracker).to receive(:new).and_return(exception_tracker) + end + + it 'captures the error without interrupting raw event creation' do + expect { listener.conversation_resolved(event) }.not_to raise_error + expect(ChatwootExceptionTracker).to have_received(:new).with(error, account: account) + expect(account.reporting_events.where(name: 'conversation_resolved').count).to be 1 + end + end + context 'when business hours enabled for inbox' do let(:created_at) { Time.zone.parse('March 20, 2022 00:00') } let(:updated_at) { Time.zone.parse('March 26, 2022 23:59') }