Ачааллж байна...
Өмнөх хичээлд банкны шилжүүлгийн хоёр алхмын хооронд алдаа гарч, 30,000 төгрөг алга болсон. Шалтгаан нь psql-ийн autocommit: тушаал бүр өөрийн жижиг transaction-д тэр даруй хадгалагдаж, хоёр алхам хоёр тусдаа гэрээ болсон. Бид transaction-ий дөрвөн баталгааг сурч, тэдгээр нь энэ сургалтын турш хаана нуугдаж байсныг харсан. Гэвч нэг асуулт хариултгүй үлдсэн: хэд хэдэн тушаалыг нэг гэрээ болгон өөрийн гараар хэрхэн багцлах вэ?
Энэ хичээлд гурван тушаал сурна: BEGIN — transaction эхлүүлэх, COMMIT — хадгалах (гэрээнд гарын үсэг зурах), ROLLBACK — цуцлах (гэрээг урах). Бид өмнөх хичээлийн шилжүүлгийг ижил алдаатайгаар давтаж, энэ удаад мөнгө нэг ч төгрөг алга болохгүйг өмнө ба дараах хүснэгтээр нотолно. Мөн psql-ийн урилга transaction-ий төлвийг хэрхэн харуулдгийг, алдааны дараа transaction ямар онцгой төлөвт ордгийг харна.
Хичээлийн хоёр дахь хагаст Python руу шилжинэ. Өмнөх бүлгийн engine.begin() бол Python дахь BEGIN ба COMMIT юм. Бид шилжүүлгийн функц бичиж, нэг чухал, шударга сургамжтай нүүр тулна: transaction нь зөвхөн алдаа гарсан үед л хамгаалдаг — алдаа гараагүй ч буруу болсон нөхцлийг та өөрөө илрүүлж, алдаа болгох ёстой.
Эхлэхээсээ өмнө өгөгдлөө эхлэл байдалд буцаана: PowerShell-д psql -U postgres -d bank -f bank_reset.sql.
Нотариатчийн ширээн дээр гэрээ хэрхэн хийгддэгийг дахин харъя. Нотариатч шинэ хавтас нээнэ — гэрээ эхэллээ (BEGIN). Тэр хавтсанд бичиг баримтуудыг нэг нэгээр нь бэлдэнэ: эхний бичиг, хоёр дахь бичиг. Хавтас нээлттэй байх үед бичгүүд хараахан хүчин төгөлдөр биш — нотариатч өөрөө тэдгээрийг харж, засаж болно, харин гадны хүмүүс тэдний тухай мэдэхгүй. Бүх бичиг бэлэн болоход нотариатч хавтсанд гарын үсэг зурж тамгална (COMMIT) — тэр мөчөөс бүх бичиг нэгэн зэрэг хүчинтэй болно.
Харин бэлтгэлийн явцад асуудал гарвал — хоёр дахь бичгийн мэдээлэл буруу, эсвэл тал нь бодлоо өөрчилсөн — нотариатч хавтсыг бүхэлд нь урж хаяна (ROLLBACK). Эхний бичиг хэдийгээр төгс бэлэн байсан ч хамт урагдана, учир нь хагас гэрээ гэж байдаггүй. Өмнөх хичээлийн асуудал нь нотариатч бичиг бүрийг бэлдмэгц тус тусад нь тамгалж байсанд оршсон.
psql-ээр банкны өгөгдлийн санд холбогдоно (psql -U postgres -d bank). Шилжүүлгийн өмнөх байдал:
id | owner | balance
----+-------+---------
1 | Бат | 50000
2 | Сараа | 20000
нийт: 70000Одоо шилжүүлгийг нэг transaction болгон хийе. Тушаал бүрийг нэг нэгээр нь ажиллуулж, psql-ийн урилгыг ажиглаарай:
bank=# BEGIN;
BEGIN
bank=*# UPDATE accounts SET balance = balance - 30000 WHERE id = 1;
UPDATE 1
bank=*# UPDATE accounts SET balance = balance + 30000 WHERE id = 2;
UPDATE 1
bank=*# COMMIT;
COMMIT
bank=#Эдгээр тушаал юу гэж хэлж байна вэ гэвэл: “transaction эхлүүл; 1-р данснаас 30,000 хас; 2-р дансанд 30,000 нэм; бүх өөрчлөлтийг нэгэн зэрэг хадгал.” BEGIN-ийн дараа урилга bank=#-ээс bank=*# болж өөрчлөгдсөнийг анзаараарай. Од тэмдэг нь psql-ийн "та transaction дотор байна, юу ч хараахан хадгалагдаагүй" гэсэн дохио — зүйрлэлийн нээлттэй хавтас. COMMIT-ийн дараа од алга болж, урилга хэвийн байдалдаа буцсан: хавтас тамгалагдаж хаагдлаа.
Шилжүүлгийн дараах байдал:
sql
SELECT id, owner, balance FROM accounts ORDER BY id;
SELECT SUM(balance) AS total FROM accounts; id | owner | balance
----+-------+---------
1 | Бат | 20000
2 | Сараа | 50000
нийт: 70000Бат 30,000 алдаж, Сараа 30,000 олсон, нийт 70,000 хэвээр. Үр дүн нь өмнөх хичээлийн autocommit горимын амжилттай хувилбартай ижил харагдаж байгаа нь зөв — transaction нь амжилттай тохиолдолд юуг ч өөрчилдөггүй. Түүний үнэ цэнэ алдаа гарах үед л илэрдэг.
Өгөгдлөө эхлэл байдалд буцаана: \q, дараа нь psql -U postgres -d bank -f bank_reset.sql, дахин psql -U postgres -d bank.
Жишээ 1: дундаа бүтэлгүйтсэн шилжүүлэг
Одоо өмнөх хичээлийн гамшгийг transaction дотор давтъя. Өмнөх байдал:
id | owner | balance
----+-------+---------
1 | Бат | 50000
2 | Сараа | 20000
нийт: 70000Тушаалууд:
bank=# BEGIN;
BEGIN
bank=*# UPDATE accounts SET balance = balance - 30000 WHERE id = 1;
UPDATE 1
bank=*# UPDATE acounts SET balance = balance + 30000 WHERE id = 2;
ERROR: relation "acounts" does not exist
LINE 1: UPDATE acounts SET balance = balance + 30000 WHERE id = 2;
^
bank=!#Хоёр дахь UPDATE өмнөх хичээлийнхтэй яг ижил алдаа өгсөн. Харин урилгыг хараарай: bank=!#. Анхаарлын тэмдэг нь “transaction алдаатай төлөвт орсон” гэсэн дохио. PostgreSQL-д transaction дотор нэг тушаал алдаа өгвөл тэр transaction бүхэлдээ "эвдэрсэн" гэж тэмдэглэгдэнэ. Энэ үед юу болохыг харахын тулд өгөгдлийг шалгахыг оролдъё:
bank=!# SELECT * FROM accounts;
ERROR: current transaction is aborted, commands ignored until end of transaction blockЭнэ мессеж юу гэж хэлж байна вэ гэвэл: "одоогийн transaction цуцлагдсан; transaction блок дуусах хүртэл тушаалуудыг үл тоомсорлоно." PostgreSQL алдаатай transaction-д цааш ажиллахыг хориглодог — хагас эвдэрсэн гэрээн дээр шинэ бичиг нэмэх утгагүй. Цорын ганц гарц нь transaction-ийг дуусгах. Цуцалъя:
bank=!# ROLLBACK;
ROLLBACK
bank=#Энэ тушаал юу гэж хэлж байна вэ гэвэл: "одоогийн transaction-ийн бүх өөрчлөлтийг цуцал." Урилга хэвийн болсон. Одоо дараах байдал:
sql
SELECT id, owner, balance FROM accounts ORDER BY id;
SELECT SUM(balance) AS total FROM accounts; id | owner | balance
----+-------+---------
1 | Бат | 50000
2 | Сараа | 20000
нийт: 70000Өмнөх ба дараах хүснэгт яг ижил. Батын данснаас 30,000 хасагдсан эхний UPDATE хоёр дахь алхмын алдаатай хамт цуцлагдав. Мөнгө нэг ч төгрөг алга болоогүй. Өмнөх хичээлтэй харьцуулбал: ижил хоёр тушаал, ижил алдаа — гэвч BEGIN ба ROLLBACK бүхэл байдлын баталгааг хэрэгжүүлж, "хагас гэрээ"-г боломжгүй болгосон.
Нэг сонирхолтой нарийн зүйл: хэрэв алдаатай transaction-д ROLLBACK-ийн оронд COMMIT гэж бичвэл psql хариуд нь ROLLBACK гэж хэвлэнэ. PostgreSQL алдаатай transaction-ийг хадгалахаас татгалзаж, автоматаар цуцалдаг — та хүсэлтээ "хадгал" гэж өгсөн ч. Энэ нь тууштай байдлын баталгаа: эвдэрсэн гэрээнд гарын үсэг зурах боломжгүй.
Жишээ 2: санаатай ROLLBACK — psql дахь “Revert Changes”
ROLLBACK зөвхөн алдааны үед биш, бодлоо өөрчилсөн үед ч хэрэгтэй. Бүлэг 3-т DB Browser-ийн Revert Changes бидэнд WHERE-гүй UPDATE-ийн гамшгийг аюулгүй туршиж, дараа нь буцаах боломж олгосон. psql-д ийм товч байхгүй гэж Бүлэг 10-т хэлсэн — гэвч BEGIN ба ROLLBACK яг тэр товч юм. Хоёр дансны үлдэгдлийг 10%-иар өсгөх гэсэн ч санамсаргүй WHERE-гүй бичье:
bank=# BEGIN;
BEGIN
bank=*# UPDATE accounts SET balance = 0;
UPDATE 2
bank=*# SELECT id, owner, balance FROM accounts ORDER BY id;
id | owner | balance
----+-------+---------
1 | Бат | 0
2 | Сараа | 0
(2 rows)Энэ UPDATE юу гэж хэлж байна вэ гэвэл: "бүх дансны үлдэгдлийг 0 болго" — Бүлэг 3-ын "алдаагүй гамшиг." UPDATE 2 гэсэн хариу болон SELECT-ийн үр дүн гамшгийг илчилсэн. Transaction дотор байгаа тул бид өөрсдийн өөрчлөлтийг харж байгаа ч энэ нь хараахан хадгалагдаагүй — зүйрлэлийн нээлттэй хавтас дахь нотариатч. Цуцалъя:
bank=*# ROLLBACK;
ROLLBACK
bank=# SELECT id, owner, balance FROM accounts ORDER BY id;
id | owner | balance
----+-------+---------
1 | Бат | 50000
2 | Сараа | 20000
(2 rows)Бүх зүйл хэвээр. Энэ нь Бүлэг 3-ын "эхлээд SELECT, дараа нь UPDATE" дадлыг илүү хүчтэй болгодог. Шинэ дадал: эрсдэлтэй өөрчлөлтийг psql-д хийхдээ BEGIN-ээр эхэл, өөрчлөлтөө хий, “UPDATE n”-ийн тоо ба SELECT-ээр шалга, зөв бол COMMIT, буруу бол ROLLBACK. Энэ бол мэргэжлийн өгөгдлийн сангийн администраторууд жинхэнэ server дээр өдөр бүр хэрэглэдэг "аюулгүйн бүс" юм.
Жишээ 3: Python — engine.begin() бол BEGIN ба COMMIT
Одоо Python руу шилжье. Өмнөх бүлгийн pg-practice хавтсанд, виртуал орчинтой PowerShell-д банкны өгөгдлийн сангийн URL-ийг тусдаа хувьсагчид тогтооно (өөрийн нууц үгээр):
powershell
$env:BANK_URL = "postgresql+psycopg://postgres:my_password@localhost:5432/bank"transfer.py:
python
import os
from sqlalchemy import create_engine, text
engine = create_engine(os.environ["BANK_URL"])
def show():
with engine.connect() as connection:
for row in connection.execute(text("SELECT id, owner, balance FROM accounts ORDER BY id")):
print(row.id, row.owner, row.balance)
total = connection.execute(text("SELECT SUM(balance) FROM accounts")).scalar()
print("нийт:", total)
def transfer(from_id, to_id, amount):
with engine.begin() as connection:
connection.execute(
text("UPDATE accounts SET balance = balance - :amount WHERE id = :id"),
{"amount": amount, "id": from_id},
)
connection.execute(
text("UPDATE accounts SET balance = balance + :amount WHERE id = :id"),
{"amount": amount, "id": to_id},
)
show()
transfer(1, 2, 30000)
print("--- шилжүүлгийн дараа ---")
show()transfer функц нь Анхны жишээний psql тушаалуудын Python хувилбар. with engine.begin() блок эхлэхэд SQLAlchemy BEGIN илгээж, блок алдаагүй дуусахад COMMIT илгээнэ — өмнөх бүлгийн Жишээ 4-т харсан зан. Хоёр UPDATE өмнөх бүлгийн параметртэй хэлбэрээр бичигдсэн. Ажиллуулна (bank_reset.sql ажиллуулсны дараа):
1 Бат 50000
2 Сараа 20000
нийт: 70000
--- шилжүүлгийн дараа ---
1 Бат 20000
2 Сараа 50000
нийт: 70000Хүлээгдсэн үр дүн. Одоо Python дахь бүтэлгүйтлийг харъя — гэхдээ энэ удаад psql-ийн алдааны оронд өөр төрлийн асуудал.
Жишээ 4: алдаагүй бүтэлгүйтэл — transaction-ий хязгаар
bank_reset.sql-ийг дахин ажиллуулаад, transfer.py-ийн доод хэсгийг солино:
python
show()
transfer(1, 99, 30000)
print("--- шилжүүлгийн дараа ---")
show()Бат 99-р данс руу мөнгө шилжүүлж байна — ийм данс байхгүй. Ажиллуулна:
1 Бат 50000
2 Сараа 20000
нийт: 70000
--- шилжүүлгийн дараа ---
1 Бат 20000
2 Сараа 20000
нийт: 40000Алдааны мессеж гараагүй. Transaction ажилласан, COMMIT хийгдсэн. Гэвч 30,000 төгрөг дахин алга болжээ — transaction дотор! Юу болов? Хоёр дахь UPDATE WHERE id = 99 гэж хайгаад нэг ч мөр олсонгүй. Бүлэг 3-т сурсанчлан WHERE-д таарах мөр олдохгүй бол UPDATE “0 мөр өөрчилсөн” гэж амжилттай дуусдаг — энэ бол алдаа биш. Алдаа гараагүй тул engine.begin() бүх зүйл хэвийн гэж үзээд COMMIT хийсэн.
Энэ бол энэ хичээлийн хамгийн чухал, шударга сургамж: transaction нь зөвхөн алдаа гарсан үед л ROLLBACK хийдэг. Тэр таны бизнесийн утгыг ойлгодоггүй — "мөнгө хүлээн авагчгүй шилжүүлэг утгагүй" гэдгийг мэдэхгүй. Хэрэв ямар нэг нөхцөл таны логикийн хувьд бүтэлгүйтэл бол та өөрөө түүнийг илрүүлж, exception болгон шидэх ёстой. Тэгвэл begin() блок exception-ийг хараад ROLLBACK хийнэ.
UPDATE хэдэн мөр өөрчилснийг SQLAlchemy-ийн үр дүнгийн rowcount хэлдэг — psql-ийн UPDATE 1-ийн Python хувилбар. transfer функцийг засъя:
python
def transfer(from_id, to_id, amount):
with engine.begin() as connection:
debit = connection.execute(
text("UPDATE accounts SET balance = balance - :amount WHERE id = :id"),
{"amount": amount, "id": from_id},
)
if debit.rowcount != 1:
raise ValueError(f"Илгээгч данс олдсонгүй: {from_id}")
credit = connection.execute(
text("UPDATE accounts SET balance = balance + :amount WHERE id = :id"),
{"amount": amount, "id": to_id},
)
if credit.rowcount != 1:
raise ValueError(f"Хүлээн авагч данс олдсонгүй: {to_id}")Нэмэлт хоёр шалгалт: UPDATE бүрийн дараа яг нэг мөр өөрчлөгдсөн эсэхийг шалгаж, үгүй бол ValueError шиднэ. bank_reset.sql-ийг ажиллуулаад дахин ажиллуулна:
1 Бат 50000
2 Сараа 20000
нийт: 70000
Traceback (most recent call last):
...
ValueError: Хүлээн авагч данс олдсонгүй: 99Шилжүүлэг бүтэлгүйтэж, програм алдаа өгсөн. Traceback-ийн улмаас "шилжүүлгийн дараа" хэсэг ажиллаагүй тул psql-ээр шалгая: SELECT id, owner, balance FROM accounts ORDER BY id; — Бат 50,000, Сараа 20,000. ValueError with engine.begin() блокоос гарахад SQLAlchemy ROLLBACK илгээж, Батын данснаас хасалтыг цуцалсан. Энэ нь өмнөх бүлгийн “with блок exception-ээр тасарсан ч __exit__ ажилладаг” гэсэн context manager-ийн зан — энд холболтыг буцаахаас гадна transaction-ийг цуцлах үүргийг гүйцэтгэсэн. Бодит програмд энэ ValueError-ийг дээд түвшинд барьж, хэрэглэгчид "хүлээн авагчийн данс буруу байна" гэх мэт ойлгомжтой мессеж харуулна.
Жишээ 5: үлдэгдэл хүрэлцэхгүй — CHECK-ийн тусламж
Нөгөө төрлийн бүтэлгүйтлийг constraint өөрөө илрүүлдэг. bank_reset.sql-ийн дараа transfer(2, 1, 50000) дуудъя — Сараад 20,000 л байгаа:
sqlalchemy.exc.IntegrityError: (psycopg.errors.CheckViolation) new row for relation "accounts" violates check constraint "accounts_balance_check"
DETAIL: Failing row contains (2, Сараа, -30000).Энэ мессеж юу гэж хэлж байна вэ гэвэл (Бүлэг 11-ийн гурван давхаргаар): SQLAlchemy-ийн түвшинд бүрэн бүтэн байдлын алдаа; psycopg-ийн түвшинд шалгах дүрмийн зөрчил; server-ийн үг — “accounts_balance_check дүрэм зөрчигдлөө, бүтэлгүйтсэн мөр нь Сараа -30,000.” Бүлэг 4-т тавьсан CHECK (balance >= 0) энд бизнесийн дүрмийг хамгаалав — бид Python-д үлдэгдэл шалгах код бичээгүй ч өгөгдлийн сан хориглосон. Энэ нь Бүлэг 4-ийн "дүрмийг өгөгдөлд аль болох ойр тавь" гэсэн зарчмын шагнал: Python, psql, ирээдүйн өөр програм — аль ч замаар хийсэн шилжүүлэг энэ дүрмийг зөрчиж чадахгүй. Алдаа нь эхний UPDATE дээр гарсан тул хоёр дахь нь огт ажиллаагүй, begin() ROLLBACK хийсэн — өгөгдөл хөндөгдөөгүй.
Хоёр жишээг нэгтгэвэл: зарим бүтэлгүйтлийг өгөгдлийн сан өөрөө илрүүлдэг (constraint зөрчил, байхгүй хүснэгт), заримыг нь зөвхөн таны код л илрүүлж чадна (0 мөр өөрчилсөн, бизнесийн утгагүй нөхцөл). Transaction хоёуланд нь ROLLBACK хийх чадвартай — зөвхөн exception хэрэгтэй.
FastAPI сургалтын SQLModel Session мөн ижил зарчмаар ажилладаг. session.add(), объектын талбарыг өөрчлөх нь нээлттэй хавтсанд бичиг нэмэхтэй адил; session.commit() нь тамгалах. Хэрэв with Session(engine) as session: блок commit()-гүйгээр дуусвал эсвэл exception-ээр тасарвал Session ROLLBACK хийнэ — өмнөх бүлгийн Түгээмэл алдаанууд хэсгийн “commit хийхээ мартах” асуудал. Мөн session.rollback()-ийг гараар дуудаж болно — жишээ нь exception-ийг барьж аваад дараагийн ажлыг үргэлжлүүлэхийн өмнө. Нэг endpoint-ийн бүх өөрчлөлт нэг commit()-ээр хадгалагдаж байгаа нь endpoint бүрийг нэг гэрээ болгодог — FastAPI сургалтын код та мэдэлгүйгээр зөв бүтэцтэй байжээ.
Хэрэв сонирхож байвал psql-д BEGIN хийж, хоёр дансны үлдэгдлийг өөрчлөөд, COMMIT ч ROLLBACK ч хийлгүйгээр \q-ээр psql-ээс гараарай. Дахин холбогдоход өөрчлөлт хадгалагдсан уу? (Сэжүүр: холболт тасрахад PostgreSQL дуусаагүй transaction-ийг юу хийдэг вэ — зүйрлэлд нотариатч гарын үсэг зуралгүй гараад явбал хавтас хүчинтэй юу?) Мөн transfer функцэд amount <= 0 бол ValueError шидэх шалгалт нэмж үзээрэй — сөрөг дүнтэй шилжүүлэг ямар хор хөнөөлтэйг бодоорой. Эдгээр нь зөвхөн санал бөгөөд дараагийн хичээлүүд тэдгээрийг хийсэн гэж тооцохгүй.
Алдаатай transaction-д үргэлжлүүлэх. psql-ийн урилга ! тэмдэгтэй үед ямар ч тушаал current transaction is aborted, commands ignored until end of transaction block гэж хариулна. Энэ мессеж нь “ROLLBACK бич” гэсэн шууд заавар. Python-д ижил байдал sqlalchemy.exc.InternalError: (psycopg.errors.InFailedSqlTransaction) current transaction is aborted хэлбэрээр гарч болно — ихэнхдээ exception-ийг барьж аваад rollback хийлгүй ижил холболтоор үргэлжлүүлсэн үед.
BEGIN-ийг мартаад ROLLBACK бичих. psql-д transaction эхлүүлээгүй байхад ROLLBACK бичвэл WARNING: there is no transaction in progress гарна — "явагдаж буй transaction байхгүй." Энэ нь өмнөх тушаалууд тань autocommit-оор аль хэдийн хадгалагдсан гэсэн аймшигтай дохио: ROLLBACK юуг ч буцаасангүй. Эрсдэлтэй ажлын өмнө урилгад * байгаа эсэхийг шалга.
0 мөр өөрчлөлтийг амжилт гэж үзэх. Жишээ 4-т харсанчлан UPDATE эсвэл DELETE-ийн WHERE мөр олохгүй бол алдаа гарахгүй, transaction COMMIT хийгдэнэ. Олон алхамт үйлдэлд алхам бүрийн rowcount (psql-д “UPDATE n”)-ийг хүлээлттэй харьцуулж, таарахгүй бол exception шид.
Transaction-ийг хэт удаан нээлттэй байлгах. psql-д BEGIN хийгээд кофе уухаар явбал, эсвэл Python-д transaction дотор хэрэглэгчийн оролт (input()), сүлжээний удаан хүсэлт хүлээвэл тэр transaction-ий өөрчилсөн мөрүүд бусад холболтод түгжигдсэн хэвээр үлдэнэ. Энэ асуудлыг дараагийн хичээлд хоёр psql цонхоор өөрийн нүдээр харна.
COMMIT хийсний дараа ROLLBACK-д найдах. COMMIT-ийн дараа ROLLBACK бичвэл дээрх no transaction in progress анхааруулга гарна — хадгалагдсан өөрчлөлт бат бөх. Өмнөх хичээлийн “ROLLBACK бол undo товч биш” гэсэн сургамж.
Энэ хичээлд та transaction-ийг өөрийн гараар удирдаж сурлаа. BEGIN нь transaction эхлүүлж (psql-ийн урилгад *), COMMIT нь бүх өөрчлөлтийг нэгэн зэрэг хадгалж, ROLLBACK нь бүгдийг цуцалдаг. Өмнөх хичээлийн бүтэлгүйтсэн шилжүүлгийг transaction дотор давтахад хоёр дахь алхмын алдаа transaction-ийг алдаатай төлөвт (урилгад !) оруулж, ROLLBACK эхний алхмыг ч цуцлан, өмнө ба дараах хүснэгт яг ижил үлдсэн; PostgreSQL алдаатай transaction-ийг хадгалахаас татгалздаг. BEGIN ба ROLLBACK нь psql дахь DB Browser-ийн Revert Changes бөгөөд эрсдэлтэй өөрчлөлтийг шалгаад шийдэх аюулгүйн бүс болдог. Python-д engine.begin() блок алдаагүй бол COMMIT, exception-тэй бол ROLLBACK хийдэг; constraint зөрчлийг өгөгдлийн сан өөрөө илрүүлдэг ч “0 мөр өөрчилсөн” гэх мэт логик бүтэлгүйтлийг та rowcount-оор шалгаж exception шидэх ёстой — transaction зөвхөн алдаа гарсан үед л хамгаалдаг.
Дараагийн хичээлээр Бүлэг 12 дуусна. Өнөөг хүртэл бид нэг холболтоор ажилласан. Гэвч Бүлэг 10-ын гол санаа — олон холболт нэгэн зэрэг. Хоёр transaction нэг мөрийг зэрэг өөрчлөх гэвэл юу болох вэ? Бид хоёр psql цонх нээж, тэднийг санаатайгаар мөргөлдүүлж, түгжээ (lock) гэж юу болохыг, хоёр transaction бие биеэ мөнхөд хүлээх deadlock-ийг, мөн “transaction-ийг богино байлга” гэсэн практик дүрмийг харна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.