Ce face Tranzacții cu sesiuni (startTransaction)
Scrierile pe un singur document sunt atomice și fără tranzacții. Când o operație atinge mai multe documente sau colecții (scazi stocul ȘI creezi comanda), o tranzacție garantează că fie se aplică toate, fie niciuna. Tranzacțiile multi-document există din MongoDB 4.0 pe replica set și din 4.2 pe clustere sharded; pe un server de sine stătător nu funcționează.
În mongosh pornești o sesiune, iei colecțiile prin session.getDatabase(...), apoi startTransaction(), scrierile și commitTransaction(). Până la commit, celelalte conexiuni nu văd modificările. abortTransaction() le anulează. O tranzacție trebuie să fie scurtă: implicit este anulată după 60 de secunde (transactionLifetimeLimitSeconds).
Parametri
| Parametru | Descriere |
|---|---|
startSession() |
Creează sesiunea în care rulează tranzacția. |
readConcern / writeConcern |
Nivelurile de consistență; snapshot și majority sunt alegerea obișnuită. |
commitTransaction() |
Aplică toate modificările. |
abortTransaction() |
Anulează toate modificările. |
withTransaction(fn) |
Rulează funcția într-o tranzacție și o reîncearcă automat la erori temporare. |
Ce returnează
startTransaction(), commitTransaction() și abortTransaction() nu afișează nimic dacă reușesc; scrierile din interior întorc rezultatele obișnuite.
Exemple
Exemplele de pe această pagină pornesc de la datele de mai jos. Rulează mai întâi acest cod, apoi exemplul.
use magazin
db.produse.insertOne({ _id: 'caiet', stoc: 40, pret: 12 })
Exemplul 1: Comandă și scăderea stocului, împreună
Dacă oricare pas ar eșua, am apela abortTransaction() și nimic nu s-ar salva.
const session = db.getMongo().startSession()
const s = session.getDatabase('magazin')
session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } })
s.produse.updateOne({ _id: 'caiet', stoc: { $gte: 2 } }, { $inc: { stoc: -2 } })
s.comenzi.insertOne({ _id: 7, client: 'Ana', produse: ['caiet', 'caiet'], total: 24 })
session.commitTransaction()
session.endSession()
Ieșire în mongosh
rs0 [direct: primary] magazin> const session = db.getMongo().startSession()
rs0 [direct: primary] magazin> const s = session.getDatabase('magazin')
rs0 [direct: primary] magazin> session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } })
rs0 [direct: primary] magazin> s.produse.updateOne({ _id: 'caiet', stoc: { $gte: 2 } }, { $inc: { stoc: -2 } })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
rs0 [direct: primary] magazin> s.comenzi.insertOne({ _id: 7, client: 'Ana', produse: ['caiet', 'caiet'], total: 24 })
{ acknowledged: true, insertedId: 7 }
rs0 [direct: primary] magazin> session.commitTransaction()
rs0 [direct: primary] magazin> session.endSession()
Exemplul 2: Pe un server fără replica set
Tranzacțiile au nevoie de un replica set (pentru dezvoltare ajunge unul cu un singur membru). Pe un server de sine stătător, prima scriere din tranzacție dă eroare.
const session = db.getMongo().startSession()
session.startTransaction()
session.getDatabase('magazin').comenzi.insertOne({ _id: 8, client: 'Dan', total: 10 })
Ieșire în mongosh
magazin> const session = db.getMongo().startSession()
magazin> session.startTransaction()
magazin> session.getDatabase('magazin').comenzi.insertOne({ _id: 8, client: 'Dan', total: 10 })
MongoServerError[IllegalOperation]: Transaction numbers are only allowed on a replica set member or mongos
Greșeli frecvente
- Operațiile trebuie făcute prin colecțiile obținute din sesiune (
session.getDatabase(...)); cele luate direct dindbnu fac parte din tranzacție. - Nu ține tranzacții deschise în timp ce aștepți utilizatorul sau un serviciu extern: blochează resurse și sunt anulate după 60 de secunde.
- Multe probleme se rezolvă fără tranzacții, prin modelarea datelor în același document și operații atomice (
$inc,findOneAndUpdate).
Comenzi înrudite
bulkWrite(): Trimite mai multe operații de scriere (inserări, actualizări, ștergeri) către o colecție într-o singură cerere.findOneAndUpdate(): Actualizează un document și îl întoarce, atomic, într-un singur pas; există și findOneAndDelete și findOneAndReplace.upsert: true și $setOnInsert: Cu upsert: true, updateOne actualizează documentul găsit sau, dacă nu există, îl creează; $setOnInsert scrie câmpuri doar la creare.