Skip to content

Add concurrency control #13

Description

@simonforsberg

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e

echo "=== Locking/version support ==="
rg -n -C2 '@Version|@Lock|LockModeType|version\b' \
  src/main/java/org/example/alfs/entities \
  src/main/java/org/example/alfs/repositories

echo
echo "=== Ticket mutation methods ==="
rg -n -C3 'updateTicketStatus|assignInvestigator|unassignInvestigator' \
  src/main/java/org/example/alfs/services/TicketService.java

Repository: ithsjava25/project-backend-alfs

Length of output: 106


🏁 Script executed:

#!/bin/bash
set -e

echo "=== Ticket entity structure ==="
fd 'Ticket\.java$' src/main/java/org/example/alfs/entities --type f | head -5 | xargs -I {} sh -c 'echo "File: {}"; cat -n "{}"'

echo
echo "=== TicketRepository ==="
fd 'TicketRepository\.java$' src/main/java/org/example/alfs/repositories --type f | head -5 | xargs -I {} sh -c 'echo "File: {}"; cat -n "{}"'

echo
echo "=== Full TicketService.updateTicketStatus method ==="
rg -n -A 20 'public.*updateTicketStatus' src/main/java/org/example/alfs/services/TicketService.java

echo
echo "=== Full TicketService.assignInvestigator method ==="
rg -n -A 20 'public.*assignInvestigator' src/main/java/org/example/alfs/services/TicketService.java

echo
echo "=== Full TicketService.unassignInvestigator method ==="
rg -n -A 20 'public.*unassignInvestigator' src/main/java/org/example/alfs/services/TicketService.java

Repository: ithsjava25/project-backend-alfs

Length of output: 8064


These ticket mutations are race-prone and need concurrency control.

All three mutation methods (updateTicketStatus, assignInvestigator, unassignInvestigator) perform read-check-write sequences on the same Ticket row without concurrency guards. Two concurrent requests can both pass precondition checks and then overwrite each other—last-write-wins. For example, two calls to assignInvestigator() can both observe ticket.getInvestigator() == null, both pass the guard at line 159, and then both attempt to save, with the second request overwriting the first.

The Ticket entity has no @Version field for optimistic locking, and TicketRepository exposes no locked query methods, so there is currently no mechanism to prevent these collisions.

Add optimistic locking (@Version on Ticket) or use pessimistic locking when fetching the row in these mutation flows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/alfs/services/TicketService.java` around lines 108
- 129, The three mutation flows (updateTicketStatus, assignInvestigator,
unassignInvestigator) perform read-check-write on the same Ticket and are
race-prone; fix by adding concurrency control: either add an optimistic lock
field to the Ticket entity (a long/int field annotated with `@Version`) and let
JPA detect concurrent updates on ticketRepository.save, or change the repository
access in those methods to fetch the row with a pessimistic lock (e.g., a new
repository method or using EntityManager.find with
LockModeType.PESSIMISTIC_WRITE) so the precondition checks and the subsequent
save happen under a lock; update the code paths in updateTicketStatus,
assignInvestigator, and unassignInvestigator to use the chosen locking strategy
and handle OptimisticLockException / lock-related exceptions appropriately.

Originally posted by @coderabbitai[bot] in #12 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions