Друзья!
Я работаю с Play + Java + Ebean.
У меня проблема с параллельными потоками при попытке выбрать и вставить значения в БД одним способом.
Сначала я проверяю наличие устройства в моей БД (PostgreSQL) по id.
Если я не нашел устройство по идентификатору, я пытаюсь вставить его в БД.
Затем я возвращаю объект устройства.
Все работает отлично, за исключением ситуации, когда два http-запроса асинхронно пытаются отправить мне одинаковые идентификаторы устройства. Во-первых, оба они выбирают значение из БД и ничего не возвращают. Затем один вводит значения, а второй завершается неудачей из-за io.eBean.DuplicateKeyException.
Я пытался синхронизировать метод. Это работает, но мне не нравится это решение. Если я сделаю это, многие запросы будут помещены в очередь. Я хочу оставаться в параллели, но синхронизироваться, только если у меня есть два или более запросов с одинаковым идентификатором.
Другой способ решить эту проблему - написать запрос с помощью INSERT ... WHERE NOT EXISTS (SELECT ...) RETURNING. Но это не объектный стиль, и существует жесткое кодирование для нескольких операций одного типа.
public CompletionStage<Device> getDevice(String deviceID, String ipAddress) {
return CompletableFuture.supplyAsync(() ->
SecurityRepository.findDeviceByDeviceID(deviceID))
.thenApplyAsync(curDevice -> curDevice
.orElseGet(() -> {
List<Device> devices =
SecurityRepository.listDevicesByIpAddress(ipAddress);
if(devices.size() >= 10) {
for(Device device : devices)
device.delete();
}
Device newDevice = new Device();
newDevice.setDeviceID(deviceID);
newDevice.ipAddress = ipAddress;
newDevice.save(); //here is the problem
return newDevice;
})
);
}
Я хочу синхронизировать этот метод, если и только если deviceID одинаков.
У вас есть предложения по этой проблеме?
Спасибо.