fix: SQLite upsert race condition in save_or_update_jargon#205
Merged
EterUltimate merged 1 commit intoJun 13, 2026
Merged
Conversation
Wrap the SELECT→INSERT/UPDATE logic in a retry loop so that when concurrent requests trigger a UNIQUE constraint violation on INSERT, the retry finds the record the other request just committed and takes the UPDATE path instead. Fixes: sqlite3.IntegrityError: UNIQUE constraint failed: jargon.chat_id, jargon.content
Contributor
Reviewer's GuideAdds a small retry loop around the SQLite-specific SELECT→INSERT/UPDATE path in save_or_update_jargon so that concurrent upserts that hit a UNIQUE constraint on INSERT will retry, re-select the just-committed row, and fall back to the UPDATE path instead of failing with IntegrityError. Sequence diagram for SQLite retry logic in save_or_update_jargonsequenceDiagram
actor Caller
participant JargonFacade
participant SQLite
Caller->>JargonFacade: save_or_update_jargon(chat_id, content, jargon_data)
loop attempts up to 2
JargonFacade->>JargonFacade: get_session()
JargonFacade->>SQLite: SELECT Jargon WHERE chat_id, content
SQLite-->>JargonFacade: record not found
JargonFacade->>SQLite: INSERT Jargon using _jargon_insert_values
alt UNIQUE constraint violated
SQLite-->>JargonFacade: IntegrityError
JargonFacade-->>JargonFacade: catch IntegrityError
JargonFacade-->>JargonFacade: _logger.debug("并发竞争导致 UNIQUE 冲突,重试")
note over JargonFacade: next loop iteration (retry)
else insert succeeds
SQLite-->>JargonFacade: commit
JargonFacade-->>Caller: return new_record.id
end
end
alt after retry, row exists
JargonFacade->>JargonFacade: get_session()
JargonFacade->>SQLite: SELECT Jargon WHERE chat_id, content
SQLite-->>JargonFacade: existing record
JargonFacade->>SQLite: UPDATE Jargon fields
SQLite-->>JargonFacade: commit
JargonFacade-->>Caller: return record.id
else final attempt still IntegrityError
JargonFacade-->>Caller: raise IntegrityError
end
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The retry logic currently treats all IntegrityError instances as concurrent UNIQUE conflicts; consider checking the error code/message and only retrying on UNIQUE violations so that other integrity issues (e.g., NOT NULL, foreign key) are not silently retried and obscured.
- Since this block is explicitly the SQLite path, it may be simpler and more robust to replace the manual SELECT→INSERT/UPDATE + retry pattern with SQLite’s native UPSERT (
ON CONFLICT(chat_id, content) DO UPDATE) via SQLAlchemy’sinsert().on_conflict_do_update, which inherently avoids this race.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The retry logic currently treats all IntegrityError instances as concurrent UNIQUE conflicts; consider checking the error code/message and only retrying on UNIQUE violations so that other integrity issues (e.g., NOT NULL, foreign key) are not silently retried and obscured.
- Since this block is explicitly the SQLite path, it may be simpler and more robust to replace the manual SELECT→INSERT/UPDATE + retry pattern with SQLite’s native UPSERT (`ON CONFLICT(chat_id, content) DO UPDATE`) via SQLAlchemy’s `insert().on_conflict_do_update`, which inherently avoids this race.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
EterUltimate
approved these changes
Jun 13, 2026
EterUltimate
left a comment
Collaborator
There was a problem hiding this comment.
LGTM. CI is green and the SQLite upsert race-condition fix has been reviewed.
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.
Description:
Wrap the SELECT→INSERT/UPDATE logic in a retry loop so that when
concurrent requests trigger a UNIQUE constraint violation on INSERT,
the retry finds the record the other request just committed and takes
the UPDATE path instead.
Fixes: sqlite3.IntegrityError: UNIQUE constraint failed: jargon.chat_id, jargon.content
Summary
修复
save_or_update_jargon在 SQLite 模式下的并发竞争问题。当多个请求同时触发同一个(chat_id, content)黑话词汇时,原逻辑因 TOCTOU 竞争导致重复 INSERT 并抛出
IntegrityError。通过捕获异常并重试一次,使重试的 SELECT能命中并发请求刚提交的记录并走 UPDATE 分支。
Changes
IntegrityError后自动重试,重试时 SELECT 可命中已提交记录并走 UPDATERelated Issues
Type of Change
Checklist
Summary by Sourcery
Handle concurrent upserts of jargon records in SQLite without raising UNIQUE constraint errors.
Bug Fixes:
Enhancements: