fix(ingestion): retry on distinct id fk constraint failure (#38977)

This commit is contained in:
Nick Best
2025-10-06 12:25:28 -07:00
committed by GitHub
parent 0a7e7ab148
commit cd80ed873c
2 changed files with 30 additions and 2 deletions

View File

@@ -17,6 +17,7 @@ import {
PersonMergeLimitExceededError,
PersonMergeRaceConditionError,
PersonMergeResult,
SourcePersonHasDistinctIdsError,
SourcePersonNotFoundError,
TargetPersonNotFoundError,
mergeError,
@@ -211,7 +212,6 @@ export class PersonMergeService {
this.context.updateIsIdentified = true
const otherPerson = await this.context.personStore.fetchForUpdate(teamId, otherPersonDistinctId)
const mergeIntoPerson = await this.context.personStore.fetchForUpdate(teamId, mergeIntoDistinctId)
// A note about the `distinctIdVersion` logic you'll find below:
@@ -518,6 +518,19 @@ export class PersonMergeService {
return mergeError(error)
} else if (error instanceof PersonMergeLimitExceededError) {
return mergeError(error)
} else if (error.code === '23503') {
// Foreign key constraint violation when attempting to delete the source person.
// This occurs when a concurrent merge operation adds a distinct ID to the source person
// after we've already moved the distinct IDs we knew about, but before the DELETE executes.
// The retry mechanism will:
// 1. Refresh the source person data to see all distinct IDs (including newly added ones)
// 2. Move ALL distinct IDs to the target person
// 3. Successfully delete the now-empty source person
return mergeError(
new SourcePersonHasDistinctIdsError(
'Cannot delete source person due to concurrent distinct ID additions'
)
)
} else {
// Re-throw unexpected errors
throw error
@@ -660,7 +673,10 @@ export class PersonMergeService {
// Handle retryable errors
if (attempt < maxRetries) {
if (result.error instanceof SourcePersonNotFoundError) {
if (
result.error instanceof SourcePersonNotFoundError ||
result.error instanceof SourcePersonHasDistinctIdsError
) {
const refreshedPerson = await this.refreshPersonData(
sourceDistinctId,
currentSourcePerson.id,

View File

@@ -66,6 +66,18 @@ export class TargetPersonNotFoundError extends PersonMergePersonNotFoundError {
}
}
/**
* Error when source person cannot be deleted due to concurrent distinct ID additions.
* This occurs when a concurrent merge operation adds a distinct ID to the person being
* deleted, causing a foreign key constraint violation. The retry will refresh the person
* data and move all distinct IDs (including the newly added ones) before attempting deletion.
*/
export class SourcePersonHasDistinctIdsError extends PersonMergePersonNotFoundError {
constructor(message: string) {
super(message, 'source')
}
}
/**
* Result of a person merge operation
*/