Lock creation of running time entries to prevent race condition

Concurrent requests could both pass the running time entry check and
create more than one running time entry for the same member.
This commit is contained in:
Constantin Graf
2026-10-09 12:58:06 +02:00
parent c994345de9
commit 4a4acb2214
2 changed files with 65 additions and 0 deletions

View File

@@ -51,6 +51,7 @@ use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
@@ -613,6 +614,22 @@ class TimeEntryController extends Controller
$this->checkPermission($organization, 'time-entries:create:all');
}
// Lock the creation of running time entries per user, so that concurrent requests can not create more than one running time entry
$lock = $request->input('end') === null ? Cache::lock('time-entries:running:'.$member->user_id, 10) : null;
$lock?->block(5);
try {
return $this->storeTimeEntry($organization, $member, $request);
} finally {
$lock?->release();
}
}
/**
* @throws TimeEntryStillRunningApiException
*/
private function storeTimeEntry(Organization $organization, Member $member, TimeEntryStoreRequest $request): JsonResource
{
if ($request->input('end') === null && TimeEntry::query()->whereBelongsTo($member, 'member')->where('end', null)->exists()) {
throw new TimeEntryStillRunningApiException;
}