Real Case of Spring
Global Lock Usage
Maksym Voroniy
PhD, Software Architect, Consultant
Simple case
2
Buying book. Analyst vision
- Select book
From big list pick 2-3 books
- Make Order
Provide payment attributes and send request to server
- Validate
Server have to check availability of all books in Store
- Complete transaction
Withdraw money and reduce number of available in Store
3
1: SelectBook
2: CreateOrder
2: Buy 4: Validate
5: Pay
5: RemovePosition
Server Store Bank
User
4
Possible
implementation
5
BookOrder:
payment
isbn
Naive implementation
Store:
amount
isbn
Payment:
amount
transaction
@Transactional // (!)
public void buyBooks(List<Book> books, Payment userPayment){
// Validate:
final String checkSql =
"SELECT 1 FROM Store WHERE isbn = ? AND amount > 1";
// Modify business entities
final String insertPaymentSql =
"INSERT INTO Payment (amount, transaction) VALUES ...";
final String insertOrderSql =
"INSERT INTO BookOrder (payment, isbn) VALUES ...";
final String updateStoreSql =
"UPDATE Store SET amount = amount -1 WHERE isbn = ?";
}
Read uncommitted | Read committed | SerializableIsolation layers:
6
Better implementation
@Transactional
public void buyBooks(List<Book> books, Payment userPayment){
// Validate:
final String checkSql =
"SELECT 1 FROM Store WHERE isbn = ? AND amount > 1 FOR UPDATE";
// Modify business entities
final String insertPaymentSql =
"INSERT INTO Payment (amount, transaction) VALUES ...";
final String insertOrderSql =
"INSERT INTO BookOrder (payment, isbn) VALUES ...";
final String updateStoreSql =
"UPDATE Store SET amount = amount -1 WHERE isbn = ?";
}
7
Analyze it…
- What if bank
confirmation take more
than 40 sec?
- What if Store, Order
management are
micro-services?
8
Spring Cloud
goes to stage
9
Spring Global Lock
Sexy small code
@Autowired
private LockRegistry lockRegistry;
@Transactional
public void buyBooks2(List<Book> books, Payment userPayment){
Lock lock = lockRegistry.obtain(userPayment.getUserId());
lock.lock(); // lock.tryLock();
try{
doInsertAndUpdate();
} catch (InterruptedException e1){
//give up
Thread.currentThread().interrupt();
} finally{
lock.unlock();
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
10
BookOrder:
payment
isbn
Under the hood
Store:
amount
isbn
Payment:
amount
transaction
Lock:
when
key
version
- GemfireLockRegistry
- JdbcLockRegistry
- RedisLockRegistry
- PassThruLockRegistry
- ZookeeperLockRegistry
11
Go to distributed environment
Broken code
Lock:
when
key
version
(c) by http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
12
Go to distributed environment
Final code
Lock:
when
key
version
(c) by http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
13
Spring Global Lock
Manage Expiration
@Autowired
private LockRegistry lockRegistry;
@Autowired
private ExpirableLockRegistry lockRegistry;
@Scheduled(fixedDelay=50000)
public void cleanObsolete(){
lockRegistry.expireUnusedOlderThan(50000);//that are not currently locked
}
14
Global Lock Important use-case
Leader Election
LockRegistryLeaderInitiator:
- Uses global lock with
expiry,
- There will never be more
than one leader
- May occasionally be no
leader for short periods
15
Summary
- Use in distributed
environment (microservices
for example);
- SQL DB is good and faster
alternative;
- NoSQL DB may not
+
16
Summary
- Use in distributed
environment (microservices
for example);
- SQL DB is good and faster
alternative;
- NoSQL DB may not
- Timeouts matters
- Your code should
handle fail
- May slow down system
+ -
17
Thank you!
maksym.voroniy@globallogic.com

Spring Cloud. The Real Case of Spring Global Lock Usage

  • 1.
    Real Case ofSpring Global Lock Usage Maksym Voroniy PhD, Software Architect, Consultant
  • 2.
  • 3.
    Buying book. Analystvision - Select book From big list pick 2-3 books - Make Order Provide payment attributes and send request to server - Validate Server have to check availability of all books in Store - Complete transaction Withdraw money and reduce number of available in Store 3
  • 4.
    1: SelectBook 2: CreateOrder 2:Buy 4: Validate 5: Pay 5: RemovePosition Server Store Bank User 4
  • 5.
  • 6.
    BookOrder: payment isbn Naive implementation Store: amount isbn Payment: amount transaction @Transactional //(!) public void buyBooks(List<Book> books, Payment userPayment){ // Validate: final String checkSql = "SELECT 1 FROM Store WHERE isbn = ? AND amount > 1"; // Modify business entities final String insertPaymentSql = "INSERT INTO Payment (amount, transaction) VALUES ..."; final String insertOrderSql = "INSERT INTO BookOrder (payment, isbn) VALUES ..."; final String updateStoreSql = "UPDATE Store SET amount = amount -1 WHERE isbn = ?"; } Read uncommitted | Read committed | SerializableIsolation layers: 6
  • 7.
    Better implementation @Transactional public voidbuyBooks(List<Book> books, Payment userPayment){ // Validate: final String checkSql = "SELECT 1 FROM Store WHERE isbn = ? AND amount > 1 FOR UPDATE"; // Modify business entities final String insertPaymentSql = "INSERT INTO Payment (amount, transaction) VALUES ..."; final String insertOrderSql = "INSERT INTO BookOrder (payment, isbn) VALUES ..."; final String updateStoreSql = "UPDATE Store SET amount = amount -1 WHERE isbn = ?"; } 7
  • 8.
    Analyze it… - Whatif bank confirmation take more than 40 sec? - What if Store, Order management are micro-services? 8
  • 9.
  • 10.
    Spring Global Lock Sexysmall code @Autowired private LockRegistry lockRegistry; @Transactional public void buyBooks2(List<Book> books, Payment userPayment){ Lock lock = lockRegistry.obtain(userPayment.getUserId()); lock.lock(); // lock.tryLock(); try{ doInsertAndUpdate(); } catch (InterruptedException e1){ //give up Thread.currentThread().interrupt(); } finally{ lock.unlock(); } } <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-core</artifactId> </dependency> 10
  • 11.
    BookOrder: payment isbn Under the hood Store: amount isbn Payment: amount transaction Lock: when key version -GemfireLockRegistry - JdbcLockRegistry - RedisLockRegistry - PassThruLockRegistry - ZookeeperLockRegistry 11
  • 12.
    Go to distributedenvironment Broken code Lock: when key version (c) by http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html 12
  • 13.
    Go to distributedenvironment Final code Lock: when key version (c) by http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html 13
  • 14.
    Spring Global Lock ManageExpiration @Autowired private LockRegistry lockRegistry; @Autowired private ExpirableLockRegistry lockRegistry; @Scheduled(fixedDelay=50000) public void cleanObsolete(){ lockRegistry.expireUnusedOlderThan(50000);//that are not currently locked } 14
  • 15.
    Global Lock Importantuse-case Leader Election LockRegistryLeaderInitiator: - Uses global lock with expiry, - There will never be more than one leader - May occasionally be no leader for short periods 15
  • 16.
    Summary - Use indistributed environment (microservices for example); - SQL DB is good and faster alternative; - NoSQL DB may not + 16
  • 17.
    Summary - Use indistributed environment (microservices for example); - SQL DB is good and faster alternative; - NoSQL DB may not - Timeouts matters - Your code should handle fail - May slow down system + - 17
  • 18.