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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user