Skip to content

Commit

Permalink
optimistic-offline-lock pattern added (issue iluwatar#1306)
Browse files Browse the repository at this point in the history
  • Loading branch information
MathewJaniec committed Nov 17, 2022
1 parent db4fbb3 commit fb79039
Show file tree
Hide file tree
Showing 13 changed files with 1,228 additions and 0 deletions.
65 changes: 65 additions & 0 deletions optimistic-offline-lock/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
layout: pattern
title: Optimistic Offline Lock
folder: optimistic-offline-lock
permalink: /patterns/optimistic-offline-lock/
categories: Concurrency
language: en
tags:
- Performance
---

## Intent
Optimistic Offline Lock pattern helps with data integrity when there is risk of concurrent
actions on the same record of data.

## Explanation
Often a business transaction executes across a series of system transactions. Once outside the
confines of a single system transaction, we can’t depend on our database manager alone to ensure
that the business transaction will leave the record data in a consistent state. Data integrity is
at risk once two sessions begin to work on the same records and lost updates are quite possible.

Optimistic Offline Lock solves this problem by validating that the changes about to be committed
by one session don’t conflict with the changes of another session. A successful pre-commit validation
is, in a sense, obtaining a lock indicating it’s okay to go ahead with the changes to the record data.
So long as the validation and the updates occur within a single system transaction the business
transaction will display consistency.

**Programmatic Example**

The code below shows example of delete operation which check if session's copy of version is the
same as the version in DB. The transaction is committed only when the versions match, an
OptimisticLockException is thrown otherwise.

```java
@Override
public void delete(Customer customer) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = getConnection();
preparedStatement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE id = ? AND version = ?");
preparedStatement.setLong(1, customer.getId());
preparedStatement.setInt(2, customer.getVersion());
if (preparedStatement.executeUpdate() == 0) {
throwOptimisticLockException(customer);
}
} catch (SQLException exception) {
LOGGER.error(exception.getMessage(), exception);
} finally {
closeDbResources(preparedStatement, connection);
}
}
```

## Class diagram
![alt text](./etc/optimistic-offline-lock.urm.png "Optimistic Offline Lock")

## Applicability
Use the Optimistic Offline Lock pattern when the chance of conflict between any two business transactions is low.

## Known uses
* source code management (SCM)

## Credits
* [Optimistic Offline Lock](https://books.google.fi/books?id=vqTfNFDzzdIC&pg=PA416#v=onepage&q&f=false)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions optimistic-offline-lock/etc/optimistic-offline-lock.urm.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@startuml
package com.iluwatar.optimistic.offline.lock.customer {
class Customer {
- firstName : String
- id : long
- lastName : String
- modified : Timestamp
- modifiedBy : String
- serialVersionUID : long {static}
- version : int
+ Customer(id : long, firstName : String, lastName : String)
~ Customer(id : long, firstName : String, lastName : String, modifiedBy : String, modified : Timestamp, version : int)
+ getFirstName() : String
+ getId() : long
+ getLastName() : String
+ getModified() : Timestamp
+ getModifiedBy() : String
+ getVersion() : int
+ setFirstName(firstName : String)
+ setLastName(lastName : String)
+ toString() : String
}
interface CustomerDataMapper {
+ delete(Customer) {abstract}
+ find(long) : Customer {abstract}
+ getAllCustomers() : List<Customer> {abstract}
+ insert(Customer, String) {abstract}
+ update(Customer, String) {abstract}
}
class CustomerDataMapperImpl {
- LOGGER : Logger {static}
- dataSource : DataSource
+ CustomerDataMapperImpl(dataSource : DataSource)
- closeDbResources(preparedStatement : PreparedStatement, connection : Connection)
- closeDbResources(resultSet : ResultSet, preparedStatement : PreparedStatement, connection : Connection)
- createCustomer(resultSet : ResultSet) : Customer
+ delete(customer : Customer)
+ find(id : long) : Customer
+ getAllCustomers() : List<Customer>
- getConnection() : Connection
+ insert(customer : Customer, modifiedBy : String)
- throwConcurrencyException(customer : Customer)
+ update(customer : Customer, modifiedBy : String)
}
class CustomerSchemaSql {
+ CREATE_SCHEMA_SQL : String {static}
+ DELETE_SCHEMA_SQL : String {static}
+ CustomerSchemaSql()
}
}
package com.iluwatar.optimistic.offline.lock {
class App {
- DB_URL : String {static}
- LOGGER : Logger {static}
+ App()
- closeDbResources(statement : Statement, connection : Connection) {static}
- createDataSource() : DataSource {static}
- createSchema(dataSource : DataSource) {static}
- deleteCustomer(customerDataMapper : CustomerDataMapper, id : long, sleepTime : long) {static}
- deleteSchema(dataSource : DataSource) {static}
- deleteUpdatedCustomerConcurrencyExample(customerDataMapper : CustomerDataMapper) {static}
- generateSampleCustomers(customerDataMapper : CustomerDataMapper) {static}
+ main(args : String[]) {static}
- showAllCustomers(customerDataMapper : CustomerDataMapper) {static}
- updateCustomer(customerDataMapper : CustomerDataMapper, id : long, setFirstName : String, setLastName : String, sleepTime : long) {static}
- updateCustomerConcurrencyExample(customerDataMapper : CustomerDataMapper) {static}
- updateDeletedCustomerConcurrencyExample(customerDataMapper : CustomerDataMapper) {static}
- waitForThreadsToFinishExecution(executorService : ExecutorService) {static}
}
}
CustomerDataMapperImpl ..|> CustomerDataMapper
@enduml
67 changes: 67 additions & 0 deletions optimistic-offline-lock/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>optimistic-offline-lock</artifactId>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.optimistic.offline.lock.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading

0 comments on commit fb79039

Please sign in to comment.