Я новичок в отдыхе и пытаюсь написать простой API отдыха, который переносит некоторую сумму из одного источника в другой.У меня нет базы данных, использую карту в качестве источника данных.Мой сервис просто проверяет, достаточно ли источника для перевода, если это так, то я обновляю и источник, и цель.Проблема в том, что я беспокоюсь о нескольких запросах одновременно.Поскольку при наличии нескольких запросов они могут одновременно выполнять проверку с одинаковыми исходными значениями, что приводит к неправильному поведению.
Пример: источник имеет сумму 100, и существует 2 запроса на передачу на сумму 70Обрабатывая оба запроса, они одновременно проверяют, что источник имеет сумму 100, после чего они могут выполнить передачу.
1) Мой ресурс:
public class MyResource {
@Inject
TransferService transferService;
@POST
@Produces(MediaType.APPLICATION_JSON)
public Transfer transfer(Transfer transfer){
return transferService.transfer(transfer);
}
}
2) Источник POJO
public class Source {
private String sourceId;
private double balance;
....
}
3) Служба источника
public class SourceServiceImp implements SourceService {
@Override
public Source getSource(String sourceId) {
Source source = EmbeddedSources.getSource(sourceId);
return source;
}
public boolean deposit(String sourceId, double amount ){
Source source = getSource(sourceId);
source.setBalance(source.getBalance() + amount);
EmbeddedSources.updateSource(sourceId, source);
return true;
}
public boolean withdraw(String sourceId, double amount ){
Source source = getSource(sourceId);
source.setBalance(source.getBalance() - amount);
EmbeddedSources.updateSource(sourceId, source);
return true;
}
}
4) TransferService
public class TransferServiceImpl implements TransferService {
private static SourceService sourceService ;
public TransferServiceImpl(SourceService sourceService){
this.sourceService = sourceService;
}
@Override
public Transfer transfer(Transfer transfer) {
Source sourceSource = sourceService.getSource(transfer.getSourceSourceId());
Source destSource = sourceService.getSource(transfer.getDestSourceId());
double amount = transfer.getAmount();
if(isBalanceEnough(sourceSource, destSource, amount)){
sourceService.withdraw(sourceSource.getSourceId(), amount);
sourceService.deposit(destSource.getSourceId(), amount);
}
transfer.setStatus("VALID");
return transfer;
}
}
5) Встроенная БД
public class EmbeddedSources {
private static Map<String, Source> sourceMap = new ConcurrentHashMap<>();
private static Map<String, Transfer> transferMap = new ConcurrentHashMap<>();
public static Source getSource(String sourceId){
return sourceMap.get(sourceId);
}
public static void updateSource(String sourceId, Source source){
sourceMap.put(sourceId, source);
}
public static void addSource(Source source){
sourceMap.put(source.getSourceId(), source.deepCopy());
}
}
Я думал об использовании очереди для запроса, но считаю, что это не хорошее решение.Также я подумал об использовании volatile для полей класса источника, но все же это не предотвратит проблему многократного запроса для одного и того же sourceId.
Как я могу предотвратить эту проблему и сделать сервис безопасным для нескольких запросов?