You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1021 lines
35KB

  1. # sqlalchemy/pool.py
  2. # Copyright (C) 2005-2021 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: http://www.opensource.org/licenses/mit-license.php
  7. """Base constructs for connection pools.
  8. """
  9. from collections import deque
  10. import time
  11. import weakref
  12. from .. import event
  13. from .. import exc
  14. from .. import log
  15. from .. import util
  16. reset_rollback = util.symbol("reset_rollback")
  17. reset_commit = util.symbol("reset_commit")
  18. reset_none = util.symbol("reset_none")
  19. class _ConnDialect(object):
  20. """partial implementation of :class:`.Dialect`
  21. which provides DBAPI connection methods.
  22. When a :class:`_pool.Pool` is combined with an :class:`_engine.Engine`,
  23. the :class:`_engine.Engine` replaces this with its own
  24. :class:`.Dialect`.
  25. """
  26. is_async = False
  27. def do_rollback(self, dbapi_connection):
  28. dbapi_connection.rollback()
  29. def do_commit(self, dbapi_connection):
  30. dbapi_connection.commit()
  31. def do_close(self, dbapi_connection):
  32. dbapi_connection.close()
  33. def do_ping(self, dbapi_connection):
  34. raise NotImplementedError(
  35. "The ping feature requires that a dialect is "
  36. "passed to the connection pool."
  37. )
  38. class _AsyncConnDialect(_ConnDialect):
  39. is_async = True
  40. class Pool(log.Identified):
  41. """Abstract base class for connection pools."""
  42. _dialect = _ConnDialect()
  43. def __init__(
  44. self,
  45. creator,
  46. recycle=-1,
  47. echo=None,
  48. logging_name=None,
  49. reset_on_return=True,
  50. events=None,
  51. dialect=None,
  52. pre_ping=False,
  53. _dispatch=None,
  54. ):
  55. """
  56. Construct a Pool.
  57. :param creator: a callable function that returns a DB-API
  58. connection object. The function will be called with
  59. parameters.
  60. :param recycle: If set to a value other than -1, number of
  61. seconds between connection recycling, which means upon
  62. checkout, if this timeout is surpassed the connection will be
  63. closed and replaced with a newly opened connection. Defaults to -1.
  64. :param logging_name: String identifier which will be used within
  65. the "name" field of logging records generated within the
  66. "sqlalchemy.pool" logger. Defaults to a hexstring of the object's
  67. id.
  68. :param echo: if True, the connection pool will log
  69. informational output such as when connections are invalidated
  70. as well as when connections are recycled to the default log handler,
  71. which defaults to ``sys.stdout`` for output.. If set to the string
  72. ``"debug"``, the logging will include pool checkouts and checkins.
  73. The :paramref:`_pool.Pool.echo` parameter can also be set from the
  74. :func:`_sa.create_engine` call by using the
  75. :paramref:`_sa.create_engine.echo_pool` parameter.
  76. .. seealso::
  77. :ref:`dbengine_logging` - further detail on how to configure
  78. logging.
  79. :param reset_on_return: Determine steps to take on
  80. connections as they are returned to the pool, which were
  81. not otherwise handled by a :class:`_engine.Connection`.
  82. reset_on_return can have any of these values:
  83. * ``"rollback"`` - call rollback() on the connection,
  84. to release locks and transaction resources.
  85. This is the default value. The vast majority
  86. of use cases should leave this value set.
  87. * ``True`` - same as 'rollback', this is here for
  88. backwards compatibility.
  89. * ``"commit"`` - call commit() on the connection,
  90. to release locks and transaction resources.
  91. A commit here may be desirable for databases that
  92. cache query plans if a commit is emitted,
  93. such as Microsoft SQL Server. However, this
  94. value is more dangerous than 'rollback' because
  95. any data changes present on the transaction
  96. are committed unconditionally.
  97. * ``None`` - don't do anything on the connection.
  98. This setting is only appropriate if the database / DBAPI
  99. works in pure "autocommit" mode at all times, or if the
  100. application uses the :class:`_engine.Engine` with consistent
  101. connectivity patterns. See the section
  102. :ref:`pool_reset_on_return` for more details.
  103. * ``False`` - same as None, this is here for
  104. backwards compatibility.
  105. .. seealso::
  106. :ref:`pool_reset_on_return`
  107. :param events: a list of 2-tuples, each of the form
  108. ``(callable, target)`` which will be passed to :func:`.event.listen`
  109. upon construction. Provided here so that event listeners
  110. can be assigned via :func:`_sa.create_engine` before dialect-level
  111. listeners are applied.
  112. :param dialect: a :class:`.Dialect` that will handle the job
  113. of calling rollback(), close(), or commit() on DBAPI connections.
  114. If omitted, a built-in "stub" dialect is used. Applications that
  115. make use of :func:`_sa.create_engine` should not use this parameter
  116. as it is handled by the engine creation strategy.
  117. .. versionadded:: 1.1 - ``dialect`` is now a public parameter
  118. to the :class:`_pool.Pool`.
  119. :param pre_ping: if True, the pool will emit a "ping" (typically
  120. "SELECT 1", but is dialect-specific) on the connection
  121. upon checkout, to test if the connection is alive or not. If not,
  122. the connection is transparently re-connected and upon success, all
  123. other pooled connections established prior to that timestamp are
  124. invalidated. Requires that a dialect is passed as well to
  125. interpret the disconnection error.
  126. .. versionadded:: 1.2
  127. """
  128. if logging_name:
  129. self.logging_name = self._orig_logging_name = logging_name
  130. else:
  131. self._orig_logging_name = None
  132. log.instance_logger(self, echoflag=echo)
  133. self._creator = creator
  134. self._recycle = recycle
  135. self._invalidate_time = 0
  136. self._pre_ping = pre_ping
  137. self._reset_on_return = util.symbol.parse_user_argument(
  138. reset_on_return,
  139. {
  140. reset_rollback: ["rollback", True],
  141. reset_none: ["none", None, False],
  142. reset_commit: ["commit"],
  143. },
  144. "reset_on_return",
  145. resolve_symbol_names=False,
  146. )
  147. self.echo = echo
  148. if _dispatch:
  149. self.dispatch._update(_dispatch, only_propagate=False)
  150. if dialect:
  151. self._dialect = dialect
  152. if events:
  153. for fn, target in events:
  154. event.listen(self, target, fn)
  155. @util.hybridproperty
  156. def _is_asyncio(self):
  157. return self._dialect.is_async
  158. @property
  159. def _creator(self):
  160. return self.__dict__["_creator"]
  161. @_creator.setter
  162. def _creator(self, creator):
  163. self.__dict__["_creator"] = creator
  164. self._invoke_creator = self._should_wrap_creator(creator)
  165. def _should_wrap_creator(self, creator):
  166. """Detect if creator accepts a single argument, or is sent
  167. as a legacy style no-arg function.
  168. """
  169. try:
  170. argspec = util.get_callable_argspec(self._creator, no_self=True)
  171. except TypeError:
  172. return lambda crec: creator()
  173. defaulted = argspec[3] is not None and len(argspec[3]) or 0
  174. positionals = len(argspec[0]) - defaulted
  175. # look for the exact arg signature that DefaultStrategy
  176. # sends us
  177. if (argspec[0], argspec[3]) == (["connection_record"], (None,)):
  178. return creator
  179. # or just a single positional
  180. elif positionals == 1:
  181. return creator
  182. # all other cases, just wrap and assume legacy "creator" callable
  183. # thing
  184. else:
  185. return lambda crec: creator()
  186. def _close_connection(self, connection):
  187. self.logger.debug("Closing connection %r", connection)
  188. try:
  189. self._dialect.do_close(connection)
  190. except Exception:
  191. self.logger.error(
  192. "Exception closing connection %r", connection, exc_info=True
  193. )
  194. def _create_connection(self):
  195. """Called by subclasses to create a new ConnectionRecord."""
  196. return _ConnectionRecord(self)
  197. def _invalidate(self, connection, exception=None, _checkin=True):
  198. """Mark all connections established within the generation
  199. of the given connection as invalidated.
  200. If this pool's last invalidate time is before when the given
  201. connection was created, update the timestamp til now. Otherwise,
  202. no action is performed.
  203. Connections with a start time prior to this pool's invalidation
  204. time will be recycled upon next checkout.
  205. """
  206. rec = getattr(connection, "_connection_record", None)
  207. if not rec or self._invalidate_time < rec.starttime:
  208. self._invalidate_time = time.time()
  209. if _checkin and getattr(connection, "is_valid", False):
  210. connection.invalidate(exception)
  211. def recreate(self):
  212. """Return a new :class:`_pool.Pool`, of the same class as this one
  213. and configured with identical creation arguments.
  214. This method is used in conjunction with :meth:`dispose`
  215. to close out an entire :class:`_pool.Pool` and create a new one in
  216. its place.
  217. """
  218. raise NotImplementedError()
  219. def dispose(self):
  220. """Dispose of this pool.
  221. This method leaves the possibility of checked-out connections
  222. remaining open, as it only affects connections that are
  223. idle in the pool.
  224. .. seealso::
  225. :meth:`Pool.recreate`
  226. """
  227. raise NotImplementedError()
  228. def connect(self):
  229. """Return a DBAPI connection from the pool.
  230. The connection is instrumented such that when its
  231. ``close()`` method is called, the connection will be returned to
  232. the pool.
  233. """
  234. return _ConnectionFairy._checkout(self)
  235. def _return_conn(self, record):
  236. """Given a _ConnectionRecord, return it to the :class:`_pool.Pool`.
  237. This method is called when an instrumented DBAPI connection
  238. has its ``close()`` method called.
  239. """
  240. self._do_return_conn(record)
  241. def _do_get(self):
  242. """Implementation for :meth:`get`, supplied by subclasses."""
  243. raise NotImplementedError()
  244. def _do_return_conn(self, conn):
  245. """Implementation for :meth:`return_conn`, supplied by subclasses."""
  246. raise NotImplementedError()
  247. def status(self):
  248. raise NotImplementedError()
  249. class _ConnectionRecord(object):
  250. """Internal object which maintains an individual DBAPI connection
  251. referenced by a :class:`_pool.Pool`.
  252. The :class:`._ConnectionRecord` object always exists for any particular
  253. DBAPI connection whether or not that DBAPI connection has been
  254. "checked out". This is in contrast to the :class:`._ConnectionFairy`
  255. which is only a public facade to the DBAPI connection while it is checked
  256. out.
  257. A :class:`._ConnectionRecord` may exist for a span longer than that
  258. of a single DBAPI connection. For example, if the
  259. :meth:`._ConnectionRecord.invalidate`
  260. method is called, the DBAPI connection associated with this
  261. :class:`._ConnectionRecord`
  262. will be discarded, but the :class:`._ConnectionRecord` may be used again,
  263. in which case a new DBAPI connection is produced when the
  264. :class:`_pool.Pool`
  265. next uses this record.
  266. The :class:`._ConnectionRecord` is delivered along with connection
  267. pool events, including :meth:`_events.PoolEvents.connect` and
  268. :meth:`_events.PoolEvents.checkout`, however :class:`._ConnectionRecord`
  269. still
  270. remains an internal object whose API and internals may change.
  271. .. seealso::
  272. :class:`._ConnectionFairy`
  273. """
  274. def __init__(self, pool, connect=True):
  275. self.__pool = pool
  276. if connect:
  277. self.__connect()
  278. self.finalize_callback = deque()
  279. fresh = False
  280. fairy_ref = None
  281. starttime = None
  282. connection = None
  283. """A reference to the actual DBAPI connection being tracked.
  284. May be ``None`` if this :class:`._ConnectionRecord` has been marked
  285. as invalidated; a new DBAPI connection may replace it if the owning
  286. pool calls upon this :class:`._ConnectionRecord` to reconnect.
  287. """
  288. _soft_invalidate_time = 0
  289. @util.memoized_property
  290. def info(self):
  291. """The ``.info`` dictionary associated with the DBAPI connection.
  292. This dictionary is shared among the :attr:`._ConnectionFairy.info`
  293. and :attr:`_engine.Connection.info` accessors.
  294. .. note::
  295. The lifespan of this dictionary is linked to the
  296. DBAPI connection itself, meaning that it is **discarded** each time
  297. the DBAPI connection is closed and/or invalidated. The
  298. :attr:`._ConnectionRecord.record_info` dictionary remains
  299. persistent throughout the lifespan of the
  300. :class:`._ConnectionRecord` container.
  301. """
  302. return {}
  303. @util.memoized_property
  304. def record_info(self):
  305. """An "info' dictionary associated with the connection record
  306. itself.
  307. Unlike the :attr:`._ConnectionRecord.info` dictionary, which is linked
  308. to the lifespan of the DBAPI connection, this dictionary is linked
  309. to the lifespan of the :class:`._ConnectionRecord` container itself
  310. and will remain persistent throughout the life of the
  311. :class:`._ConnectionRecord`.
  312. .. versionadded:: 1.1
  313. """
  314. return {}
  315. @classmethod
  316. def checkout(cls, pool):
  317. rec = pool._do_get()
  318. try:
  319. dbapi_connection = rec.get_connection()
  320. except Exception as err:
  321. with util.safe_reraise():
  322. rec._checkin_failed(err, _fairy_was_created=False)
  323. echo = pool._should_log_debug()
  324. fairy = _ConnectionFairy(dbapi_connection, rec, echo)
  325. rec.fairy_ref = ref = weakref.ref(
  326. fairy,
  327. lambda ref: _finalize_fairy
  328. and _finalize_fairy(None, rec, pool, ref, echo, True),
  329. )
  330. _strong_ref_connection_records[ref] = rec
  331. if echo:
  332. pool.logger.debug(
  333. "Connection %r checked out from pool", dbapi_connection
  334. )
  335. return fairy
  336. def _checkin_failed(self, err, _fairy_was_created=True):
  337. self.invalidate(e=err)
  338. self.checkin(
  339. _fairy_was_created=_fairy_was_created,
  340. )
  341. def checkin(self, _fairy_was_created=True):
  342. if self.fairy_ref is None and _fairy_was_created:
  343. # _fairy_was_created is False for the initial get connection phase;
  344. # meaning there was no _ConnectionFairy and we must unconditionally
  345. # do a checkin.
  346. #
  347. # otherwise, if fairy_was_created==True, if fairy_ref is None here
  348. # that means we were checked in already, so this looks like
  349. # a double checkin.
  350. util.warn("Double checkin attempted on %s" % self)
  351. return
  352. self.fairy_ref = None
  353. connection = self.connection
  354. pool = self.__pool
  355. while self.finalize_callback:
  356. finalizer = self.finalize_callback.pop()
  357. finalizer(connection)
  358. if pool.dispatch.checkin:
  359. pool.dispatch.checkin(connection, self)
  360. pool._return_conn(self)
  361. @property
  362. def in_use(self):
  363. return self.fairy_ref is not None
  364. @property
  365. def last_connect_time(self):
  366. return self.starttime
  367. def close(self):
  368. if self.connection is not None:
  369. self.__close()
  370. def invalidate(self, e=None, soft=False):
  371. """Invalidate the DBAPI connection held by this :class:`._ConnectionRecord`.
  372. This method is called for all connection invalidations, including
  373. when the :meth:`._ConnectionFairy.invalidate` or
  374. :meth:`_engine.Connection.invalidate` methods are called,
  375. as well as when any
  376. so-called "automatic invalidation" condition occurs.
  377. :param e: an exception object indicating a reason for the invalidation.
  378. :param soft: if True, the connection isn't closed; instead, this
  379. connection will be recycled on next checkout.
  380. .. versionadded:: 1.0.3
  381. .. seealso::
  382. :ref:`pool_connection_invalidation`
  383. """
  384. # already invalidated
  385. if self.connection is None:
  386. return
  387. if soft:
  388. self.__pool.dispatch.soft_invalidate(self.connection, self, e)
  389. else:
  390. self.__pool.dispatch.invalidate(self.connection, self, e)
  391. if e is not None:
  392. self.__pool.logger.info(
  393. "%sInvalidate connection %r (reason: %s:%s)",
  394. "Soft " if soft else "",
  395. self.connection,
  396. e.__class__.__name__,
  397. e,
  398. )
  399. else:
  400. self.__pool.logger.info(
  401. "%sInvalidate connection %r",
  402. "Soft " if soft else "",
  403. self.connection,
  404. )
  405. if soft:
  406. self._soft_invalidate_time = time.time()
  407. else:
  408. self.__close()
  409. self.connection = None
  410. def get_connection(self):
  411. recycle = False
  412. # NOTE: the various comparisons here are assuming that measurable time
  413. # passes between these state changes. however, time.time() is not
  414. # guaranteed to have sub-second precision. comparisons of
  415. # "invalidation time" to "starttime" should perhaps use >= so that the
  416. # state change can take place assuming no measurable time has passed,
  417. # however this does not guarantee correct behavior here as if time
  418. # continues to not pass, it will try to reconnect repeatedly until
  419. # these timestamps diverge, so in that sense using > is safer. Per
  420. # https://stackoverflow.com/a/1938096/34549, Windows time.time() may be
  421. # within 16 milliseconds accuracy, so unit tests for connection
  422. # invalidation need a sleep of at least this long between initial start
  423. # time and invalidation for the logic below to work reliably.
  424. if self.connection is None:
  425. self.info.clear()
  426. self.__connect()
  427. elif (
  428. self.__pool._recycle > -1
  429. and time.time() - self.starttime > self.__pool._recycle
  430. ):
  431. self.__pool.logger.info(
  432. "Connection %r exceeded timeout; recycling", self.connection
  433. )
  434. recycle = True
  435. elif self.__pool._invalidate_time > self.starttime:
  436. self.__pool.logger.info(
  437. "Connection %r invalidated due to pool invalidation; "
  438. + "recycling",
  439. self.connection,
  440. )
  441. recycle = True
  442. elif self._soft_invalidate_time > self.starttime:
  443. self.__pool.logger.info(
  444. "Connection %r invalidated due to local soft invalidation; "
  445. + "recycling",
  446. self.connection,
  447. )
  448. recycle = True
  449. if recycle:
  450. self.__close()
  451. self.info.clear()
  452. self.__connect()
  453. return self.connection
  454. def _is_hard_or_soft_invalidated(self):
  455. return (
  456. self.connection is None
  457. or self.__pool._invalidate_time > self.starttime
  458. or (self._soft_invalidate_time > self.starttime)
  459. )
  460. def __close(self):
  461. self.finalize_callback.clear()
  462. if self.__pool.dispatch.close:
  463. self.__pool.dispatch.close(self.connection, self)
  464. self.__pool._close_connection(self.connection)
  465. self.connection = None
  466. def __connect(self):
  467. pool = self.__pool
  468. # ensure any existing connection is removed, so that if
  469. # creator fails, this attribute stays None
  470. self.connection = None
  471. try:
  472. self.starttime = time.time()
  473. connection = pool._invoke_creator(self)
  474. pool.logger.debug("Created new connection %r", connection)
  475. self.connection = connection
  476. self.fresh = True
  477. except Exception as e:
  478. with util.safe_reraise():
  479. pool.logger.debug("Error on connect(): %s", e)
  480. else:
  481. # in SQLAlchemy 1.4 the first_connect event is not used by
  482. # the engine, so this will usually not be set
  483. if pool.dispatch.first_connect:
  484. pool.dispatch.first_connect.for_modify(
  485. pool.dispatch
  486. ).exec_once_unless_exception(self.connection, self)
  487. # init of the dialect now takes place within the connect
  488. # event, so ensure a mutex is used on the first run
  489. pool.dispatch.connect.for_modify(
  490. pool.dispatch
  491. )._exec_w_sync_on_first_run(self.connection, self)
  492. def _finalize_fairy(
  493. connection,
  494. connection_record,
  495. pool,
  496. ref, # this is None when called directly, not by the gc
  497. echo,
  498. reset=True,
  499. fairy=None,
  500. ):
  501. """Cleanup for a :class:`._ConnectionFairy` whether or not it's already
  502. been garbage collected.
  503. When using an async dialect no IO can happen here (without using
  504. a dedicated thread), since this is called outside the greenlet
  505. context and with an already running loop. In this case function
  506. will only log a message and raise a warning.
  507. """
  508. if ref:
  509. _strong_ref_connection_records.pop(ref, None)
  510. elif fairy:
  511. _strong_ref_connection_records.pop(weakref.ref(fairy), None)
  512. if ref is not None:
  513. if connection_record.fairy_ref is not ref:
  514. return
  515. assert connection is None
  516. connection = connection_record.connection
  517. # null pool is not _is_asyncio but can be used also with async dialects
  518. dont_restore_gced = pool._dialect.is_async
  519. if dont_restore_gced:
  520. detach = not connection_record or ref
  521. can_manipulate_connection = not ref
  522. else:
  523. detach = not connection_record
  524. can_manipulate_connection = True
  525. if connection is not None:
  526. if connection_record and echo:
  527. pool.logger.debug(
  528. "Connection %r being returned to pool%s",
  529. connection,
  530. ", transaction state was already reset by caller"
  531. if not reset
  532. else "",
  533. )
  534. try:
  535. fairy = fairy or _ConnectionFairy(
  536. connection, connection_record, echo
  537. )
  538. assert fairy.connection is connection
  539. if reset and can_manipulate_connection:
  540. fairy._reset(pool)
  541. if detach:
  542. if connection_record:
  543. fairy._pool = pool
  544. fairy.detach()
  545. if can_manipulate_connection:
  546. if pool.dispatch.close_detached:
  547. pool.dispatch.close_detached(connection)
  548. pool._close_connection(connection)
  549. else:
  550. message = (
  551. "The garbage collector is trying to clean up "
  552. "connection %r. This feature is unsupported on async "
  553. "dbapi, since no IO can be performed at this stage to "
  554. "reset the connection. Please close out all "
  555. "connections when they are no longer used, calling "
  556. "``close()`` or using a context manager to "
  557. "manage their lifetime."
  558. ) % connection
  559. pool.logger.error(message)
  560. util.warn(message)
  561. except BaseException as e:
  562. pool.logger.error(
  563. "Exception during reset or similar", exc_info=True
  564. )
  565. if connection_record:
  566. connection_record.invalidate(e=e)
  567. if not isinstance(e, Exception):
  568. raise
  569. if connection_record and connection_record.fairy_ref is not None:
  570. connection_record.checkin()
  571. # a dictionary of the _ConnectionFairy weakrefs to _ConnectionRecord, so that
  572. # GC under pypy will call ConnectionFairy finalizers. linked directly to the
  573. # weakref that will empty itself when collected so that it should not create
  574. # any unmanaged memory references.
  575. _strong_ref_connection_records = {}
  576. class _ConnectionFairy(object):
  577. """Proxies a DBAPI connection and provides return-on-dereference
  578. support.
  579. This is an internal object used by the :class:`_pool.Pool` implementation
  580. to provide context management to a DBAPI connection delivered by
  581. that :class:`_pool.Pool`.
  582. The name "fairy" is inspired by the fact that the
  583. :class:`._ConnectionFairy` object's lifespan is transitory, as it lasts
  584. only for the length of a specific DBAPI connection being checked out from
  585. the pool, and additionally that as a transparent proxy, it is mostly
  586. invisible.
  587. .. seealso::
  588. :class:`._ConnectionRecord`
  589. """
  590. def __init__(self, dbapi_connection, connection_record, echo):
  591. self.connection = dbapi_connection
  592. self._connection_record = connection_record
  593. self._echo = echo
  594. connection = None
  595. """A reference to the actual DBAPI connection being tracked."""
  596. _connection_record = None
  597. """A reference to the :class:`._ConnectionRecord` object associated
  598. with the DBAPI connection.
  599. This is currently an internal accessor which is subject to change.
  600. """
  601. @classmethod
  602. def _checkout(cls, pool, threadconns=None, fairy=None):
  603. if not fairy:
  604. fairy = _ConnectionRecord.checkout(pool)
  605. fairy._pool = pool
  606. fairy._counter = 0
  607. if threadconns is not None:
  608. threadconns.current = weakref.ref(fairy)
  609. if fairy.connection is None:
  610. raise exc.InvalidRequestError("This connection is closed")
  611. fairy._counter += 1
  612. if (
  613. not pool.dispatch.checkout and not pool._pre_ping
  614. ) or fairy._counter != 1:
  615. return fairy
  616. # Pool listeners can trigger a reconnection on checkout, as well
  617. # as the pre-pinger.
  618. # there are three attempts made here, but note that if the database
  619. # is not accessible from a connection standpoint, those won't proceed
  620. # here.
  621. attempts = 2
  622. while attempts > 0:
  623. connection_is_fresh = fairy._connection_record.fresh
  624. fairy._connection_record.fresh = False
  625. try:
  626. if pool._pre_ping:
  627. if not connection_is_fresh:
  628. if fairy._echo:
  629. pool.logger.debug(
  630. "Pool pre-ping on connection %s",
  631. fairy.connection,
  632. )
  633. result = pool._dialect.do_ping(fairy.connection)
  634. if not result:
  635. if fairy._echo:
  636. pool.logger.debug(
  637. "Pool pre-ping on connection %s failed, "
  638. "will invalidate pool",
  639. fairy.connection,
  640. )
  641. raise exc.InvalidatePoolError()
  642. elif fairy._echo:
  643. pool.logger.debug(
  644. "Connection %s is fresh, skipping pre-ping",
  645. fairy.connection,
  646. )
  647. pool.dispatch.checkout(
  648. fairy.connection, fairy._connection_record, fairy
  649. )
  650. return fairy
  651. except exc.DisconnectionError as e:
  652. if e.invalidate_pool:
  653. pool.logger.info(
  654. "Disconnection detected on checkout, "
  655. "invalidating all pooled connections prior to "
  656. "current timestamp (reason: %r)",
  657. e,
  658. )
  659. fairy._connection_record.invalidate(e)
  660. pool._invalidate(fairy, e, _checkin=False)
  661. else:
  662. pool.logger.info(
  663. "Disconnection detected on checkout, "
  664. "invalidating individual connection %s (reason: %r)",
  665. fairy.connection,
  666. e,
  667. )
  668. fairy._connection_record.invalidate(e)
  669. try:
  670. fairy.connection = (
  671. fairy._connection_record.get_connection()
  672. )
  673. except Exception as err:
  674. with util.safe_reraise():
  675. fairy._connection_record._checkin_failed(
  676. err,
  677. _fairy_was_created=True,
  678. )
  679. # prevent _ConnectionFairy from being carried
  680. # in the stack trace. Do this after the
  681. # connection record has been checked in, so that
  682. # if the del triggers a finalize fairy, it won't
  683. # try to checkin a second time.
  684. del fairy
  685. attempts -= 1
  686. pool.logger.info("Reconnection attempts exhausted on checkout")
  687. fairy.invalidate()
  688. raise exc.InvalidRequestError("This connection is closed")
  689. def _checkout_existing(self):
  690. return _ConnectionFairy._checkout(self._pool, fairy=self)
  691. def _checkin(self, reset=True):
  692. _finalize_fairy(
  693. self.connection,
  694. self._connection_record,
  695. self._pool,
  696. None,
  697. self._echo,
  698. reset=reset,
  699. fairy=self,
  700. )
  701. self.connection = None
  702. self._connection_record = None
  703. _close = _checkin
  704. def _reset(self, pool):
  705. if pool.dispatch.reset:
  706. pool.dispatch.reset(self, self._connection_record)
  707. if pool._reset_on_return is reset_rollback:
  708. if self._echo:
  709. pool.logger.debug(
  710. "Connection %s rollback-on-return", self.connection
  711. )
  712. pool._dialect.do_rollback(self)
  713. elif pool._reset_on_return is reset_commit:
  714. if self._echo:
  715. pool.logger.debug(
  716. "Connection %s commit-on-return",
  717. self.connection,
  718. )
  719. pool._dialect.do_commit(self)
  720. @property
  721. def _logger(self):
  722. return self._pool.logger
  723. @property
  724. def is_valid(self):
  725. """Return True if this :class:`._ConnectionFairy` still refers
  726. to an active DBAPI connection."""
  727. return self.connection is not None
  728. @util.memoized_property
  729. def info(self):
  730. """Info dictionary associated with the underlying DBAPI connection
  731. referred to by this :class:`.ConnectionFairy`, allowing user-defined
  732. data to be associated with the connection.
  733. The data here will follow along with the DBAPI connection including
  734. after it is returned to the connection pool and used again
  735. in subsequent instances of :class:`._ConnectionFairy`. It is shared
  736. with the :attr:`._ConnectionRecord.info` and
  737. :attr:`_engine.Connection.info`
  738. accessors.
  739. The dictionary associated with a particular DBAPI connection is
  740. discarded when the connection itself is discarded.
  741. """
  742. return self._connection_record.info
  743. @property
  744. def record_info(self):
  745. """Info dictionary associated with the :class:`._ConnectionRecord
  746. container referred to by this :class:`.ConnectionFairy`.
  747. Unlike the :attr:`._ConnectionFairy.info` dictionary, the lifespan
  748. of this dictionary is persistent across connections that are
  749. disconnected and/or invalidated within the lifespan of a
  750. :class:`._ConnectionRecord`.
  751. .. versionadded:: 1.1
  752. """
  753. if self._connection_record:
  754. return self._connection_record.record_info
  755. else:
  756. return None
  757. def invalidate(self, e=None, soft=False):
  758. """Mark this connection as invalidated.
  759. This method can be called directly, and is also called as a result
  760. of the :meth:`_engine.Connection.invalidate` method. When invoked,
  761. the DBAPI connection is immediately closed and discarded from
  762. further use by the pool. The invalidation mechanism proceeds
  763. via the :meth:`._ConnectionRecord.invalidate` internal method.
  764. :param e: an exception object indicating a reason for the invalidation.
  765. :param soft: if True, the connection isn't closed; instead, this
  766. connection will be recycled on next checkout.
  767. .. versionadded:: 1.0.3
  768. .. seealso::
  769. :ref:`pool_connection_invalidation`
  770. """
  771. if self.connection is None:
  772. util.warn("Can't invalidate an already-closed connection.")
  773. return
  774. if self._connection_record:
  775. self._connection_record.invalidate(e=e, soft=soft)
  776. if not soft:
  777. self.connection = None
  778. self._checkin()
  779. def cursor(self, *args, **kwargs):
  780. """Return a new DBAPI cursor for the underlying connection.
  781. This method is a proxy for the ``connection.cursor()`` DBAPI
  782. method.
  783. """
  784. return self.connection.cursor(*args, **kwargs)
  785. def __getattr__(self, key):
  786. return getattr(self.connection, key)
  787. def detach(self):
  788. """Separate this connection from its Pool.
  789. This means that the connection will no longer be returned to the
  790. pool when closed, and will instead be literally closed. The
  791. containing ConnectionRecord is separated from the DB-API connection,
  792. and will create a new connection when next used.
  793. Note that any overall connection limiting constraints imposed by a
  794. Pool implementation may be violated after a detach, as the detached
  795. connection is removed from the pool's knowledge and control.
  796. """
  797. if self._connection_record is not None:
  798. rec = self._connection_record
  799. rec.fairy_ref = None
  800. rec.connection = None
  801. # TODO: should this be _return_conn?
  802. self._pool._do_return_conn(self._connection_record)
  803. self.info = self.info.copy()
  804. self._connection_record = None
  805. if self._pool.dispatch.detach:
  806. self._pool.dispatch.detach(self.connection, rec)
  807. def close(self):
  808. self._counter -= 1
  809. if self._counter == 0:
  810. self._checkin()
  811. def _close_no_reset(self):
  812. self._counter -= 1
  813. if self._counter == 0:
  814. self._checkin(reset=False)