def capture_unique_violation(self): """Trigger a unique violation, return its ``exc_info`` tuple.""" try: self.cause_unique_violation() except IntegrityError as e: if is_unique_violation(e): return sys.exc_info() else: raise
def _attempt_allocation_of_free_address(self, requested_address, alloc_type, user=None, subnet=None): """Attempt to allocate `requested_address`, which is known to be free. It is known to be free *in this transaction*, so this could still fail. If it does fail because of a `UNIQUE_VIOLATION` it will request a retry, except while holding an addition lock. This is not perfect: other threads could jump in before acquiring the lock and steal an apparently free address. However, in stampede situations this appears to be effective enough. Experiment by increasing the `count` parameter in `test_allocate_new_works_under_extreme_concurrency`. This method shares a lot in common with `_attempt_allocation` so check out its documentation for more details. :param requested_address: The address to be allocated. :typr requested_address: IPAddress :param alloc_type: Allocation type. :param user: Optional user. :return: `StaticIPAddress` if successful. :raise RetryTransaction: if the address was already taken. """ ipaddress = StaticIPAddress(alloc_type=alloc_type, subnet=subnet) try: # Try to save this address to the database. Do this in a nested # transaction so that we can continue using the outer transaction # even if this breaks. with orm.savepoint(): ipaddress.set_ip_address(requested_address.format()) ipaddress.save() except IntegrityError as error: if orm.is_unique_violation(error): # The address is taken. We could allow the transaction retry # machinery to take care of this, but instead we'll ask it to # retry with the `address_allocation` lock. We can't take it # here because we're already in a transaction; we need to exit # the transaction, take the lock, and only then try again. orm.request_transaction_retry(locks.address_allocation) else: raise else: # We deliberately do *not* save the user until now because it # might result in an IntegrityError, and we rely on the latter # in the code above to indicate an already allocated IP # address and nothing else. ipaddress.user = user ipaddress.save() return ipaddress