mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-13 12:14:51 +00:00
1 parent
70692f647f
commit
8b11e76f46
9 files changed
+399
No files matched your search
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: Optimistic Offline Lock
|
||||
category: Concurrency
|
||||
language: en
|
||||
tag:
|
||||
- Data access
|
||||
---
|
||||
|
||||
## Intent
|
||||
|
||||
Provide an ability to avoid concurrent changes of one record in relational databases.
|
||||
|
||||
## Explanation
|
||||
|
||||
Each transaction during object modifying checks equation of object's version before start of transaction
|
||||
and before commit itself.
|
||||
|
||||
**Real world example**
|
||||
> Since people love money, the best (and most common) example is banking system:
|
||||
> imagine you have 100$ on your e-wallet and two people are trying to send you 50$ both at a time.
|
||||
> Without locking, your system will start **two different thread**, each of whose will read your current balance
|
||||
> and just add 50$. The last thread won't re-read balance and will just rewrite it.
|
||||
> So, instead 200$ you will have only 150$.
|
||||
|
||||
**In plain words**
|
||||
> Each transaction during object modifying will save object's last version and check it before saving.
|
||||
> If it differs, the transaction will be rolled back.
|
||||
|
||||
**Wikipedia says**
|
||||
> Optimistic concurrency control (OCC), also known as optimistic locking,
|
||||
> is a concurrency control method applied to transactional systems such as
|
||||
> relational database management systems and software transactional memory.
|
||||
|
||||
**Programmatic Example**
|
||||
Let's simulate the case from *real world example*. Imagine we have next entity:
|
||||
|
||||
```java
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Bank card entity.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Card {
|
||||
|
||||
/**
|
||||
* Primary key.
|
||||
*/
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* Foreign key points to card's owner.
|
||||
*/
|
||||
private long personId;
|
||||
|
||||
/**
|
||||
* Sum of money.
|
||||
*/
|
||||
private float sum;
|
||||
|
||||
/**
|
||||
* Current version of object;
|
||||
*/
|
||||
private int version;
|
||||
}
|
||||
```
|
||||
|
||||
Then the correct modifying will be like this:
|
||||
|
||||
```java
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* Service to update {@link Card} entity.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class CardUpdateService implements UpdateService<Card> {
|
||||
|
||||
private final JpaRepository<Card> cardJpaRepository;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = ApplicationException.class) //will roll back transaction in case ApplicationException
|
||||
public Card doUpdate(Card card, long cardId) {
|
||||
float additionalSum = card.getSum();
|
||||
Card cardToUpdate = cardJpaRepository.findById(cardId);
|
||||
int initialVersion = cardToUpdate.getVersion();
|
||||
float resultSum = cardToUpdate.getSum() + additionalSum;
|
||||
cardToUpdate.setSum(resultSum);
|
||||
//Maybe more complex business-logic e.g. HTTP-requests and so on
|
||||
|
||||
if (initialVersion != cardJpaRepository.getEntityVersionById(cardId)) {
|
||||
String exMessage = String.format("Entity with id %s were updated in another transaction", cardId);
|
||||
throw new ApplicationException(exMessage);
|
||||
}
|
||||
|
||||
cardJpaRepository.update(cardToUpdate);
|
||||
return cardToUpdate;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Applicability
|
||||
|
||||
Since optimistic locking can cause degradation of system's efficiency and reliability due to
|
||||
many retries/rollbacks, it's important to use it safely. They are useful in case when transactions are not so long
|
||||
and does not distributed among many microservices, when you need to reduce network/database overhead.
|
||||
|
||||
Important to note that you should not choose this approach in case when modifying one object
|
||||
in different threads is common situation.
|
||||
|
||||
## Tutorials
|
||||
|
||||
- [Offline Concurrency Control](https://www.baeldung.com/cs/offline-concurrency-control)
|
||||
- [Optimistic lock in JPA](https://www.baeldung.com/jpa-optimistic-locking)
|
||||
|
||||
## Known uses
|
||||
|
||||
- [Hibernate ORM](https://docs.jboss.org/hibernate/orm/4.3/devguide/en-US/html/ch05.html)
|
||||
|
||||
## Consequences
|
||||
|
||||
**Advantages**:
|
||||
|
||||
- Reduces network/database overhead
|
||||
- Let to avoid database deadlock
|
||||
- Improve the performance and scalability of the application
|
||||
|
||||
**Disadvantages**:
|
||||
|
||||
- Increases complexity of the application
|
||||
- Requires mechanism of versioning
|
||||
- Requires rollback/retry mechanisms
|
||||
|
||||
## Related patterns
|
||||
|
||||
- [Pessimistic Offline Lock](https://martinfowler.com/eaaCatalog/pessimisticOfflineLock.html)
|
||||
|
||||
## Credits
|
||||
|
||||
- [Source (Martin Fowler)](https://martinfowler.com/eaaCatalog/optimisticOfflineLock.html)
|
||||
- [Advantages and disadvantages](https://www.linkedin.com/advice/0/what-benefits-drawbacks-using-optimistic)
|
||||
- [Comparison of optimistic and pessimistic locks](https://www.linkedin.com/advice/0/what-advantages-disadvantages-using-optimistic)
|
||||
Reference in new issue
Block a user