The Downside Assertion
When a number of purchasers, processes, or threads compete for a restricted variety of assets concurrently, inflicting degraded turnaround time and efficiency, the system enters a state referred to as competition. That is the most typical drawback in methods that deal with excessive visitors volumes. With out sleek dealing, competition results in race situations and an inconsistent state.
Instance State of affairs
Contemplate shopping for flight tickets on-line. There is just one seat accessible on the flight. Alice and Bob each need this seat and click on “Ebook Now” at precisely the identical time.
With none coordination, the next occasions happen.
- Alice’s request reads: 1 seat accessible.
- Bob’s request reads: 1 seat accessible (Not one of the writes have occurred but)
- Alice’s request checks whether or not 1 ≥ 1 (sure, there’s a seat accessible) and continues to cost.
- Bob’s request checks whether or not 1 ≥ 1 (sure, there’s a seat accessible) and continues to cost.
- Alice will get charged $1000, and the seat rely is decremented to 0.
- Bob will get charged $1000, seat rely decremented to -1.
This race situation happens as a result of studying and writing aren’t atomic. There is a hole between studying the present state and updating based mostly on it; in that tiny window, many issues can go mistaken.
The issue can solely worsen at scale. With tens of hundreds of concurrent customers attempting to pay money for the identical useful resource, even small race situation home windows may cause enormous conflicts.
The answer to competition issues varies relying on the character of the database, whether or not it’s a single-node occasion or unfold throughout a number of nodes. On this article, we are going to have a look at the options assuming our database is hosted on a single node.
Options
1. AtomicityÂ
Atomicity ensures that partial failures should not doable; both all operations in a bunch succeed or all fail. It solves many competition issues.
ACID-compliant databases guarantee atomicity through the use of transactions. A transaction defines a set of operations as a single unit of labor.
For our instance, atomicity ensures that flight seat reserving and ticket buy each occur efficiently.
BEGIN TRANSACTION;
-- Verify and reserve the seat
UPDATE flights
SET available_seats = available_seats - 1
WHERE flight_id = 123;
-- Create the ticket document
INSERT INTO tickets (user_id, flight_id, seat_number, purchase_time)
VALUES ('user123', '123', 'F25', NOW());
COMMIT;
If any of those operations fail, the transaction rolls again. You do not find yourself with a seat reserved however no ticket issued. However atomicity alone does not clear up the issue we noticed. Two folks can nonetheless e-book the identical seat on the identical flight.Â
This is why: Alice and Bob can each begin their transactions concurrently, test that available_seats >= 1, and execute their UPDATE statements. Since every transaction is atomic, each succeed, however now we have bought two tickets for one seat.
The problem is that transactions present atomicity inside themselves, however do not stop concurrent reads of the identical knowledge. We’d like coordination mechanisms to resolve this.
Varied concurrency management methods can be utilized, similar to pessimistic concurrency management and optimistic concurrency management. These methods permit the database to handle concurrent entry to shared knowledge in a managed and constant method, serving to stop race situations. Discover that, mainly, we have now solely two steps when reserving a room: learn the info and replace it. So we are able to clear up race situations both on a learn step or on an replace step.
2. Fixing Race Situation Throughout Learn Step: Pessimistic Concurrency Management
Pessimistic concurrency management is a method used to stop race situations in a database by locking knowledge being accessed or up to date. This ensures that just one consumer can entry the info at a time, and that different customers should wait till the lock is launched earlier than they will entry it.
In SQL, pessimistic concurrency management might be applied utilizing the “SELECT … FOR UPDATE” assertion. This assertion permits a consumer to lock the rows of a desk which might be being accessed and prevents different customers from updating or locking these rows till the lock is launched.
BEGIN TRANSACTION;
-- Lock the row first to stop race situations
SELECT available_seats FROM flights
WHERE flight_id = 123
FOR UPDATE;
-- Now safely replace the seat rely
UPDATE flights
SET available_seats = available_seats - 1
WHERE flight_id = 123;
-- Create the ticket document
INSERT INTO tickets (user_id, flight_id, seat_number, purchase_time)
VALUES ('user123', 123, 'F25', NOW());
COMMIT;
The FOR UPDATE clause acquires an unique lock on the flight row earlier than studying. When Alice runs this code, Bob’s similar transaction will block on the SELECT assertion till Alice’s transaction completes. This prevents each from seeing the identical preliminary seat rely and ensures just one individual can test and replace at a time.
Efficiency concerns are actually essential when utilizing locks. You wish to lock as few rows as doable for as quick a time as doable. Locking total tables kills concurrency. Maintain locks for seconds as a substitute of milliseconds, and also you create bottlenecks. In our instance, we’re solely locking one particular flight row for a quick interval throughout the reserving.
3. Isolation Ranges
As a substitute of explicitly locking rows with FOR UPDATE, you’ll be able to let the database robotically deal with conflicts by elevating what’s referred to as the isolation degree. Isolation ranges management how a lot concurrent transactions can see of one another’s adjustments. Consider it as how “remoted” every transaction is from the work of different transactions.
Most databases assist 4 commonplace isolation ranges (these are completely different choices, not a development):
- READ UNCOMMITTED – Can see uncommitted adjustments from different transactionsÂ
- READ COMMITTED – Can solely see dedicated adjustmentsÂ
- REPEATABLE READ – Similar knowledge learn a number of instances inside a transaction stays constant
- SERIALIZABLE – Strongest isolation, transactions seem to run one after one other
The defaults of both READ COMMITTED or REPEATABLE READ nonetheless permit our flight ticket race situation as a result of each Alice and Bob can learn “1 seat accessible” concurrently earlier than updating. The SERIALIZABLE isolation degree solves this by making transactions seem to run one by one:
-- Set isolation degree for this transaction
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE flights
SET available_seats = available_seats - 1
WHERE flight_id = 123;
-- Create the ticket document
INSERT INTO tickets (user_id, flight_id, seat_number, purchase_time)
VALUES ('user123', 123, 'F25', NOW());
COMMIT;
The tradeoff is that SERIALIZABLE isolation is rather more costly than specific locks. It requires the database to trace all reads and writes to detect potential conflicts, and transaction aborts waste work that should be redone.
4. Fixing Race Situation Throughout Write Step: Optimistic Concurrency Management
Optimistic concurrency management, then again, checks for conflicts earlier than committing the adjustments. If a battle is detected, the consumer is notified, and the adjustments should not utilized.
One technique to implement optimistic concurrency management in a flight reserving system is to make use of a “model” column within the “flights” desk. This column can retailer a “model quantity” for every flight, incremented every time it’s up to date.
BEGIN TRANSACTION;
UPDATE flights
SET available_seats = available_seats - 1, model = model + 1
WHERE flight_id = 123 and model = 1;;
-- Create the ticket document
INSERT INTO tickets (user_id, flight_id, seat_number, purchase_time)
VALUES ('user123', 123, 'F25', NOW());
COMMIT;
If these statements are executed concurrently for each Bob and Alice, the primary UPDATE assertion to be executed will increment the “model” of the flight with ID 123 to 2, and the second UPDATE assertion will fail, because the “model” within the WHERE clause is 1 (so zero rows might be up to date with the second transaction).
Conclusion
Whereas optimistic concurrency management is a cheaper alternative, this method is sensible when conflicts are uncommon. In most situations, the prospect of two folks shopping for the very same merchandise at the very same second is low. The occasional retry is price avoiding the overhead of pessimistic locking.







