Сегодня предлагаю Вам разобрать одну практическую задачу о гонке клиентских запросов, с которой я столкнулся в МаксимаТелеком при разработке back-end для нашего мобильного приложения MT_FREE.
При старте клиентское приложение асинхронно отправляет «пачку» запросов к API. Приложение имеет идентификатор clientId, на основании которого можно различить запросы одного клиента от другого. На каждый запрос на сервере выполняется код вида:
// получаем из репозитория данные клиента
Client client = clientRepository.findByClientId(clientId);
// если клиент не создан ранее
if(client == null){
client = clientRepository.save(new Client(clientId));
}
// далее обрабатываем запрос
где сущность Client имеет поле clientId, которое должно являться уникальным и имеет для этого в базе unique constraint. Так как в Spring каждый запрос будет выполнять данный код в отдельном потоке, даже если это запросы от одного и того же клиентского приложения, будет возникать ошибка вида:
integrity constraint violation: unique constraint or index violation; UK_BFJDOY2DPUSSYLQ7G1S3S1TN8 table: CLIENT
Ошибка возникает по очевидной причине: 2 или более потоков с одним clientId получают сущность client == null и начинают ее создавать, после чего при коммите получают ошибку.
Задача:
Необходимо синхронизировать запросы от одного clientId так, чтобы только первый запрос выполнил создание сущности Client, а остальные блокировались бы на момент создания и получали бы уже созданный им объект.