Skip to content

Commit

Permalink
[GEC] Fixes for concurrent modification error
Browse files Browse the repository at this point in the history
  • Loading branch information
varadarajan-tw committed Jan 24, 2025
1 parent 609f20a commit 35edd08
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ describe('GoogleEnhancedConversions', () => {
)
})

it('handles concurrent_modification error correctly', async () => {
it('handles concurrent_modification error correctly from addOperations API', async () => {
const events: SegmentEvent[] = [
createTestEvent({
timestamp,
Expand Down Expand Up @@ -486,5 +486,195 @@ describe('GoogleEnhancedConversions', () => {

await expect(responses).rejects.toThrowError(RetryableError)
})

it('handles concurrent_modification error correctly from create offlineUserDataJobs API', async () => {
const events: SegmentEvent[] = [
createTestEvent({
timestamp,
event: 'Audience Entered',
properties: {
gclid: '54321',
email: '[email protected]',
orderId: '1234',
phone: '1234567890',
firstName: 'Jane',
lastName: 'Doe',
currency: 'USD',
value: '123',
address: {
street: '123 Street SW',
city: 'San Diego',
state: 'CA',
postalCode: '982004'
}
}
}),
createTestEvent({
timestamp,
event: 'Audience Entered',
properties: {
gclid: '54321',
email: '[email protected]',
orderId: '1234',
phone: '1234567890',
firstName: 'Jane',
lastName: 'Doe',
currency: 'USD',
value: '123',
address: {
street: '123 Street SW',
city: 'San Diego',
state: 'CA',
postalCode: '982004'
}
}
})
]

nock(`https://googleads.googleapis.com/${API_VERSION}/customers/${customerId}/offlineUserDataJobs:create`)
.post(/.*/)
.reply(400, {
error: {
code: 400,
details: [
{
'@type': 'type.googleapis.com/google.ads.googleads.v17.errors.GoogleAdsFailure',
errors: [
{
errorCode: {
databaseError: 'CONCURRENT_MODIFICATION'
},
message: 'Multiple requests were attempting to modify the same resource at once. Retry the request.'
}
],
requestId: 'OZ5_72C-3qFN9a87mjE7_w'
}
],
message: 'Request contains an invalid argument.',
status: 'INVALID_ARGUMENT'
}
})

const responses = testDestination.testBatchAction('userList', {
events,
mapping: {
ad_user_data_consent_state: 'GRANTED',
ad_personalization_consent_state: 'GRANTED',
external_audience_id: '1234',
retlOnMappingSave: {
outputs: {
id: '1234',
name: 'Test List',
external_id_type: 'CONTACT_INFO'
}
}
},
useDefaultMappings: true,
settings: {
customerId
}
})

await expect(responses).rejects.toThrowError(RetryableError)
})

it('handles concurrent_modification error correctly from run offlineUserDataJobs API', async () => {
const events: SegmentEvent[] = [
createTestEvent({
timestamp,
event: 'Audience Entered',
properties: {
gclid: '54321',
email: '[email protected]',
orderId: '1234',
phone: '1234567890',
firstName: 'Jane',
lastName: 'Doe',
currency: 'USD',
value: '123',
address: {
street: '123 Street SW',
city: 'San Diego',
state: 'CA',
postalCode: '982004'
}
}
}),
createTestEvent({
timestamp,
event: 'Audience Entered',
properties: {
gclid: '54321',
email: '[email protected]',
orderId: '1234',
phone: '1234567890',
firstName: 'Jane',
lastName: 'Doe',
currency: 'USD',
value: '123',
address: {
street: '123 Street SW',
city: 'San Diego',
state: 'CA',
postalCode: '982004'
}
}
})
]

nock(`https://googleads.googleapis.com/${API_VERSION}/customers/${customerId}/offlineUserDataJobs:create`)
.post(/.*/)
.reply(200, { data: 'offlineDataJob' })

nock(`https://googleads.googleapis.com/${API_VERSION}/offlineDataJob:addOperations`)
.post(/.*/)
.reply(200, { data: 'offlineDataJob' })

nock(`https://googleads.googleapis.com/${API_VERSION}/offlineDataJob:run`)
.post(/.*/)
.reply(400, {
error: {
code: 400,
details: [
{
'@type': 'type.googleapis.com/google.ads.googleads.v17.errors.GoogleAdsFailure',
errors: [
{
errorCode: {
databaseError: 'CONCURRENT_MODIFICATION'
},
message: 'Multiple requests were attempting to modify the same resource at once. Retry the request.'
}
],
requestId: 'OZ5_72C-3qFN9a87mjE7_w'
}
],
message: 'Request contains an invalid argument.',
status: 'INVALID_ARGUMENT'
}
})

const responses = testDestination.testBatchAction('userList', {
events,
mapping: {
ad_user_data_consent_state: 'GRANTED',
ad_personalization_consent_state: 'GRANTED',
external_audience_id: '1234',
retlOnMappingSave: {
outputs: {
id: '1234',
name: 'Test List',
external_id_type: 'CONTACT_INFO'
}
}
},
useDefaultMappings: true,
settings: {
customerId
}
})

await expect(responses).rejects.toThrowError(RetryableError)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -551,24 +551,27 @@ const createOfflineUserJob = async (
return (response.data as any).resourceName
} catch (error) {
statsContext?.statsClient?.incr('error.createJob', 1, statsContext?.tags)
handleGoogleAdsError(error)
}
}

// Google throws 400 error for CONCURRENT_MODIFICATION error which is a retryable error
// We rewrite this error to a 500 so that Centrifuge can retry the request
const errors = (error as GoogleAdsError).response?.data?.error?.details ?? []
for (const errorDetails of errors) {
for (const errorItem of errorDetails.errors) {
if (errorItem?.errorCode?.databaseError) {
throw new RetryableError(
errorItem?.message ??
'Multiple requests were attempting to modify the same resource at once. Retry the request.',
500
)
}
const handleGoogleAdsError = (error: any) => {
// Google throws 400 error for CONCURRENT_MODIFICATION error which is a retryable error
// We rewrite this error to a 500 so that Centrifuge can retry the request
const errors = (error as GoogleAdsError).response?.data?.error?.details ?? []
for (const errorDetails of errors) {
for (const errorItem of errorDetails.errors) {
if (errorItem?.errorCode?.databaseError) {
throw new RetryableError(
errorItem?.message ??
'Multiple requests were attempting to modify the same resource at once. Retry the request.',
500
)
}
}

throw error
}

throw error
}

const addOperations = async (
Expand Down Expand Up @@ -597,23 +600,7 @@ const addOperations = async (
return response.data
} catch (error) {
statsContext?.statsClient?.incr('error.addOperations', 1, statsContext?.tags)

// Google throws 400 error for CONCURRENT_MODIFICATION error which is a retryable error
// We rewrite this error to a 500 so that Centrifuge can retry the request
const errors = (error as GoogleAdsError).response?.data?.error?.details ?? []
for (const errorDetails of errors) {
for (const errorItem of errorDetails.errors) {
if (errorItem?.errorCode?.databaseError) {
throw new RetryableError(
errorItem?.message ??
'Multiple requests were attempting to modify the same resource at once. Retry the request.',
500
)
}
}
}

throw error
handleGoogleAdsError(error)
}
}

Expand All @@ -636,22 +623,7 @@ const runOfflineUserJob = async (
return response.data
} catch (error) {
statsContext?.statsClient?.incr('error.runJob', 1, statsContext?.tags)
// Google throws 400 error for CONCURRENT_MODIFICATION error which is a retryable error
// We rewrite this error to a 500 so that Centrifuge can retry the request
const errors = (error as GoogleAdsError).response?.data?.error?.details ?? []
for (const errorDetails of errors) {
for (const errorItem of errorDetails.errors) {
if (errorItem?.errorCode?.databaseError) {
throw new RetryableError(
errorItem?.message ??
'Multiple requests were attempting to modify the same resource at once. Retry the request.',
500
)
}
}
}

throw error
handleGoogleAdsError(error)
}
}

Expand Down

0 comments on commit 35edd08

Please sign in to comment.