Transaksi basisdata¶
Django memberikan anda sedikit cara mengendalikan bagaimana transaksi basisdata dikelola.
Mengelola transaksi basisdata¶
Kebiasaan transaksi awal Django¶
Kebiasaan awal Django adalah menjalankan suasana penyeraahn otomatis. Setiap query segera diserahkan ke basisdata, meskipun transaksi adalah aktif. Lihat dibawah ini untuk rincian.
Django menggunakan transaksi atau titiksimpan otomatis untuk menjamin kesatuan dari operasi ORM yang membutuhkan banyak query, khususnya query delete() dan update().
Django’s TestCase
class also wraps each test in a
transaction for performance reasons.
Tying transactions to HTTP requests¶
Cara umum untuk menangani transaksi di jaringan adalah membungkus setiap permintaan di transaksi. Setel ATOMIC_REQUESTS
ke True
di konfigurasi dari setiap basisdata untuk anda ingin adakan kebiasaan ini.
It works like this. Before calling a view function, Django starts a transaction. If the response is produced without problems, Django commits the transaction. If the view produces an exception, Django rolls back the transaction.
You may perform subtransactions using savepoints in your view code, typically
with the atomic()
context manager. However, at the end of the view,
either all or none of the changes will be committed.
Peringatan
While the simplicity of this transaction model is appealing, it also makes it inefficient when traffic increases. Opening a transaction for every view has some overhead. The impact on performance depends on the query patterns of your application and on how well your database handles locking.
Per-request transactions and streaming responses
When a view returns a StreamingHttpResponse
, reading
the contents of the response will often execute code to generate the
content. Since the view has already returned, such code runs outside of
the transaction.
Generally speaking, it isn’t advisable to write to the database while generating a streaming response, since there’s no sensible way to handle errors after starting to send the response.
In practice, this feature simply wraps every view function in the atomic()
decorator described below.
Note that only the execution of your view is enclosed in the transactions. Middleware runs outside of the transaction, and so does the rendering of template responses.
Ketika ATOMIC_REQUESTS
diadakan, itu masih memungkinkan mencegah tampilan dari berjalan dalam sebuah transaksi.
-
non_atomic_requests
(using=None)[sumber]¶ Penghias ini akan meniadakan pengaruh dari
ATOMIC_REQUESTS
untuk tampilan yang diberikan:from django.db import transaction @transaction.non_atomic_requests def my_view(request): do_stuff() @transaction.non_atomic_requests(using='other') def my_other_view(request): do_stuff_on_the_other_database()
It only works if it’s applied to the view itself.
Mengendalikan transaksi secara eksplisit¶
Django provides a single API to control database transactions.
-
atomic
(using=None, savepoint=True)[sumber]¶ Atomicity is the defining property of database transactions.
atomic
allows us to create a block of code within which the atomicity on the database is guaranteed. If the block of code is successfully completed, the changes are committed to the database. If there is an exception, the changes are rolled back.Blok
atomic
dapat bersarang. Dalam kasus ini, ketika sebuah blok sebelah dalam berhasil terpenuhi, pengaruhnya dapat masih di rollback jika sebuah pengecualian dimunculkan di blok sebelah luar pada titik kemudian.atomic
is usable both as a decorator:from django.db import transaction @transaction.atomic def viewfunc(request): # This code executes inside a transaction. do_stuff()
dan sebagai context manager:
from django.db import transaction def viewfunc(request): # This code executes in autocommit mode (Django's default). do_stuff() with transaction.atomic(): # This code executes inside a transaction. do_more_stuff()
Wrapping
atomic
in a try/except block allows for natural handling of integrity errors:from django.db import IntegrityError, transaction @transaction.atomic def viewfunc(request): create_parent() try: with transaction.atomic(): generate_relationships() except IntegrityError: handle_exception() add_children()
In this example, even if
generate_relationships()
causes a database error by breaking an integrity constraint, you can execute queries inadd_children()
, and the changes fromcreate_parent()
are still there. Note that any operations attempted ingenerate_relationships()
will already have been rolled back safely whenhandle_exception()
is called, so the exception handler can also operate on the database if necessary.Avoid catching exceptions inside
atomic
!When exiting an
atomic
block, Django looks at whether it’s exited normally or with an exception to determine whether to commit or roll back. If you catch and handle exceptions inside anatomic
block, you may hide from Django the fact that a problem has happened. This can result in unexpected behavior.This is mostly a concern for
DatabaseError
and its subclasses such asIntegrityError
. After such an error, the transaction is broken and Django will perform a rollback at the end of theatomic
block. If you attempt to run database queries before the rollback happens, Django will raise aTransactionManagementError
. You may also encounter this behavior when an ORM-related signal handler raises an exception.The correct way to catch database errors is around an
atomic
block as shown above. If necessary, add an extraatomic
block for this purpose. This pattern has another advantage: it delimits explicitly which operations will be rolled back if an exception occurs.If you catch exceptions raised by raw SQL queries, Django’s behavior is unspecified and database-dependent.
In order to guarantee atomicity,
atomic
disables some APIs. Attempting to commit, roll back, or change the autocommit state of the database connection within anatomic
block will raise an exception.atomic
takes ausing
argument which should be the name of a database. If this argument isn’t provided, Django uses the"default"
database.Dibawah tenda, kode pengelolaan transaksi Django:
membuka sebuah transaksi ketika memasukkan blok
atomic
paling luar;membuat titik simpan ketika memasukkan blok
atomic
sebelah dalam;melepaskan atau digulung kembali ke titik simpan ketika keluar blok paling dalam;
menyerahkan atau gulung kembali transaksi ketika keluar blok paling luar
Anda dapat meniadakan pembuatan dari titik simpan untuk blok sebelah dalam dengan mengatur argumen
savepoint
keFalse
. Jika sebuah pengecualian muncul, Django akan melakukan gulunh kembali ketika keluar blok induk pertama dengan sebuah titik simpan jika ada, dan blok paling luar jika tidak. Atomicity masih dijamin oleh transaksi paling luar. Pilihan ini harus hanya digunakan jika kelebihan dari titik simpan dapat dilihat. Itu mempunyai kekurangan dari memecahkan penanganan kesalahan yang digambarkan diatas.Anda dapat menggunakan
atomic
ketika penyerahan otomatis dimatikan. Itu akan hanya menggunakan titik simpan, bahkan untuk blok paling luar.
Pertimbangan penampilan
Open transactions have a performance cost for your database server. To
minimize this overhead, keep your transactions as short as possible. This
is especially important if you’re using atomic()
in long-running
processes, outside of Django’s request / response cycle.
Penyerahan otomatis¶
Kenapa Django menggunakan penyerahan otomatis¶
Dalam standar SQL, setiap permintaan SQL memulai sebuah transaksi, meskipun satu sudah aktif. Transaksi tersebut harus secara eksplisit diserahkan dan digulung kembali.
Ini tidak selalu mudah untuk pengembang aplikasi. Untuk meredakan masalah ini, kebanyakan basisdata menyediakan sebuah suasana penyerahan otomatis. Ketika penyerahan otomatis dinayalan dan tidak ada transaksi aktif, setiap permintaan SQL dibungkus dalam transaksinya sendiri. Dengan kata lain, bukan hanya melakukan setiap permintaan tersebut mulai sebuah transaksi, tetapi transaksi juga mendapatkan otomatis diserahkan atau digulung kembali, tergandunt pada apakah permintaan berhasil.
PEP 249, the Python Database API Specification v2.0, requires autocommit to be initially turned off. Django overrides this default and turns autocommit on.
Untuk menghindari ini, anda dapat menonaktifkan pengelolaan transaksi, tetapi itu sangat tidak dianjurkan.
Menonaktifkan pengelolaan transaksi¶
You can totally disable Django’s transaction management for a given database
by setting AUTOCOMMIT
to False
in its
configuration. If you do this, Django won’t enable autocommit, and won’t
perform any commits. You’ll get the regular behavior of the underlying
database library.
Ini membutuhkan anda untuk menyerahkan secara eksplisit setiap transaksi, bahkan yang dimulai oleh Django atau oleh pustaka pihak ketiga. Jadi, ini adalah penggunaan terbaik dalam keadaan dimana anda ingin menjalankan middleware pengendalian transaksi milik anda atau melakukan sesuatu yang sangat aneh.
Melakukan tindakan setelah penyerahan¶
Terkadang anda butuh melakukan sebuah tindakan terkait pada transaksi basisdata saat ini, tetapi hanya jika transaksi berhasil diserahkan. Contoh mungkin termasuk tugas Celery, sebuah pemberitahuan surel, atau penghapusan tembolok
Django menyediakan fungsi on_commit()
untuk mendaftarkan fungsi panggil kembali yang harusnya dijalankan setelah transaksi berhasil diserahkan:
Lewati fungsi apapun (yang tidak mengambil argumen) ke on_commit()
:
from django.db import transaction
def do_something():
pass # send a mail, invalidate a cache, fire off a Celery task, etc.
transaction.on_commit(do_something)
Anda dapat juga membungkus fungsi anda dalam lambda:
transaction.on_commit(lambda: some_celery_task.delay('arg1'))
Fungsi anda lewati akan dipanggil segera setelah hipotetis penulisan basisdata dibuat dimana on_commit()
dipanggil akan berhasil diserahkan.
Jika anda memanggil on_commit()
selagi tidak ada sebiah transaksi aktif, panggil kembali akan dijalankan segera.
Jika hipotetis penulisan basisdata itu bukannya digulung kembali (khususnya ketika sebuah pengecualian tidak tertangani dimunculkan dalam sebuah blok atomic()
), fungsi anda akan disingkirkan dan tidak pernah dipanggil.
Savepoint¶
Titik simpan (yaitu blok atomic()
bersarang) ditangani dengan benar. Yaitu, sebuah on_commit()
callable terdaftar setelah sebuah titik simpan (dalam blok atomic()
bersarang) akan dipanggil setelah transaksi paling luar diserahkan, tetapi tidak jika disimpan kembali ke titik simpan tersebut atau titik simpan sebelumnya yang muncul selama transaksi:
with transaction.atomic(): # Outer atomic, start a new transaction
transaction.on_commit(foo)
with transaction.atomic(): # Inner atomic block, create a savepoint
transaction.on_commit(bar)
# foo() and then bar() will be called when leaving the outermost block
Pada sisi lain, ketika sebuah titik simpan disimpan kembali (karena sebuah pengecualian telah dimunculkan), sebe;ah dalam dapat dipanggil tidak akan dipanggil:
with transaction.atomic(): # Outer atomic, start a new transaction
transaction.on_commit(foo)
try:
with transaction.atomic(): # Inner atomic block, create a savepoint
transaction.on_commit(bar)
raise SomeError() # Raising an exception - abort the savepoint
except SomeError:
pass
# foo() will be called, but not bar()
Urutan pelaksanaan¶
On-commit functions for a given transaction are executed in the order they were registered.
Penanganan pengecualian¶
If one on-commit function within a given transaction raises an uncaught
exception, no later registered functions in that same transaction will run.
This is, of course, the same behavior as if you’d executed the functions
sequentially yourself without on_commit()
.
Waktu pelaksanaan¶
Gulungan kembali anda dijalankan setelah penyerahan berhasil, jadi sebuah kegagalan dalam gulung kembali tidak akan menyebabkan transaksi di gulung kembali. Mereka dijalankan kondisional atas transaksi berhasil, tetapi mereka bukan bagian dari transaksi. Untuk penggunaan kasus dimaksud (pemberitahuan surat, tugas Celery, dll.), ini harus baik-baik saja. Jika tidak (jika tindakan mengikuti anda juga gawat dimana kegagalan itu harus berarti kegagalan dari transaksi itu sendiri), lalu anda tidak ingin menggunakan hubungan on_commit()
. Sebagai gantinya, anda ingin two-phase commit seperti psycopg Two-Phase Commit protocol support dan optional Two-Phase Commit Extensions in the Python DB-API specification.
Callback tidak berjalan sampai perbaikan otomatis dipulihkan pada hubungan perbaikan berikut (karena sebaliknya apapun permintaan selesai dalam sebuah callback akan membuka sebuah transaksi tersirat, mencegah hubungan dari kembali kedalam suasana perbaikan otomatis).
When in autocommit mode and outside of an atomic()
block, the function
will run immediately, not on commit.
On-commit functions only work with autocommit mode
and the atomic()
(or ATOMIC_REQUESTS
) transaction API. Calling on_commit()
when
autocommit is disabled and you are not within an atomic block will result in an
error.
Use in tests¶
Django’s TestCase
class wraps each test in a transaction
and rolls back that transaction after each test, in order to provide test
isolation. This means that no transaction is ever actually committed, thus your
on_commit()
callbacks will never be run. If you need to test the results
of an on_commit()
callback, use a
TransactionTestCase
instead.
Why no rollback hook?¶
A rollback hook is harder to implement robustly than a commit hook, since a variety of things can cause an implicit rollback.
For instance, if your database connection is dropped because your process was killed without a chance to shut down gracefully, your rollback hook will never run.
The solution is simple: instead of doing something during the atomic block
(transaction) and then undoing it if the transaction fails, use
on_commit()
to delay doing it in the first place until after the
transaction succeeds. It’s a lot easier to undo something you never did in the
first place!
Low-level APIs¶
Peringatan
Selalu memilih atomic()
jika memungkan sama sekali. Itu menghitung untuk keanehan dari setiap basisdata dan mecegah tindakan-tindakan tidak sah.
The low level APIs are only useful if you’re implementing your own transaction management.
Penyerahan otomatis¶
Django provides a straightforward API in the django.db.transaction
module to manage the autocommit state of each database connection.
These functions take a using
argument which should be the name of a
database. If it isn’t provided, Django uses the "default"
database.
Autocommit is initially turned on. If you turn it off, it’s your responsibility to restore it.
Once you turn autocommit off, you get the default behavior of your database adapter, and Django won’t help you. Although that behavior is specified in PEP 249, implementations of adapters aren’t always consistent with one another. Review the documentation of the adapter you’re using carefully.
You must ensure that no transaction is active, usually by issuing a
commit()
or a rollback()
, before turning autocommit back on.
Django will refuse to turn autocommit off when an atomic()
block is
active, because that would break atomicity.
Transaksi¶
A transaction is an atomic set of database queries. Even if your program crashes, the database guarantees that either all the changes will be applied, or none of them.
Django doesn’t provide an API to start a transaction. The expected way to
start a transaction is to disable autocommit with set_autocommit()
.
Once you’re in a transaction, you can choose either to apply the changes
you’ve performed until this point with commit()
, or to cancel them with
rollback()
. These functions are defined in django.db.transaction
.
These functions take a using
argument which should be the name of a
database. If it isn’t provided, Django uses the "default"
database.
Django will refuse to commit or to rollback when an atomic()
block is
active, because that would break atomicity.
Savepoint¶
A savepoint is a marker within a transaction that enables you to roll back part of a transaction, rather than the full transaction. Savepoints are available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using the InnoDB storage engine) backends. Other backends provide the savepoint functions, but they’re empty operations – they don’t actually do anything.
Savepoints aren’t especially useful if you are using autocommit, the default
behavior of Django. However, once you open a transaction with atomic()
,
you build up a series of database operations awaiting a commit or rollback. If
you issue a rollback, the entire transaction is rolled back. Savepoints
provide the ability to perform a fine-grained rollback, rather than the full
rollback that would be performed by transaction.rollback()
.
When the atomic()
decorator is nested, it creates a savepoint to allow
partial commit or rollback. You’re strongly encouraged to use atomic()
rather than the functions described below, but they’re still part of the
public API, and there’s no plan to deprecate them.
Each of these functions takes a using
argument which should be the name of
a database for which the behavior applies. If no using
argument is
provided then the "default"
database is used.
Savepoints are controlled by three functions in django.db.transaction
:
-
savepoint
(using=None)[sumber]¶ Creates a new savepoint. This marks a point in the transaction that is known to be in a “good” state. Returns the savepoint ID (
sid
).
-
savepoint_commit
(sid, using=None)[sumber]¶ Releases savepoint
sid
. The changes performed since the savepoint was created become part of the transaction.
These functions do nothing if savepoints aren’t supported or if the database is in autocommit mode.
In addition, there’s a utility function:
The following example demonstrates the use of savepoints:
from django.db import transaction
# open a transaction
@transaction.atomic
def viewfunc(request):
a.save()
# transaction now contains a.save()
sid = transaction.savepoint()
b.save()
# transaction now contains a.save() and b.save()
if want_to_keep_b:
transaction.savepoint_commit(sid)
# open transaction still contains a.save() and b.save()
else:
transaction.savepoint_rollback(sid)
# open transaction now contains only a.save()
Savepoint dapat digunakan untuk memulihkan dari kesalahan basisdata dengan melakukan rollback sebagian. Jika anda sedang melakukan ini didalam sebuah blok atomic()
, keseluruhan blok akan masih dapat di rollback, karena itu tidak mengetahui anda telah menangani keadaan pada tingkatan terendah! Untuk mencegah ini, anda dapat mengendalikan perilaku rollback dengan fungsi-fungsi berikut.
Setel bendera rollback menjadi True
memaksa sebuah rollback ketika keluar blok atomik paling sebelah dalam. Ini mungkin berguna untuk memicu sebuah rollback tanpa memunculkan sebuah pengecualian.
Setel itu menjadi False
mencegah rollback seperti itu. Sebelum melakukan itu, pastikan anda telah rollback transaksi pada savepoint dikenal-baik dalam blok atomic saat ini! Sebaliknya anda sedang merusak atomic dan kerusakan data mungkin muncul.
Database-specific notes¶
Savepoint di SQLite¶
While SQLite ≥ 3.6.8 supports savepoints, a flaw in the design of the
sqlite3
module makes them hardly usable.
When autocommit is enabled, savepoints don’t make sense. When it’s disabled,
sqlite3
commits implicitly before savepoint statements. (In fact, it
commits before any statement other than SELECT
, INSERT
, UPDATE
,
DELETE
and REPLACE
.) This bug has two consequences:
Transaksi di MySQL¶
If you’re using MySQL, your tables may or may not support transactions; it depends on your MySQL version and the table types you’re using. (By “table types,” we mean something like “InnoDB” or “MyISAM”.) MySQL transaction peculiarities are outside the scope of this article, but the MySQL site has information on MySQL transactions.
If your MySQL setup does not support transactions, then Django will always function in autocommit mode: statements will be executed and committed as soon as they’re called. If your MySQL setup does support transactions, Django will handle transactions as explained in this document.
Penanganan pengecualian dalam transaksi PostgreSQL¶
Catatan
This section is relevant only if you’re implementing your own transaction
management. This problem cannot occur in Django’s default mode and
atomic()
handles it automatically.
Inside a transaction, when a call to a PostgreSQL cursor raises an exception
(typically IntegrityError
), all subsequent SQL in the same transaction
will fail with the error “current transaction is aborted, queries ignored
until end of transaction block”. While simple use of save()
is unlikely
to raise an exception in PostgreSQL, there are more advanced usage patterns
which might, such as saving objects with unique fields, saving using the
force_insert/force_update flag, or invoking custom SQL.
There are several ways to recover from this sort of error.
Gulung kembali transaksi¶
The first option is to roll back the entire transaction. For example:
a.save() # Succeeds, but may be undone by transaction rollback
try:
b.save() # Could throw exception
except IntegrityError:
transaction.rollback()
c.save() # Succeeds, but a.save() may have been undone
Calling transaction.rollback()
rolls back the entire transaction. Any
uncommitted database operations will be lost. In this example, the changes
made by a.save()
would be lost, even though that operation raised no error
itself.
Savepoint rollback¶
You can use savepoints to control the extent of a rollback. Before performing a database operation that could fail, you can set or update the savepoint; that way, if the operation fails, you can roll back the single offending operation, rather than the entire transaction. For example:
a.save() # Succeeds, and never undone by savepoint rollback
sid = transaction.savepoint()
try:
b.save() # Could throw exception
transaction.savepoint_commit(sid)
except IntegrityError:
transaction.savepoint_rollback(sid)
c.save() # Succeeds, and a.save() is never undone
In this example, a.save()
will not be undone in the case where
b.save()
raises an exception.