Guard Salesforce::RoleSyncJob against missing parent records#850
Draft
fspeirs wants to merge 1 commit into
Draft
Guard Salesforce::RoleSyncJob against missing parent records#850fspeirs wants to merge 1 commit into
fspeirs wants to merge 1 commit into
Conversation
Defer the Contact_Editor_Affiliation__c write until both the parent Editor__c (school) and Contact (user) have been pushed to Salesforce, using the existing SalesforceRecordNotFound retry path. Co-authored-by: Cursor <cursoragent@cursor.com>
Test coverageSimpleCov coverage data was unavailable for this run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Heroku Connect was leaving
Contact_Editor_Affiliation__crows permanentlyFAILEDin the mirror with errors like:This happens because
SchoolandRoleafter_commitcallbacks both fire in the same transaction and enqueue independent jobs (SchoolSyncJobandRoleSyncJob) on thesalesforce_syncqueue. The jobs use different concurrency keys, so the role write to the Heroku Connect mirror can be pushed to Salesforce before the Editor record lands. When that happens the lookup-by-external-id fails, Salesforce rejects the INSERT, and Heroku Connect does not auto-retry foreign-key resolution failures — the mirror row staysFAILEDforever.The same pattern happens against
Contactwhen a role is created before the upstream Pi Accounts → Salesforce Contact pipeline has materialised the user.Evidence from production
Investigating Editors in Salesforce with no
Contact_Editor_Affiliation__c:_hc_err:_hc_err = "Foreign key external ID … not found for field EditorUUID__c in entity Editor__c". The parentEditor__chad since synced (sfidpopulated,_hc_lastop = SYNCED), so the affiliation INSERT would now succeed — but Connect never retries.Contacterror and have noSalesforce::Contactmirror row at all (separate upstream issue, not in scope).00:01:04 UTCtoday for school6a228ff8-…, confirming the race is still actively breaking new affiliations.The 10 recoverable cases were re-synced manually; the SOQL count for not-rejected Editors with no CEA dropped 16 → 6 (3 missing-Contact + 3 by-design schools with only student / no roles).
Fix
Before writing to the affiliation mirror, verify the parent records exist in the local Heroku Connect mirror with
sfidpopulated (i.e. Salesforce has acknowledged them). If either parent isn't ready, raiseSalesforceRecordNotFound.SalesforceSyncJobalready declares:…so this slots into the existing retry mechanism (same one
ContactSyncJobalready relies on) without any new error class, queue, or backoff config.Behaviour
FAILEDpermanentlyFAILEDpermanentlyFAILEDrow in mirrorSALESFORCE_ENABLED=falseWhat this does not fix
Contactin Salesforce — needs investigation upstream of editor-api.FAILEDmirror rows already in production — those need a one-off cleanup (deletesfid: nilrows then re-enqueueRoleSyncJob); already done for the 10 recoverable cases above.Tests
spec/jobs/salesforce/role_sync_job_spec.rb:let!so the new guards pass.Salesforce::Schoolexists withsfid: nil→ raisesSalesforceRecordNotFound(Editor not yet synced)Salesforce::Schoolrow at all → raisesSalesforceRecordNotFoundSalesforce::Contactexists withsfid: nil→ raisesSalesforceRecordNotFound(Contact not yet synced)Salesforce::Contactrow at all → raisesSalesforceRecordNotFoundTest plan
Foreign key external ID … not founderrors oncontact_editor_affiliation__c— should drop to ~0 for new schoolsContact_Editor_Affiliation__crows appear in Salesforce within retry windowMade with Cursor