Locking in Spring Boot

Published On: 28 April 2023.By .
  • General

When multiple users try to access and modify the same data simultaneously, concurrency issues can arise. In a database system, locks are used to prevent multiple users from modifying the same data simultaneously. There are different types of locks such as shared lock, optimistic lock, and pessimistic lock that can be used to prevent concurrency issues.

In this blog post, we will discuss these locks in detail and their implementation in a database system.

Shared Lock

A shared lock also known as a read lock allows multiple transactions to read specific data simultaneously. However, it restricts transactions from modifying the data until the shared lock is released. Shared locks are useful in scenarios where multiple users need to read the same data, and there is no need for write operations.

For example, consider a scenario where multiple users try to read the same data simultaneously, such as a product catalog on an e-commerce website. In this scenario, shared locks can be used to allow multiple users to read the product catalog simultaneously without the risk of concurrent modifications.

In Spring Boot, shared locks can be implemented using the @Lock annotation with the LockModeType.PESSIMISTIC_READ parameter. Here’s an example implementation of shared locks in Spring Boot:

In the above example, the findElectronics method applies a shared lock on the selected rows and allows other transactions to read the same rows but not modify them.

Pessimistic Locking

Pessimistic locking is a technique used to prevent concurrency issues by acquiring locks on data before performing any operations. In pessimistic locking, a transaction acquires a lock on data before reading or modifying it. The lock prevents other transactions from modifying the same data simultaneously. Pessimistic locking is useful in scenarios where conflicts are frequent, and the cost of retrying transactions is high.

For example, consider a scenario where multiple users try to update the same record in a database, and conflicts are frequent. In this scenario, pessimistic locking can be used to prevent concurrency issues by acquiring locks on data before performing any operations.

To implement pessimistic locking in Spring Boot, we can use the @Lock annotation with the LockModeType.PESSIMISTIC_WRITE parameter. This annotation applies a pessimistic lock on the selected rows and prevents other transactions from modifying them until the lock is released.

In the above example, the findByIdForUpdate method applies a pessimistic lock on the selected row and prevents other transactions from modifying it until the lock is released.

When using pessimistic locking, if another transaction attempts to modify the same locked data, it will be blocked until the lock is released. The blocking transaction will receive a LockAcquisitionException when attempting to modify the data. This exception indicates that the transaction was unable to acquire the lock and therefore cannot proceed with the modification. By catching and handling this exception, you can choose how to handle the blocking transaction, such as retrying the operation or informing the user that the operation was unsuccessful.

Optimistic Locking

Optimistic locking is a technique used to prevent concurrency issues by detecting conflicts when updating data. In optimistic locking, a transaction reads data without acquiring a lock, and before committing the changes, it checks if the data has been modified by another transaction. If the data has been modified, the transaction aborts and retries the operation. Optimistic locking is useful in scenarios where conflicts are rare, and the cost of retrying transactions is low.

For example, consider a scenario where multiple users try to update the same record in a database. In this scenario, optimistic locking can be used to prevent concurrency issues by detecting conflicts when updating data.

To implement optimistic locking in Spring Boot, we can use the @Version annotation on the entity class. This annotation adds a version attribute to the entity, and Spring Data JPA uses it to perform optimistic locking.

In the above example, the @Version annotation adds a version attribute to the entity, and Spring Data JPA uses it to perform optimistic locking.

@Version annotation instructs Hibernate to include a version number in the SQL statements it generates for the entity. When a transaction updates an entity, Hibernate compares the version number of the entity being updated with the version number in the database. If the version numbers match, the update proceeds and the version number is incremented. If they don’t match, an OptimisticLockException is thrown.

In the above code, the updateProductPrice method retrieves the product from the database using the ProductRepository and sets a new price on it. When the save method is called on the productRepository, Hibernate checks the version number of the product in the database against the version number of the product being updated. If the version numbers match, the update process will continue, and the version number will be incremented. If they don’t match, an OptimisticLockException is thrown.

Differences :

  1. Usage: Shared locking is utilized when multiple users need to read a resource concurrently, but only one user can modify the resource at a time. Pessimistic locking is employed when conflicts between users are frequent, and acquiring locks is relatively easy. In contrast, optimistic locking is used when conflicts between users are infrequent, and acquiring locks is relatively expensive.
  2. Lock acquisition: With shared locking, multiple users can acquire shared locks on the resource to read from it concurrently. With pessimistic locking, a user acquires an exclusive lock on the resource to prevent other users from modifying it. With optimistic locking, a user does not acquire a lock on the resource, but instead assumes that no other user is modifying the same data.
  3. Lock release: With shared locking, a user releases their shared lock on the resource once they have finished reading it. With pessimistic locking, a user releases their exclusive lock on the resource once they have finished modifying it. In optimistic locking, a user does not release any lock, but instead, checks if another user has modified the resource before attempting to save changes.
  4. Performance: Shared locking allows for high concurrency and performance when reading from a resource, but can result in contention and lower performance when modifying the resource. Pessimistic locking ensures that only one user can modify the resource at a time, but can result in lower concurrency and performance. Optimistic locking can result in higher concurrency and performance when modifying a resource, but can lead to conflicts and lower performance if conflicts occur frequently.

When to use which lock?

A common real-life example where shared, pessimistic, and optimistic locks are used is in an e-commerce website that allows multiple users to browse, add products to the cart, and checkout simultaneously.

Suppose a user adds a product to their cart, and at the same time, another user also tries to add the same product to their cart. To ensure data integrity, various types of locks can be utilized in such a scenario.

  • Shared lock: A shared lock can be used when a user is browsing or reading the product information. This allows multiple users to read the same product information at the same time without affecting each other. For example, when a user is browsing a product catalog or viewing a product detail page, a shared lock can be applied to the database record to ensure that the data is not modified while the user is browsing.
  • Pessimistic lock: A pessimistic lock can be used when a user adds a product to their cart. This lock ensures that the data is locked until the user completes the checkout process or cancels the order. This prevents other users from modifying the same data, which could result in double booking of the same product. For example, when a user adds a product to their cart, a pessimistic lock can be applied to the database record to prevent other users from adding the same product to their carts.
  • Optimistic lock: An optimistic lock can be used when a user updates the product quantity in their cart. This lock checks if the data has been modified by another user since the user last read the data. If the data has not been modified, the update is allowed to proceed. Otherwise, the user is notified that the data has been modified and needs to be updated again. For example, when a user updates the quantity of a product in their cart, an optimistic lock can be applied to the database record to ensure that the data has not been modified by another user since the user last read the data.

Overall, the use of different types of locks ensures that multiple users can use the e-commerce website simultaneously without interfering with each other’s actions and maintaining the consistency and integrity of the data.

Conclusion:

In conclusion, database locking is an essential technique to ensure the consistency and reliability of data in multi-user environments. In Spring Boot, we can implement shared locks, optimistic locking, and pessimistic locking using Spring Data JPA’s built-in features. However, it’s important to use these locking mechanisms judiciously and with caution, as overusing them can cause performance issues and deadlocks. Therefore, it’s essential to analyze the application’s requirements and choose the appropriate locking mechanism accordingly.

Related content

That’s all for this blog