1

The Need for Optimistic Concurrency in SPAs

 3 years ago
source link: https://blog.bitsrc.io/the-need-for-optimistic-concurrency-in-spas-6b141361c32c
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

The Need for Optimistic Concurrency in SPAs

A guide for using Optimistic Concurrency to enhance your SPAs

Optimistic Concurrency in

What happens if two users edit the same data in your SPA at the same time, with one saving the changes first and the other later? Whose data will be kept, and whose will be discarded? To answer that question, you would need to know a concept known as optimistic concurrency.

This article will discuss why we need optimistic concurrency, how it differs from pessimistic concurrency, and how to use optimistic concurrency with different databases.

What exactly is Optimistic Concurrency?

Let’s look at a simple example to see what optimistic concurrency is.

When using Git, merge conflicts are nothing new to us as developers. They occur when multiple users update files and submit them to the version control system simultaneously. As a result, merge conflicts are promoted to the user who has submitted the most recent updates to avoid losing essential changes.

Optimistic concurrency believes that these types of conflicts will occur infrequently (hence the term “optimistic”). However, when they do, the humans involved should be allowed to choose the action to complete multiple transactions without interfering with each other.

Optimistic VS. Pessimistic Concurrency

Another solution for preventing conflicts when concurrent updates are performed on the same record is pessimistic concurrency. However, pessimistic concurrency will not work on SPAs for most cases, and here’s why.

In the pessimistic concurrency model, a user who updates a row creates a lock. No one else can change that row until the user completes the update and releases the lock. Locking records requires the use of additional server resources and a persistent connection to the database server. As a result, pessimistic concurrency is not ideal for an application where users interact with data frequently.

Note: I’m not saying we can completely avoid pessimistic concurrency. It is needed for certain operations. However, with SPA, since the disconnected state from the server-side is maintained in the browser, more cases are there, which requires optimistic concurrently handling.

Because web applications do not maintain a constant connection with the server and the database, it is impossible to tell whether the user is still editing the record and the lock should be retained or not.

Pessimistic concurrency works best when lock times are short, such as in programmatic record processing.

On the other hand, in the optimistic concurrency model, users do not lock a row when reading it, at the database level. When a user wants to update a row, the application checks to see if it has changed since the last time the server read it from the database. Therefore, optimistic concurrency is ideal for SPAs.

Optimistic concurrency allows you to serve more clients and scales better because of the non-locking behavior at the database level.

How to implement Optimistic Concurrency

When storing records on a database, optimistic concurrency is implemented primarily by using version numbers or timestamps for a particular record. Meaning that whenever a record is updated, logic can be executed that compares the version number of the record being saved to the version stored in the database.

Let’s look at how different techniques to implement optimistic concurrency with two well-known NoSQL databases.

Note: You can also implement it with relational databases by adding a where clause to the update query to compare against the timestamp or version column.

MongoDB

When it comes to MongoDB commonly used pattern is to use a timestamp for optimistic concurrency. However in this example, I’m using a version class, as shown below.

public class Version
{
public Guid Id { get; set; }
public int Version { get; set; }
public int Value { get; set; }
}

When sending updates, you can increment the version number and include an additional clause against the previously read version to compare the version numbers.

r => r.Id == id && r.Version == version

AWS DynamoDB

By default, AWS DynamoDB write operations such as PutItem, UpdateItem, and DeleteItem are not optimistically concurrent.

You have to use the Conditional Writes feature in DynamoDB to implement optimistic concurrency. With conditional writes, you can declare a condition, that an item should only be updated if certain values match what you expect. You could either use a timestamp (e.g.: last updated at) or a version number or another property value as the condition.

The example below shows how to use the conditional updating mechanism to update the price of an item.

aws dynamodb update-item \
--table-name ProductCatalog \
--key '{"Id":{"N":"1"}}' \
--update-expression "SET Price = :newval" \
--condition-expression "Price = :currval" \
--expression-attribute-values file://expression-attribute-values.json

The arguments stored in the json file would look like below.

{
":newval":{"N":"15"},
":currval":{"N":"10"}
}

In this case, the price will be updated to 15 only if the current price is equal to 10.

When sending an identifier with the updated query data, we can either send it via the HTTP URL parameters, request body, or ETags.

Since URL parameters and HTTP request body are pretty straightforward, let us look at how to use ETags to transmit the version identifier to detect collisions.

Using ETag

When using ETags, HTTP requests can include ETag response headers, which can be used similarly to version numbers to determine whether or not a resource has changed since it was last loaded.

You can detect mid-air edit collisions using the ETag and If-Match headers. For example, when editing a blog, the current blog content may be hashed and placed in an ETag header in the response.

ETag: "hash-value"

When saving changes to the blog (posting data), the POST request will include the If-Match header, which contains the ETag values to compare freshness against.

If-Match: "hash-value"

If the hashes do not match, it indicates that a user has edited the document in the interim, and a 412 Precondition Failed error is returned.

Develop & share JS components with Bit

Bit is an ultra-extensible tool that lets you create truly modular applicationswith independently authored, source-controlled, and maintained components.

Use it to build modular apps & design systems, author and deliver micro frontends, or simply share components between applications.

An independently source-controlled and shared “card” component (on the right, its dependency graph, auto-generated by Bit)

Conclusion

Because of the nature of SPAs having a disconnected state from the server, there is a greater chance that changes made by one user to a record will conflict with another.

In this article, I discussed how you could use optimistic concurrency to handle these conflicts.

And if you find different techniques or libraries to facilitate it with SPA, mention them in the comments below.

Thank you for reading, and happy coding!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK