Skip to content
广告❤️成为赞助商

Optimistic Lock

Optimistic lock is a concurrency control mechanism used to ensure that when a record is updated, it has not been modified by other transactions. MongoPlus provides the OptimisticLockerInterceptor plugin, making it simple to implement optimistic locking in your application.

How Optimistic Lock Works

The implementation of optimistic lock generally includes the following steps:

  1. When reading a record, get the current version number (version).
  2. When updating the record, pass this version number along.
  3. During the update operation, set the condition version = newVersion as version = oldVersion.
  4. If the version number does not match, the update fails.

Configuring the Optimistic Lock Plugin

To use the optimistic lock plugin, two steps are required:

1. Configure the Plugin

java
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
    OptimisticLockerInterceptor optimisticLockerInterceptor = new OptimisticLockerInterceptor();
    // Whether to throw an exception when the update fails
    optimisticLockerInterceptor.setUpdateFailException(new RuntimeException("Update failed"));
    // Whether to throw an exception when the version field is not obtained
    optimisticLockerInterceptor.setVersionIsNullException(new MongoPlusException("Version field not obtained"));
    // Set the increment value for the optimistic lock
    optimisticLockerInterceptor.setAutoInc(1);
    // Enable retry
    optimisticLockerInterceptor.enableRetry(getRetryStrategy());
    return optimisticLockerInterceptor;
}

/**
 * Get retry strategy
 */
Retry getRetryStrategy() {
    return Retry.builder()
            // Maximum retry count
            .maxRetryNum(10)
            // Callback on first hit
            .hitRetry(updateRetryResult -> System.out.println("First hit: " + updateRetryResult))
            // Callback on successful update
            .onSuccess(object -> System.out.println("Update successful: " + object))
            // Callback on failed update
            .onFailure(object -> System.out.println("Update failed: " + object))
            // Fallback logic after reaching maximum retries
            .fallback(((updateRetryResult, invocation) -> {
                try {
                    return invocation.proceed();
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }))
            // Increase version number on each retry
            .autoVersionNum(1)
            // Whether to continue executing subsequent interceptors during retry (default true)
            // If subsequent interceptors perform other operations, set this to false.
            // For example, if subsequent interceptors modify other tables, retry will modify them multiple times.
            .processIntercept(true)
            // Retry interval
            .retryInterval(100L)
            // Enable asynchronous retry
            // When async retry is enabled, the current update failure response is returned immediately,
            // and the asynchronous retry is not under transaction control. You can use callbacks to monitor the final result.
            .asyncRetry(true)
            .build();
}

2. Add @Version Annotation on Entity Field

In your entity class, add the @Version annotation on the field representing the version number:

java
import com.mongoplus.annotation.collection.Version;

public class YourEntity {
    @Version
    private Integer version;
    // Other fields...
}

Notes

  • Only int type is supported.
  • Supported methods include built-in updateById(entity), update(entity, wrapper), saveOrUpdate(entity). You can also manually set updateWrapper.set("version",0) via a condition builder.
  • In update(entity, wrapper) method, the wrapper cannot be reused.

With the above configuration and the @Version annotation on your entity class, you can easily implement optimistic locking in MongoPlus, effectively preventing data conflicts during concurrent updates.