Transaksi NDB

Transaksi adalah operasi atau kumpulan operasi yang dijamin bersifat atomik, yang berarti bahwa transaksi tidak pernah diterapkan sebagian. Semua operasi dalam transaksi tersebut akan diterapkan, atau tidak ada yang diterapkan. Transaksi memiliki durasi maksimum 60 detik dengan masa berlaku tidak ada aktivitas 10 detik setelah 30 detik.

Dengan API asinkron NDB, aplikasi dapat mengelola beberapa transaksi secara bersamaan jika transaksi tersebut independen. API sinkron menawarkan API yang disederhanakan menggunakan dekorator @ndb.transactional(). Fungsi yang didekorasi dijalankan dalam konteks transaksi.

@ndb.transactional
def insert_if_absent(note_key, note):
    fetch = note_key.get()
    if fetch is None:
        note.put()
        return True
    return False
note_key = ndb.Key(Note, note_title, parent=parent)
note = Note(key=note_key, content=note_text)
inserted = insert_if_absent(note_key, note)

Transaksi akan gagal jika transaksi tersebut "berbenturan" dengan transaksi lain; NDB akan otomatis mencoba kembali transaksi yang gagal tersebut beberapa kali. Fungsi ini dapat dipanggil beberapa kali jika transaksi dicoba lagi. Ada batas (default 3) jumlah percobaan ulang ; jika transaksi masih tidak berhasil, NDB akan memunculkan TransactionFailedError. Anda dapat mengubah jumlah percobaan ulang dengan meneruskan retries=N ke dekorator transactional(). Jumlah percobaan ulang 0 berarti transaksi dicoba sekali, tetapi tidak dicoba lagi jika gagal; jumlah percobaan ulang N berarti bahwa transaksi dapat dicoba hingga total N+1 kali. Contoh:

@ndb.transactional(retries=1)
def insert_if_absent_2_retries(note_key, note):
    # do insert

Dalam transaksi, hanya kueri ancestor yang diizinkan. Secara default, transaksi hanya dapat berfungsi dengan entity dalam entity group yang sama (entity yang kuncinya memiliki "ancestor" yang sama).

Anda dapat menentukan transaksi lintas grup ("XG") (yang memungkinkan hingga dua puluh lima grup entity), dengan meneruskan xg=True:

@ndb.transactional(xg=True)
def insert_if_absent_xg(note_key, note):
    # do insert

Transaksi lintas grup beroperasi di beberapa entity group, dan berperilaku seperti transaksi satu grup, tetapi tidak akan gagal jika kode mencoba memperbarui entity dari lebih dari satu entity group.

Jika fungsi memunculkan pengecualian, transaksi akan segera dibatalkan dan NDB memunculkan ulang pengecualian sehingga kode panggilan melihatnya. Anda dapat memaksa transaksi untuk gagal tanpa ada peringatan dengan memunculkan pengecualian ndb.Rollback (dalam hal ini, panggilan fungsi menampilkan None). Tidak ada mekanisme untuk memaksa percobaan ulang.

Anda mungkin memiliki fungsi yang tidak selalu ingin dijalankan dalam transaksi. Daripada mendekorasi fungsi tersebut dengan @ndb.transactional, teruskan sebagai fungsi callback ke ndb.transaction()

def insert_if_absent_sometimes(note_key, note):
    # do insert
inserted = ndb.transaction(lambda:
                           insert_if_absent_sometimes(note_key, note))

Untuk menguji apakah beberapa kode berjalan di dalam transaksi, gunakan fungsi in_transaction().

Anda dapat menentukan perilaku fungsi "transaksional" jika dipanggil oleh kode yang sudah ada dalam transaksi. Dekorator @ndb.non_transactional menentukan bahwa fungsi tidak boleh berjalan dalam transaksi; jika dipanggil dalam transaksi, fungsi akan berjalan di luar transaksi. Dekorator @ndb.transactional dan fungsi ndb.transaction menggunakan argumen kata kunci propagation. Misalnya, jika suatu fungsi harus memulai transaksi independen baru, dekorasikan seperti ini:

@ndb.transactional(propagation=ndb.TransactionOptions.INDEPENDENT)
def insert_if_absent_indep(note_key, note):
    # do insert

Jenis propagasi tercantum bersama Opsi Konteks dan Opsi Transaksi lainnya

Perilaku transaksi dan perilaku caching NDB dapat digabungkan untuk membingungkan Anda jika Anda tidak tahu apa yang terjadi. Jika Anda mengubah entity di dalam transaksi tetapi belum meng-commit transaksi, cache konteks NDB memiliki nilai yang telah diubah, tetapi datastore yang mendasarinya masih memiliki nilai yang tidak diubah.

Antrean tugas transaksional

Anda dapat mengantrekan tugas sebagai bagian dari transaksi Datastore, sehingga tugas hanya diantrekan jika transaksi berhasil di-commit. Jika transaksi tidak di-commit, tugas tidak akan diantrekan. Jika transaksi di-commit, tugas akan diantrekan. Setelah diantrekan, tugas tidak akan langsung dieksekusi, sehingga tugas tersebut tidak atomik dengan transaksi. Namun, setelah diantrekan, tugas akan dicoba lagi hingga berhasil. Hal ini berlaku untuk setiap tugas yang diantrekan selama fungsi yang didekorasi.

Tugas transaksional berguna karena memungkinkan Anda menggabungkan tindakan non-Datastore ke transaksi yang bergantung pada keberhasilan transaksi (seperti mengirim email untuk mengonfirmasi pembelian). Anda juga dapat mengaitkan tindakan Datastore ke transaksi, misalnya untuk melakukan perubahan pada entity group di luar transaksi jika dan hanya jika transaksi berhasil.

Aplikasi tidak dapat menyisipkan lebih dari lima tugas transaksional ke dalam task queue selama satu transaksi. Tugas transaksional tidak boleh memiliki nama yang ditentukan pengguna.

from google.appengine.api import taskqueue
from google.appengine.ext import ndb
@ndb.transactional
def insert_if_absent_taskq(note_key, note):
    taskqueue.add(url=flask.url_for('taskq_worker'), transactional=True)
    # do insert