ankane/pghero · warning · PgHero::NotEnabled

Historical query stats not enabled

Error message

Historical query stats not enabled

What it means

PgHero raises this from PgHero::Methods::QueryStats#historical_query_stats when historical_query_stats_enabled? is false. Historical (time-ranged) query stats are an opt-in layer on top of live pg_stat_statements: they require the pghero_queries and pghero_query_stats capture tables to exist AND capture_query_stats not set to false for the database in config/pghero.yml. Without both, PgHero has no stored snapshots to aggregate with SUM(total_time)/SUM(calls) and refuses rather than returning empty data.

Source

Thrown at lib/pghero/methods/query_stats.rb:265

            SELECT NULL, #{"NULL, " if origin}NULL, NULL, SUM(total_time), NULL FROM query_stats
          )
        SQL

        binds = {limit: limit.to_i}
        binds[:user] = user if user
        binds[:query_hash] = query_hash if query_hash

        # we may be able to skip query_columns
        # in more recent versions of Postgres
        # as pg_stat_statements should be already normalized
        result = select_all(query, binds, query_columns: [:query])
        total = result.pop
        [result, total[:total_time] || 0]
      end

      def historical_query_stats(limit: nil, sort: nil, user: nil, query_hash: nil, start_at: nil, end_at: nil)
        if !historical_query_stats_enabled?
          raise NotEnabled, "Historical query stats not enabled"
        end

        limit ||= 100
        sort ||= "total_time"
        query = <<~SQL
          WITH query_stats AS (
            SELECT
              query_hash,
              "user",
              query_id,
              SUM(total_time) AS total_time,
              SUM(calls) AS calls
            FROM
              pghero_query_stats
            WHERE
              database = :id
              #{"AND captured_at >= :start_at" if start_at}
              #{"AND captured_at <= :end_at" if end_at}

View on GitHub (pinned to 7edb57986f)

Solutions

  1. Run `rails generate pghero:query_stats` followed by `rails db:migrate` to create pghero_queries and pghero_query_stats.
  2. Ensure config/pghero.yml does not set capture_query_stats: false for the database in question (removing the key defaults to enabled).
  3. Schedule `rake pghero:capture_query_stats` at least hourly (cron, whenever, sidekiq-cron) so snapshots accumulate before you query history.
  4. If the feature is intentionally off, guard callers with `PgHero.historical_query_stats_enabled?` or rescue PgHero::NotEnabled and fall back to `PgHero.query_stats` (live pg_stat_statements data).

Example fix

# before - raises PgHero::NotEnabled: Historical query stats not enabled
PgHero.historical_query_stats(start_at: 7.days.ago, end_at: 1.hour.ago)

# after
# terminal:
#   rails generate pghero:query_stats
#   rails db:migrate
# config/schedule.rb:
#   every :hour do
#     rake "pghero:capture_query_stats"
#   end
stats = PgHero.historical_query_stats(start_at: 7.days.ago, end_at: 1.hour.ago) if PgHero.historical_query_stats_enabled?
Defensive patterns

Strategy: validation

Validate before calling

return unless PgHero.historical_query_stats_enabled?
stats = PgHero.historical_query_stats(start_at: 7.days.ago, end_at: 1.hour.ago)

Type guard

# Ruby predicate - the closest thing to a type guard
def historical_stats_available?
  PgHero.historical_query_stats_enabled?
end

Try / catch

begin
  PgHero.historical_query_stats(start_at: 24.hours.ago)
rescue PgHero::NotEnabled
  Rails.logger.info("PgHero historical query stats not configured; using live stats")
  PgHero.query_stats
end

Prevention

When it happens

Trigger: Calling PgHero.historical_query_stats(start_at: ..., end_at: ...) (or the web UI queries page with a historical time range) before `rails generate pghero:query_stats && rails db:migrate` has created the capture tables; or when config/pghero.yml sets capture_query_stats: false for that database; or on an environment where the migration ran but the config disables capture.

Common situations: Fresh PgHero install where the optional migration was skipped; migration applied in development but not in staging/production; the hourly `rake pghero:capture_query_stats` cron was never scheduled so nobody noticed the feature was inactive; pghero.yml copied from another environment with capture_query_stats: false.

Related errors


AI-assisted analysis of ankane/pghero@7edb57986f (2026-08-21). Data as JSON: /api/errors/c3b79743f0cda15a. Report an issue: GitHub.