I want to test if data anomalies such as dirty read, non-repeatable read, phantom read, lost update and so on occur in transaction.
Actually, I used person table which has id and name as shown below.
person table:
| id | name |
|---|---|
| 1 | John |
| 2 | David |
Then, I tested non-repeatable read with test view below and one command prompt. *During sleep(10), one command prompt updates "David" to "Tom" and commits:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
from django.db import transaction
from time import sleep
@transaction.atomic
def test(request):
print(Person.objects.get(pk=2)) # "David"
sleep(10) # Update "David" to "Tom" and commit by one command prompt.
print(Person.objects.get(pk=2)) # "Tom"
return HttpResponse("Test")
But, every time I test data anomalies, I manually need to run test view and update and commit with one command prompt which takes much time.
So, how can I more efficiently test if data anomalies occur in transaction?
With threads, you can more efficiently test if data anomalies occur in transaction.
I created 5 sets of code with threads to test 5 common data anomalies dirty read, non-repeatable read, phantom read, lost update and write skew with the Django's default isolation level READ COMMITTED and PostgreSQL as shown below. *Lost update or write skew occurs by race condition and I used PostgreSQL.
I explain about:
<Dirty read>, <Non-repeatable read> and <Phantom read>
First, I created
store_persontable withidandnamewithmodels.pyas shown below:store_persontable:Then, I created and ran the test code of dirty read as shown below:
Then, dirty read didn't occur according to the result belew on console because in any isolation levels in PostgreSQL, dirty read doesn't happen:
And also, I could get the PostgreSQL query logs below. You can check how to log PostgreSQL queries:
And, this table below shows the flow and SQL query logs of PostgreSQL above:
BEGIN;SELECT "store_person"."id", "store_person"."name" FROM "store_person" WHERE "store_person"."id" = 2 LIMIT 21;2 David
David.BEGIN;UPDATE "store_person" SET "name" = 'Tom' WHERE "store_person"."id" = 2;DavidtoTom.SELECT "store_person"."id", "store_person"."name" FROM "store_person" WHERE "store_person"."id" = 2 LIMIT 21;2 David
Davidinstead ofTombefore T2 commits.*Dirty read doesn't occur!!
COMMIT;COMMIT;Next, I created and ran the test code of non-repeatable read as shown below:
Then, non-repeatable read occurred according to the result belew on console because in READ COMMITTED isolation level in PostgreSQL, non-repeatable read occurs:
And also, I could get the PostgreSQL query logs below:
And, this table below shows the flow and SQL query logs of PostgreSQL above:
BEGIN;SELECT "store_person"."id", "store_person"."name" FROM "store_person" WHERE "store_person"."id" = 2 LIMIT 21;2 David
David.BEGIN;UPDATE "store_person" SET "name" = 'Tom' WHERE "store_person"."id" = 2;DavidtoTom.COMMIT;SELECT "store_person"."id", "store_person"."name" FROM "store_person" WHERE "store_person"."id" = 2 LIMIT 21;2 Tom
Tominstead ofDavidafter T2 commits.*Non-repeatable read occurs!!
COMMIT;Next, I created and ran the test code of phantom read as shown below:
Then, phantom read occurred according to the result belew on console because in READ COMMITTED isolation level in PostgreSQL, phantom read occurs:
And also, I could get the PostgreSQL query logs below:
And, this table below shows the flow and SQL query logs of PostgreSQL above:
BEGIN;SELECT "store_person"."id", "store_person"."name" FROM "store_person";1 John
2 David
BEGIN;INSERT INTO "store_person" ("id", "name") VALUES (3, 'Tom') RETURNING "store_person"."id";3andTomtopersontable.COMMIT;SELECT "store_person"."id", "store_person"."name" FROM "store_person";1 John
2 David
3 Tom
*Phantom read occurs!!
COMMIT;<Lost update>
First, I created
store_producttable withid,nameandstockwithmodels.pyas shown below:store_producttable:Then, lost update occurred according to the result belew on console because in READ COMMITTED isolation level in PostgreSQL, lost update occurs:
And also, I could get the PostgreSQL query logs below:
And, this table below shows the flow and SQL query logs of PostgreSQL above:
BEGIN;SELECT "store_product"."id", "store_product"."name", "store_product"."stock" FROM "store_product" WHERE "store_product"."id" = 2 LIMIT 21;2 Orange 20
20which is updated later to13because a customer buys 7 oranges.BEGIN;SELECT "store_product"."id", "store_product"."name", "store_product"."stock" FROM "store_product" WHERE "store_product"."id" = 2 LIMIT 21;2 Orange 20
20which is updated later to16because a customer buys 4 oranges.UPDATE "store_product" SET "stock" = 13 WHERE "store_product"."id" = 2;20to13.COMMIT;UPDATE "store_product" SET "stock" = 16 WHERE "store_product"."id" = 2;13to16after T1 commits.COMMIT;*Lost update occurs.
<Write skew>
First, I created
store_doctortable withid,nameandon_callwithmodels.pyas shown below:store_doctortable:Then, write skew occurred according to the result belew on console because in READ COMMITTED isolation level in PostgreSQL, write skew occurs:
And also, I could get the PostgreSQL query logs below:
And, this table below shows the flow and SQL query logs of PostgreSQL above:
BEGIN;SELECT COUNT(*) AS "__count" FROM "store_doctor" WHERE "store_doctor"."on_call";2
2so John can take a rest.BEGIN;SELECT COUNT(*) AS "__count" FROM "store_doctor" WHERE "store_doctor"."on_call";2
2so Lisa can take a rest.UPDATE "store_doctor" SET "on_call" = false WHERE "store_doctor"."id" = 1;TruetoFalsewhich means John takes a rest.COMMIT;UPDATE "store_doctor" SET "on_call" = false WHERE "store_doctor"."id" = 2;TruetoFalsewhich means Lisa takes a rest.COMMIT;John and Lisa both take a rest.
*Write skew occurs.