refactor: unify backfill service to per-dimension query pattern

Replace the two-path aggregation (group-then-fan-out for standard events,
per-dimension for distinct-count) with a single per-dimension loop for both.
This removes the bug-prone fan-out logic and the conceptual split.

Strengthen specs to cover all three dimensions (account, agent, inbox),
assert total rollup row counts, and add distinct-count deduplication test.
This commit is contained in:
Shivam Mishra
2026-03-13 15:17:46 +05:30
parent 4e2924538f
commit b8543c09fb
2 changed files with 122 additions and 76 deletions
@@ -62,15 +62,72 @@ describe ReportingEvents::BackfillService do
expect(reporting_event_instantiations).to eq(0)
first_response_rollup = find_rollup('agent', user.id, 'first_response')
expect(first_response_rollup.count).to eq(2)
expect(first_response_rollup.sum_value).to eq(140)
expect(first_response_rollup.sum_value_business_hours).to eq(80)
rollups = ReportingEventsRollup.where(account_id: account.id, date: date)
# 3 dimensions × first_response + 3 dimensions × resolutions_count + 3 dimensions × resolution_time
expect(rollups.count).to eq(9)
resolution_time_rollup = find_rollup('agent', second_user.id, 'resolution_time')
expect(resolution_time_rollup.count).to eq(1)
expect(resolution_time_rollup.sum_value).to eq(200)
expect(resolution_time_rollup.sum_value_business_hours).to eq(80)
# account dimension
account_first_response = find_rollup('account', account.id, 'first_response')
expect(account_first_response.count).to eq(2)
expect(account_first_response.sum_value).to eq(140)
expect(account_first_response.sum_value_business_hours).to eq(80)
# agent dimension
agent_first_response = find_rollup('agent', user.id, 'first_response')
expect(agent_first_response.count).to eq(2)
expect(agent_first_response.sum_value).to eq(140)
expect(agent_first_response.sum_value_business_hours).to eq(80)
agent_resolution_time = find_rollup('agent', second_user.id, 'resolution_time')
expect(agent_resolution_time.count).to eq(1)
expect(agent_resolution_time.sum_value).to eq(200)
expect(agent_resolution_time.sum_value_business_hours).to eq(80)
# inbox dimension
inbox_first_response = find_rollup('inbox', inbox.id, 'first_response')
expect(inbox_first_response.count).to eq(2)
expect(inbox_first_response.sum_value).to eq(140)
expect(inbox_first_response.sum_value_business_hours).to eq(80)
inbox_resolution_time = find_rollup('inbox', second_inbox.id, 'resolution_time')
expect(inbox_resolution_time.count).to eq(1)
expect(inbox_resolution_time.sum_value).to eq(200)
expect(inbox_resolution_time.sum_value_business_hours).to eq(80)
end
it 'deduplicates distinct-count events per dimension' do
second_user = create(:user, account: account)
second_inbox = create(:inbox, account: account)
conversation_b = create(:conversation, account: account, inbox: inbox, assignee: user)
conversation_c = create(:conversation, account: account, inbox: second_inbox, assignee: second_user)
# Two events for the same conversation — should count as 1
create_backfill_event(name: 'conversation_bot_handoff', value: 0, value_in_business_hours: 0, user: user,
inbox: inbox, conversation: conversation, created_at: Time.utc(2026, 2, 11, 14))
create_backfill_event(name: 'conversation_bot_handoff', value: 0, value_in_business_hours: 0, user: user,
inbox: inbox, conversation: conversation, created_at: Time.utc(2026, 2, 11, 15))
# Different conversation, same agent/inbox
create_backfill_event(name: 'conversation_bot_handoff', value: 0, value_in_business_hours: 0, user: user,
inbox: inbox, conversation: conversation_b, created_at: Time.utc(2026, 2, 11, 16))
# Different agent/inbox
create_backfill_event(name: 'conversation_bot_handoff', value: 0, value_in_business_hours: 0, user: second_user,
inbox: second_inbox, conversation: conversation_c, created_at: Time.utc(2026, 2, 11, 17))
described_class.backfill_date(account, date)
rollups = ReportingEventsRollup.where(account_id: account.id, date: date)
expect(rollups.count).to eq(5)
# account: 3 distinct conversations
expect(find_rollup('account', account.id, 'bot_handoffs_count').count).to eq(3)
# agent: user has 2 distinct, second_user has 1
expect(find_rollup('agent', user.id, 'bot_handoffs_count').count).to eq(2)
expect(find_rollup('agent', second_user.id, 'bot_handoffs_count').count).to eq(1)
# inbox: inbox has 2 distinct, second_inbox has 1
expect(find_rollup('inbox', inbox.id, 'bot_handoffs_count').count).to eq(2)
expect(find_rollup('inbox', second_inbox.id, 'bot_handoffs_count').count).to eq(1)
end
def create_backfill_event(**attributes)