|
1 | 1 | import _thread
|
2 | 2 | import copy
|
3 | 3 | import datetime
|
| 4 | +import logging |
4 | 5 | import threading
|
5 | 6 | import time
|
6 | 7 | import warnings
|
|
26 | 27 | NO_DB_ALIAS = "__no_db__"
|
27 | 28 | RAN_DB_VERSION_CHECK = set()
|
28 | 29 |
|
| 30 | +logger = logging.getLogger("django.db.backends.base") |
| 31 | + |
29 | 32 |
|
30 | 33 | # RemovedInDjango50Warning
|
31 | 34 | def timezone_constructor(tzname):
|
@@ -417,7 +420,9 @@ def savepoint_rollback(self, sid):
|
417 | 420 |
|
418 | 421 | # Remove any callbacks registered while this savepoint was active.
|
419 | 422 | self.run_on_commit = [
|
420 |
| - (sids, func) for (sids, func) in self.run_on_commit if sid not in sids |
| 423 | + (sids, func, robust) |
| 424 | + for (sids, func, robust) in self.run_on_commit |
| 425 | + if sid not in sids |
421 | 426 | ]
|
422 | 427 |
|
423 | 428 | @async_unsafe
|
@@ -723,28 +728,49 @@ def schema_editor(self, *args, **kwargs):
|
723 | 728 | )
|
724 | 729 | return self.SchemaEditorClass(self, *args, **kwargs)
|
725 | 730 |
|
726 |
| - def on_commit(self, func): |
| 731 | + def on_commit(self, func, robust=False): |
727 | 732 | if not callable(func):
|
728 | 733 | raise TypeError("on_commit()'s callback must be a callable.")
|
729 | 734 | if self.in_atomic_block:
|
730 | 735 | # Transaction in progress; save for execution on commit.
|
731 |
| - self.run_on_commit.append((set(self.savepoint_ids), func)) |
| 736 | + self.run_on_commit.append((set(self.savepoint_ids), func, robust)) |
732 | 737 | elif not self.get_autocommit():
|
733 | 738 | raise TransactionManagementError(
|
734 | 739 | "on_commit() cannot be used in manual transaction management"
|
735 | 740 | )
|
736 | 741 | else:
|
737 | 742 | # No transaction in progress and in autocommit mode; execute
|
738 | 743 | # immediately.
|
739 |
| - func() |
| 744 | + if robust: |
| 745 | + try: |
| 746 | + func() |
| 747 | + except Exception as e: |
| 748 | + logger.error( |
| 749 | + f"Error calling {func.__qualname__} in on_commit() (%s).", |
| 750 | + e, |
| 751 | + exc_info=True, |
| 752 | + ) |
| 753 | + else: |
| 754 | + func() |
740 | 755 |
|
741 | 756 | def run_and_clear_commit_hooks(self):
|
742 | 757 | self.validate_no_atomic_block()
|
743 | 758 | current_run_on_commit = self.run_on_commit
|
744 | 759 | self.run_on_commit = []
|
745 | 760 | while current_run_on_commit:
|
746 |
| - sids, func = current_run_on_commit.pop(0) |
747 |
| - func() |
| 761 | + _, func, robust = current_run_on_commit.pop(0) |
| 762 | + if robust: |
| 763 | + try: |
| 764 | + func() |
| 765 | + except Exception as e: |
| 766 | + logger.error( |
| 767 | + f"Error calling {func.__qualname__} in on_commit() during " |
| 768 | + f"transaction (%s).", |
| 769 | + e, |
| 770 | + exc_info=True, |
| 771 | + ) |
| 772 | + else: |
| 773 | + func() |
748 | 774 |
|
749 | 775 | @contextmanager
|
750 | 776 | def execute_wrapper(self, wrapper):
|
|
0 commit comments