Source code for relstorage.adapters.postgresql.stats

##############################################################################
#
# Copyright (c) 2009 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Stats implementations
"""

from __future__ import absolute_import

from ..stats import AbstractStats
from .._util import query_property

[docs] class PostgreSQLStats(AbstractStats): # Getting the COUNT(*) of tables can be very expensive # due to the need to examine rows to check their MVCC visibility. # This method only promises to get the *approximate* size, # so we use the tables the optimizer uses to get stats. # With the autovacuum daemon running, this shouldn't get *too* far # out of date. # (https://www.postgresql.org/docs/11/monitoring-stats.html#PG-STAT-ALL-TABLES-VIEW) _get_object_count_queries = ( "SELECT reltuples::bigint FROM pg_class WHERE relname = 'current_object'", "SELECT reltuples::bigint FROM pg_class WHERE relname = 'object_state'" ) _get_object_count_query = query_property('_get_object_count') _update_object_count_queries = ( # Only on PG11 can you list more than one table. 'ANALYZE current_object', 'ANALYZE object_state' ) _update_object_count_query = query_property('_update_object_count')
[docs] def get_object_count(self): """Returns the approximate number of objects in the database""" conn, cursor = self.connmanager.open_for_load() try: cursor.execute(self._get_object_count_query) # If the tables haven't been analyzed, this could be None? # Or, somehow negative? 0 seems most likely, but just in case, # we check both. # Seen on GithubActions macos/py3.7/psycopg2, sept 2022. # Not seen before. return max(cursor.fetchone()[0] or 0, 0) finally: self.connmanager.close(conn, cursor)
[docs] def get_db_size(self): """Returns the approximate size of the database in bytes""" def get_size(_conn, cursor): cursor.execute("SELECT pg_database_size(current_database())") return cursor.fetchone()[0] return self.connmanager.open_and_call(get_size)
[docs] def large_database_change(self): def analyze(_conn, cursor): # VACUUM cannot be run inside a transaction block; # ANALYZE can be. Both update pg_class.reltuples. # VACUUM needs a read table lock, meaning it can be blocked by writes # and vice-versa; ANALYZE doesn't appear to need locks. cursor.execute(self._update_object_count_query) # Depending on the number of pages this touched, the estimate # can be better or worse. self.connmanager.open_and_call(analyze)