Skip to content

refactor(email-service): Contact upserting logic#999

Merged
evanhutnik merged 2 commits intomainfrom
evan/contact-update
Jan 14, 2026
Merged

refactor(email-service): Contact upserting logic#999
evanhutnik merged 2 commits intomainfrom
evan/contact-update

Conversation

@evanhutnik
Copy link
Copy Markdown
Contributor

Summary

Problem

The previous implementation of contact upserting used an INSERT ... ON CONFLICT DO UPDATE pattern that would acquire row-level locks on the email_contacts table. During high-concurrency scenarios like backfilling, this led to:

  • Row-level locks held for the duration of transactions
  • High database session counts as connections waited for locks to be released
  • Potential deadlocks when multiple transactions tried to upsert overlapping sets of contacts

Solution

Refactored the contact upsert logic to use a lock-free, optimistic approach:

  1. Fetch existing contacts first (read-only, no locks)
  2. Filter to only new contacts that need to be inserted
  3. Insert with ON CONFLICT DO NOTHING (no row locks on existing rows)
  4. Handle race conditions by re-fetching any contacts that were inserted by concurrent transactions

This approach eliminates the need for row-level locks on existing contacts and significantly reduces database contention.

  • Moved contact upserting outside transactions in send/draft endpoints to prevent holding locks during long-running operations

Screenshots, GIFs, and Videos

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Jan 14, 2026

test

#[tracing::instrument(skip(pool, contacts))]
async fn upsert_message_contacts(
pool: &PgPool,
contacts: Vec<ContactPhotoless>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed now but in the future using the non_empty crate here can help avoid those checks of

if x.is_empty() {
    return early
}

@evanhutnik evanhutnik merged commit 0f56b56 into main Jan 14, 2026
39 checks passed
@evanhutnik evanhutnik deleted the evan/contact-update branch January 14, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants