| | 380 | |
| | 381 | Transaction management outside the web server |
| | 382 | ============================================= |
| | 383 | |
| | 384 | Django's typical transaction and connection management does not work outside |
| | 385 | the web server context. |
| | 386 | |
| | 387 | You may, for example, setup Django to run some recurring tasks in their own |
| | 388 | threads and notice that they leave behind unfinished database connection |
| | 389 | processes (in PostgreSQL this manifests as the connections being listed as |
| | 390 | "Idle In Transaction"). |
| | 391 | |
| | 392 | Looking through the Postgres logs, you may find that the transactions aren't |
| | 393 | being completed (no ``ROLLBACK``). Switching to manual transaction management |
| | 394 | and doing the rollback manually will work, but this still leaves the processes |
| | 395 | as "Idle". Finally, calling ``django.db.connection.close()`` will end these |
| | 396 | "Idle" processes. What's going on here and how can we fix it? |
| | 397 | |
| | 398 | Transactions |
| | 399 | ------------ |
| | 400 | |
| | 401 | Django's default :ref:`autocommit behavior<topics-db-transactions-autocommit>` |
| | 402 | holds true for threaded functions. However, as noted above: |
| | 403 | |
| | 404 | As soon as you perform an action that needs to write to the database, |
| | 405 | Django produces the ``INSERT/UPDATE/DELETE`` statements and then does the |
| | 406 | ``COMMIT``. There’s no implicit ``ROLLBACK``. |
| | 407 | |
| | 408 | That last sentence is very literal. Django DOES NOT issue a ``ROLLBACK`` |
| | 409 | command unless something in Django has set the dirty flag. If your functions |
| | 410 | are only performing ``SELECT`` statements, it won't set the dirty flag to |
| | 411 | trigger a ``COMMIT``. |
| | 412 | |
| | 413 | This goes against the fact that PostgreSQL thinks the transaction requires a |
| | 414 | ``ROLLBACK`` because Django issued a ``SET`` command for the timezone. |
| | 415 | |
| | 416 | Connections |
| | 417 | ----------- |
| | 418 | |
| | 419 | Connection management is where things get tricky. Django uses |
| | 420 | :data:`~django.core.signals.request_finished` to close the database connection |
| | 421 | it normally uses. Since nothing normally happens in Django that doesn't involve |
| | 422 | a request, you take this behavior for granted. |
| | 423 | |
| | 424 | In this case, though, there isn't a request because the thread is outside the |
| | 425 | web server process. No request means no signal. No signal means the database |
| | 426 | connection won't be closed. |
| | 427 | |
| | 428 | Going back to transactions, it turns out that simply issuing a call to |
| | 429 | ``django.db.connection.close()`` in the absence of any changes to the |
| | 430 | transaction management also issues the meeded ``ROLLBACK`` statement. |
| | 431 | |
| | 432 | Solution |
| | 433 | -------- |
| | 434 | |
| | 435 | The solution is to allow Django's transaction management to proceed as usual |
| | 436 | and to simply close the connection one of three ways: |
| | 437 | |
| | 438 | 1. Write a decorator that closes the connection and wrap the necessary |
| | 439 | functions in it. |
| | 440 | 2. Hook into the existing :mod:`request signals<django.core.signals>` to |
| | 441 | have Django close the connection. |
| | 442 | 3. Close the connection manually (``django.db.connection.close()``) at the end |
| | 443 | of the function. |