英文:
Is @Transactional enough to keep concurrency in a table?
问题
I have a table that contains a "stock" of codes to be used, like licenses. The repository is called CodeRepository. That code can be used just once so I need to assign those codes, once per service i'm activating. The repository for the services that use those codes is called ServiceRepository. I wrote the following code:
@Transactional
public void assignFreeCodeToService(Service service) {
Code code = CodeRepository.findFirstByStatus("free").orElseThrow(() -> new ApiException(ApiException.ErrorCode.NOT_FOUND, "No available Code was found"));
// change code status
code.setStatus("assigned");
// assign the code to the service
service.setCode(code.geId());
// save the code new status
codeRepository.save(code);
// save the service with the new code
serviceRepository.save(service);
}
So, when i'm activating a service, this will assign a "code" and save it. What I want to know, if this code is enough to keep concurrency if two or more services are being activated at the same time. Will this code to prevent to use the same code twice?
英文:
I have a table that contains a "stock" of codes to be used, like licenses. The repository is called CodeRepository. That code can be used just once so I need to assign those codes, once per service i'm activating. The repository for the services that use those codes is called ServiceRepository. I wrote the following code:
@Transactional
public void assignFreeCodeToService(Service service) {
Code code = CodeRepository.findFirstByStatus("free").orElseThrow(() -> new ApiException(ApiException.ErrorCode.NOT_FOUND, "No available Code was found"));
// change code status
code.setStatus("assigned");
// assign the code to the service
service.setCode(code.geId());
// save the code new status
codeRepository.save(code);
// save the service with the new code
serviceRepository.save(service);
}
So, when i'm activating a service, this will assign a "code" and save it. What I want to know, if this code is enough to keep concurrency if two or more services are being activated at the same time. Will this code to prevent to use the same code twice?
答案1
得分: 1
答案:
不。理由如下:
- 在“读已提交”隔离级别(这是大多数成熟数据库的默认级别)的情况下,您的代码会面临“丢失更新现象”:它获取具有“status=free”的“Code”,并将“status”更新为“acquired”,而不考虑在获取和更新之间代码可能已经发生了更改。
- 在“可重复读”隔离级别(默认为“MySQL”)或更高级别的情况下,您的代码可能会抛出诸如“由于并发更新而无法序列化访问”的错误,您应该准备好以某种方式减轻此类错误。
建议使用@nathan-hughes的建议来使用“行级锁”是一个很好的起点,然而“spring-data” API没有提供足够的控制来使代码更具并发性。
英文:
Answering the initial Q:
> What I want to know, if this code is enough to keep concurrency if two or more services are being activated at the same time. Will this code to prevent to use the same code twice?
No. The reasoning is following:
- in case of
read committed
isolation level (which is the default for the most mature DB's) your code is facing withlost update phenomena
: it acquiresCode
s which havestatus=free
and updatesstatus
toacquired
not taking into account the fact the code has been potentially changed between acquisition and update - in case of
repeatable read
isolation level (default forMySQL
) or higher you code may throw dumb errors likecould not serialize access due to concurrent update
and you should be ready to somehow mitigate such errors.
Suggestion of @nathan-hughes to use row-level locks
is a good starting point, however spring-data
API does not provide enough control over locking behaviour to make the code more concurrent.
答案2
得分: 0
@Transactional
用于实现本地事务。它会使数据库操作隔离。所以如果环境是独立的,你可以使用它。
英文:
@Transactional
is used to implement local affair. It will make operation database isolate.So if the environment is stand-alone, you can use it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论