Skip to content

fix: atomic position calculation in pin/channel creation#196

Open
gusye1234 wants to merge 1 commit into
mainfrom
fix/149-race-condition-position
Open

fix: atomic position calculation in pin/channel creation#196
gusye1234 wants to merge 1 commit into
mainfrom
fix/149-race-condition-position

Conversation

@gusye1234

Copy link
Copy Markdown
Contributor

Summary

  • Replaces two-step read-then-write position calculation with atomic subquery INSERT
  • Prevents race condition where concurrent calls could get duplicate positions

Changes

  • src/shared/src/db/queries/agent-pin.ts: pinAgent() now uses subquery for position
  • src/shared/src/db/queries/channel.ts: createChannel() now uses subquery for position

Test plan

  • Create two pins simultaneously and verify no duplicate positions
  • Create two channels simultaneously and verify no duplicate positions

Closes #149

…tion

Previously, MAX(position) was read and then used in a separate INSERT,
allowing concurrent calls to get duplicate positions. Now uses a
subquery in the INSERT to atomically calculate the next position.

Closes #149
@gusye1234 gusye1234 requested a review from a team as a code owner May 28, 2026 09:27

@gusye1234 gusye1234 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — PR #196: atomic position calculation in pin/channel creation

Good fix for a race condition in the position assignment logic. Moving from read-then-write to a single-statement subquery eliminates the window where concurrent inserts could read the same MAX and produce duplicate positions.

  1. pinAgent: subquery computes MAX(position) + 1 within the INSERT VALUES — evaluated atomically before the row is inserted. onConflictDoNothing() still handles duplicate pin attempts.
  2. createChannel: same pattern, scoped by workspaceId.
  3. The COALESCE(..., -1) + 1 default ensures first position is 0, consistent with the prior logic.

The subquery-in-VALUES pattern is valid in both SQLite (D1) and PostgreSQL — the subquery is evaluated before the insert executes, so referencing the same table is safe.

Checklist:

  • Functionality: eliminates duplicate position assignments under concurrency
  • Edge cases: first-ever pin/channel still gets position 0
  • Code quality: actually simpler than before (fewer statements)
  • Code simplification: removed the separate SELECT query — net reduction in code
  • Tests: CI green

LGTM — ready to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition in position calculation (agent-pin, channel)

1 participant