fix(query): deleteOutdatedRecords transaction handling#5656
Open
agusayerza wants to merge 2 commits intomasterfrom
Open
fix(query): deleteOutdatedRecords transaction handling#5656agusayerza wants to merge 2 commits intomasterfrom
deleteOutdatedRecords transaction handling#5656agusayerza wants to merge 2 commits intomasterfrom
Conversation
agusayerza
commented
Mar 17, 2026
| return db.transaction(async (trx) => { | ||
| const now = trx.fn.now(6); | ||
| // Lock to prevent concurrent modifications with upserts and deletes | ||
| await trx.raw(`SELECT pg_advisory_xact_lock(?) as lock_records_outdated`, [newLockId(connectionId, model)]); |
Contributor
Author
There was a problem hiding this comment.
Unsure if the lock is really needed to delete stale records
Contributor
There was a problem hiding this comment.
Suggested guarding error checks to avoid TypeError and preserve retry logic.
Status: Changes Suggested | Risk: Medium
Issues Identified & Suggestions
- Guard error object before
'code' in errcheck to avoid TypeError:packages/records/lib/models/records.ts
Review Details
📁 1 files reviewed | 💬 1 comments
Instruction Files
└── .claude/
├── agents/
│ └── nango-docs-migrator.md
└── skills/
├── agent-builder-skill/
│ ├── EXAMPLES.md
│ └── SKILL.md
├── creating-integration-docs/
│ └── SKILL.md
└── creating-skills-skill/
└── SKILL.md
👍 / 👎 individual comments to help improve reviews for you
TBonnin
reviewed
Mar 17, 2026
| span.setTag('error', err); | ||
| if (deletedIds.length > 0) { | ||
| logger.error(`${e.message}. Partial progress: ${deletedIds.length} records deleted before failure.`); | ||
| return Ok(deletedIds); |
Collaborator
There was a problem hiding this comment.
that's problematic. Sync will be considered successful and webhook sent to customer accordingly even though some of the records that are outdated haven't been deleted.
TBonnin
reviewed
Mar 17, 2026
| const e = new Error(`Failed to mark previous generation records as deleted for connection ${connectionId}, model ${model}, generation ${generation}`, { | ||
| cause: err | ||
| }); | ||
| span.setTag('error', err); |
Collaborator
There was a problem hiding this comment.
I realize here we are passing err instead of e
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.
Fix
deleteOutdatedRecordstransaction handlingFixes NAN-5045
Problem
We've been seeing a major increase in persist errors ("Failed to save record" / "Failed to delete record") rooted in DB timeouts, with spikes correlating to high DB stress (CPU >85%, block read times 10-20min, IO:DataFileRead wait events).
The main culprit identified via Datadog APM is the
DELETE .../outdatedendpoint (deleteOutdatedRecords): 57.8k requests, 4.43s p95 latency spiking to 24s, 0.4% error rateThe issue is structural:
Other things to consider:
upsert()anddeleteRecords()acquirepg_advisory_xact_lock, this function did not. As we are just removing old records, Im not sure if this is necessary tbhupsert()which retries on PostgreSQL deadlock errors (40P01), a deadlock here would fail the entire operation (once again, not critical and not something I've seen happen but it would fail the sync)Fix
packages/records/lib/models/records.tsdeleteOutdatedRecords:pg_advisory_xact_lockusing the samenewLockId(connectionId, model)asupsert()anddeleteRecords(), ensuring clean serialization.upsert()pattern.The update also introduces partial-progress handling so that if a later batch fails, already-deleted records can still be returned successfully with appropriate logging.
This summary was automatically generated by @propel-code-bot