Version Number.
Entity Versioning, Optimistic Locking.
Resolve concurrency conflicts when multiple clients are trying to update same entity simultaneously.
Real world example
Alice and Bob are working on the book, which stored in the database. Our heroes are making changes simultaneously, and we need some mechanism to prevent them from overwriting each other.
In plain words
Version Number pattern grants protection against concurrent updates to same entity.
Wikipedia says
Optimistic concurrency control assumes that multiple transactions can frequently complete without interfering with each other. While running, transactions use data resources without acquiring locks on those resources. Before committing, each transaction verifies that no other transaction has modified the data it has read. If the check reveals conflicting modifications, the committing transaction rolls back and can be restarted.
Programmatic Example
We have a Book
entity, which is versioned, and has a copy-constructor:
1public class Book {
2 private long id;
3 private String title = "";
4 private String author = "";
5
6 private long version = 0; // version number
7
8 public Book(Book book) {
9 this.id = book.id;
10 this.title = book.title;
11 this.author = book.author;
12 this.version = book.version;
13 }
14
15 // getters and setters are omitted here
16}
We also have BookRepository
, which implements concurrency control:
1public class BookRepository {
2 private final Map<Long, Book> collection = new HashMap<>();
3
4 public void update(Book book) throws BookNotFoundException, VersionMismatchException {
5 if (!collection.containsKey(book.getId())) {
6 throw new BookNotFoundException("Not found book with id: " + book.getId());
7 }
8
9 var latestBook = collection.get(book.getId());
10 if (book.getVersion() != latestBook.getVersion()) {
11 throw new VersionMismatchException(
12 "Tried to update stale version " + book.getVersion()
13 + " while actual version is " + latestBook.getVersion()
14 );
15 }
16
17 // update version, including client representation - modify by reference here
18 book.setVersion(book.getVersion() + 1);
19
20 // save book copy to repository
21 collection.put(book.getId(), new Book(book));
22 }
23
24 public Book get(long bookId) throws BookNotFoundException {
25 if (!collection.containsKey(bookId)) {
26 throw new BookNotFoundException("Not found book with id: " + bookId);
27 }
28
29 // return copy of the book
30 return new Book(collection.get(bookId));
31 }
32}
Here’s the concurrency control in action:
1var bookId = 1;
2// Alice and Bob took the book concurrently
3final var aliceBook = bookRepository.get(bookId);
4final var bobBook = bookRepository.get(bookId);
5
6aliceBook.setTitle("Kama Sutra"); // Alice has updated book title
7bookRepository.update(aliceBook); // and successfully saved book in database
8LOGGER.info("Alice updates the book with new version {}", aliceBook.getVersion());
9
10// now Bob has the stale version of the book with empty title and version = 0
11// while actual book in database has filled title and version = 1
12bobBook.setAuthor("Vatsyayana Mallanaga"); // Bob updates the author
13try {
14 LOGGER.info("Bob tries to update the book with his version {}", bobBook.getVersion());
15 bookRepository.update(bobBook); // Bob tries to save his book to database
16} catch (VersionMismatchException e) {
17 // Bob update fails, and book in repository remained untouchable
18 LOGGER.info("Exception: {}", e.getMessage());
19 // Now Bob should reread actual book from repository, do his changes again and save again
20}
Program output:
1Alice updates the book with new version 1
2Bob tries to update the book with his version 0
3Exception: Tried to update stale version 0 while actual version is 1
Use Version Number for:
Version Number pattern allows to implement a concurrency control, which is usually done via Optimistic Offline Lock pattern.