events.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. # pool/events.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. from __future__ import annotations
  8. import typing
  9. from typing import Any
  10. from typing import Optional
  11. from typing import Type
  12. from typing import Union
  13. from .base import ConnectionPoolEntry
  14. from .base import Pool
  15. from .base import PoolProxiedConnection
  16. from .base import PoolResetState
  17. from .. import event
  18. from .. import util
  19. if typing.TYPE_CHECKING:
  20. from ..engine import Engine
  21. from ..engine.interfaces import DBAPIConnection
  22. class PoolEvents(event.Events[Pool]):
  23. """Available events for :class:`_pool.Pool`.
  24. The methods here define the name of an event as well
  25. as the names of members that are passed to listener
  26. functions.
  27. e.g.::
  28. from sqlalchemy import event
  29. def my_on_checkout(dbapi_conn, connection_rec, connection_proxy):
  30. "handle an on checkout event"
  31. event.listen(Pool, "checkout", my_on_checkout)
  32. In addition to accepting the :class:`_pool.Pool` class and
  33. :class:`_pool.Pool` instances, :class:`_events.PoolEvents` also accepts
  34. :class:`_engine.Engine` objects and the :class:`_engine.Engine` class as
  35. targets, which will be resolved to the ``.pool`` attribute of the
  36. given engine or the :class:`_pool.Pool` class::
  37. engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
  38. # will associate with engine.pool
  39. event.listen(engine, "checkout", my_on_checkout)
  40. """ # noqa: E501
  41. _target_class_doc = "SomeEngineOrPool"
  42. _dispatch_target = Pool
  43. @util.preload_module("sqlalchemy.engine")
  44. @classmethod
  45. def _accept_with(
  46. cls,
  47. target: Union[Pool, Type[Pool], Engine, Type[Engine]],
  48. identifier: str,
  49. ) -> Optional[Union[Pool, Type[Pool]]]:
  50. if not typing.TYPE_CHECKING:
  51. Engine = util.preloaded.engine.Engine
  52. if isinstance(target, type):
  53. if issubclass(target, Engine):
  54. return Pool
  55. else:
  56. assert issubclass(target, Pool)
  57. return target
  58. elif isinstance(target, Engine):
  59. return target.pool
  60. elif isinstance(target, Pool):
  61. return target
  62. elif hasattr(target, "_no_async_engine_events"):
  63. target._no_async_engine_events()
  64. else:
  65. return None
  66. @classmethod
  67. def _listen(
  68. cls,
  69. event_key: event._EventKey[Pool],
  70. **kw: Any,
  71. ) -> None:
  72. target = event_key.dispatch_target
  73. kw.setdefault("asyncio", target._is_asyncio)
  74. event_key.base_listen(**kw)
  75. def connect(
  76. self,
  77. dbapi_connection: DBAPIConnection,
  78. connection_record: ConnectionPoolEntry,
  79. ) -> None:
  80. """Called at the moment a particular DBAPI connection is first
  81. created for a given :class:`_pool.Pool`.
  82. This event allows one to capture the point directly after which
  83. the DBAPI module-level ``.connect()`` method has been used in order
  84. to produce a new DBAPI connection.
  85. :param dbapi_connection: a DBAPI connection.
  86. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  87. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  88. the DBAPI connection.
  89. """
  90. def first_connect(
  91. self,
  92. dbapi_connection: DBAPIConnection,
  93. connection_record: ConnectionPoolEntry,
  94. ) -> None:
  95. """Called exactly once for the first time a DBAPI connection is
  96. checked out from a particular :class:`_pool.Pool`.
  97. The rationale for :meth:`_events.PoolEvents.first_connect`
  98. is to determine
  99. information about a particular series of database connections based
  100. on the settings used for all connections. Since a particular
  101. :class:`_pool.Pool`
  102. refers to a single "creator" function (which in terms
  103. of a :class:`_engine.Engine`
  104. refers to the URL and connection options used),
  105. it is typically valid to make observations about a single connection
  106. that can be safely assumed to be valid about all subsequent
  107. connections, such as the database version, the server and client
  108. encoding settings, collation settings, and many others.
  109. :param dbapi_connection: a DBAPI connection.
  110. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  111. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  112. the DBAPI connection.
  113. """
  114. def checkout(
  115. self,
  116. dbapi_connection: DBAPIConnection,
  117. connection_record: ConnectionPoolEntry,
  118. connection_proxy: PoolProxiedConnection,
  119. ) -> None:
  120. """Called when a connection is retrieved from the Pool.
  121. :param dbapi_connection: a DBAPI connection.
  122. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  123. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  124. the DBAPI connection.
  125. :param connection_proxy: the :class:`.PoolProxiedConnection` object
  126. which will proxy the public interface of the DBAPI connection for the
  127. lifespan of the checkout.
  128. If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current
  129. connection will be disposed and a fresh connection retrieved.
  130. Processing of all checkout listeners will abort and restart
  131. using the new connection.
  132. .. seealso:: :meth:`_events.ConnectionEvents.engine_connect`
  133. - a similar event
  134. which occurs upon creation of a new :class:`_engine.Connection`.
  135. """
  136. def checkin(
  137. self,
  138. dbapi_connection: Optional[DBAPIConnection],
  139. connection_record: ConnectionPoolEntry,
  140. ) -> None:
  141. """Called when a connection returns to the pool.
  142. Note that the connection may be closed, and may be None if the
  143. connection has been invalidated. ``checkin`` will not be called
  144. for detached connections. (They do not return to the pool.)
  145. :param dbapi_connection: a DBAPI connection.
  146. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  147. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  148. the DBAPI connection.
  149. """
  150. @event._legacy_signature(
  151. "2.0",
  152. ["dbapi_connection", "connection_record"],
  153. lambda dbapi_connection, connection_record, reset_state: (
  154. dbapi_connection,
  155. connection_record,
  156. ),
  157. )
  158. def reset(
  159. self,
  160. dbapi_connection: DBAPIConnection,
  161. connection_record: ConnectionPoolEntry,
  162. reset_state: PoolResetState,
  163. ) -> None:
  164. """Called before the "reset" action occurs for a pooled connection.
  165. This event represents
  166. when the ``rollback()`` method is called on the DBAPI connection
  167. before it is returned to the pool or discarded.
  168. A custom "reset" strategy may be implemented using this event hook,
  169. which may also be combined with disabling the default "reset"
  170. behavior using the :paramref:`_pool.Pool.reset_on_return` parameter.
  171. The primary difference between the :meth:`_events.PoolEvents.reset` and
  172. :meth:`_events.PoolEvents.checkin` events are that
  173. :meth:`_events.PoolEvents.reset` is called not just for pooled
  174. connections that are being returned to the pool, but also for
  175. connections that were detached using the
  176. :meth:`_engine.Connection.detach` method as well as asyncio connections
  177. that are being discarded due to garbage collection taking place on
  178. connections before the connection was checked in.
  179. Note that the event **is not** invoked for connections that were
  180. invalidated using :meth:`_engine.Connection.invalidate`. These
  181. events may be intercepted using the :meth:`.PoolEvents.soft_invalidate`
  182. and :meth:`.PoolEvents.invalidate` event hooks, and all "connection
  183. close" events may be intercepted using :meth:`.PoolEvents.close`.
  184. The :meth:`_events.PoolEvents.reset` event is usually followed by the
  185. :meth:`_events.PoolEvents.checkin` event, except in those
  186. cases where the connection is discarded immediately after reset.
  187. :param dbapi_connection: a DBAPI connection.
  188. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  189. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  190. the DBAPI connection.
  191. :param reset_state: :class:`.PoolResetState` instance which provides
  192. information about the circumstances under which the connection
  193. is being reset.
  194. .. versionadded:: 2.0
  195. .. seealso::
  196. :ref:`pool_reset_on_return`
  197. :meth:`_events.ConnectionEvents.rollback`
  198. :meth:`_events.ConnectionEvents.commit`
  199. """
  200. def invalidate(
  201. self,
  202. dbapi_connection: DBAPIConnection,
  203. connection_record: ConnectionPoolEntry,
  204. exception: Optional[BaseException],
  205. ) -> None:
  206. """Called when a DBAPI connection is to be "invalidated".
  207. This event is called any time the
  208. :meth:`.ConnectionPoolEntry.invalidate` method is invoked, either from
  209. API usage or via "auto-invalidation", without the ``soft`` flag.
  210. The event occurs before a final attempt to call ``.close()`` on the
  211. connection occurs.
  212. :param dbapi_connection: a DBAPI connection.
  213. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  214. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  215. the DBAPI connection.
  216. :param exception: the exception object corresponding to the reason
  217. for this invalidation, if any. May be ``None``.
  218. .. seealso::
  219. :ref:`pool_connection_invalidation`
  220. """
  221. def soft_invalidate(
  222. self,
  223. dbapi_connection: DBAPIConnection,
  224. connection_record: ConnectionPoolEntry,
  225. exception: Optional[BaseException],
  226. ) -> None:
  227. """Called when a DBAPI connection is to be "soft invalidated".
  228. This event is called any time the
  229. :meth:`.ConnectionPoolEntry.invalidate`
  230. method is invoked with the ``soft`` flag.
  231. Soft invalidation refers to when the connection record that tracks
  232. this connection will force a reconnect after the current connection
  233. is checked in. It does not actively close the dbapi_connection
  234. at the point at which it is called.
  235. :param dbapi_connection: a DBAPI connection.
  236. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  237. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  238. the DBAPI connection.
  239. :param exception: the exception object corresponding to the reason
  240. for this invalidation, if any. May be ``None``.
  241. """
  242. def close(
  243. self,
  244. dbapi_connection: DBAPIConnection,
  245. connection_record: ConnectionPoolEntry,
  246. ) -> None:
  247. """Called when a DBAPI connection is closed.
  248. The event is emitted before the close occurs.
  249. The close of a connection can fail; typically this is because
  250. the connection is already closed. If the close operation fails,
  251. the connection is discarded.
  252. The :meth:`.close` event corresponds to a connection that's still
  253. associated with the pool. To intercept close events for detached
  254. connections use :meth:`.close_detached`.
  255. :param dbapi_connection: a DBAPI connection.
  256. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  257. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  258. the DBAPI connection.
  259. """
  260. def detach(
  261. self,
  262. dbapi_connection: DBAPIConnection,
  263. connection_record: ConnectionPoolEntry,
  264. ) -> None:
  265. """Called when a DBAPI connection is "detached" from a pool.
  266. This event is emitted after the detach occurs. The connection
  267. is no longer associated with the given connection record.
  268. :param dbapi_connection: a DBAPI connection.
  269. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  270. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  271. the DBAPI connection.
  272. """
  273. def close_detached(self, dbapi_connection: DBAPIConnection) -> None:
  274. """Called when a detached DBAPI connection is closed.
  275. The event is emitted before the close occurs.
  276. The close of a connection can fail; typically this is because
  277. the connection is already closed. If the close operation fails,
  278. the connection is discarded.
  279. :param dbapi_connection: a DBAPI connection.
  280. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  281. """