PgHero Documentation

repository·master·Indexed 27 days ago

https://github.com/ankane/pghero

A performance dashboard for PostgreSQL designed to help developers monitor and optimize database performance. PgHero can be installed as a Docker container, a Linux package (Ubuntu/Debian), or a Rails engine. It provides features for tracking historical query and space statistics, monitoring Amazon RDS and Google Cloud SQL system stats, and managing database performance thresholds via pghero.yml.

Tokens
9.7K
Snippets
25
Records
58
Agent score
94%

What's inside PgHero

  1. Configure Multiple Databases

    master

    To manage multiple databases, create a pghero.yml file with a databases key mapping names to connection URLs. Then, copy this file into the PgHero config directory.

    databases:
      primary:
        url: postgres://...
      replica:
        url: postgres://...
    cat pghero.yml | sudo pghero run sh -c "cat > config/pghero.yml"
    sudo service pghero restart
  2. Install PgHero on Debian

    master

    To install PgHero on Debian (supporting 12 Bookworm), add the Packager repository and install the package via apt-get.

    sudo apt-get -y install wget
    sudo mkdir -p /etc/apt/keyrings
    sudo wget -q -O /etc/apt/keyrings/pghero.asc https://dl.packager.io/srv/pghero/pghero/key
    echo "deb [signed-by=/etc/apt/keyrings/pghero.asc] https://dl.packager.io/srv/deb/pghero/pghero/master/debian $(. /etc/os-release && echo $VERSION_ID) main" | sudo tee /etc/apt/sources.list.d/pghero.list
    sudo apt-get update
    sudo apt-get -y install pghero
  3. Install PgHero on Ubuntu

    master

    To install PgHero on Ubuntu (supporting 22.04 Jammy and 24.04 Noble), add the Packager repository and install the package via apt-get.

    sudo apt-get -y install wget
    sudo wget -q -O /etc/apt/keyrings/pghero.asc https://dl.packager.io/srv/pghero/pghero/key
    echo "deb [signed-by=/etc/apt/keyrings/pghero.asc] https://dl.packager.io/srv/deb/pghero/pghero/master/ubuntu $(. /etc/os-release && echo $VERSION_ID) main" | sudo tee /etc/apt/sources.list.d/pghero.list
    sudo apt-get update
    sudo apt-get -y install pghero
  4. Customize PgHero with pghero.yml

    master

    You can manage multiple databases and fine-tune thresholds using a pghero.yml configuration file. To use this in Docker, create a Dockerfile that copies your config into /app/config/pghero.yml and build it.

    Example pghero.yml structure:

    databases:
      main:
        url: <%= ENV["DATABASE_URL"] %>
    
    # Configuration options:
    # long_running_query_sec: 60
    # slow_query_ms: 20
    # slow_query_calls: 100
    # total_connections_threshold: 500
    # explain: true / false / analyze
    # explain_timeout_sec: 10
    # visualize_url: https://...
    # time_zone: "Pacific Time (US & Canada)"
    FROM ankane/pghero
    
    COPY pghero.yml /app/config/pghero.yml
  5. Understand how PgHero suggests indexes

    master

    PgHero uses a multi-step heuristic process to suggest indexes for slow queries. The process follows these steps:

    1. Identify slow queries: It retrieves the most time-consuming queries from the pg_stat_statements extension.
    2. Parse queries: It uses pg_query to analyze the query structure. It specifically looks for queries targeting a single table that contain a WHERE clause using only =, IN, IS NULL, or IS NOT NULL operators, and/or an ORDER BY clause.
    3. Analyze column statistics: It uses the pg_stats view to retrieve estimates regarding the number of distinct rows and the percentage of NULL values for each column involved.
    4. Determine index order: For columns in the WHERE clause, it sorts them by highest cardinality (most unique values) to allow the database to narrow the search most efficiently. It performs row estimation as columns are added to the proposed index.
    5. Incorporate sorting: It continues the process by adding columns from the ORDER BY clause.
    6. Apply stopping criteria: To avoid adding redundant columns, the process stops once the row estimation narrows the result set down to approximately 50 rows, provided the last added columns still provide value.
  6. Set up Historical Space Stats

    master

    To track database space usage over time, follow these steps:

    1. Create the table:
    CREATE TABLE "pghero_space_stats" (
      "id" bigserial primary key,
      "database" text,
      "schema" text,
      "relation" text,
      "size" bigint,
      "captured_at" timestamp
    );
    CREATE INDEX ON "pghero_space_stats" ("database", "captured_at");
    1. Capture stats: Run this task once a day:
    docker run -ti -e DATABASE_URL=... ankane/pghero bin/rake pghero:capture_space_stats
    1. Cleanup: Remove old stats using KEEP_DAYS:
    docker run -ti -e DATABASE_URL=... ankane/pghero bin/rake pghero:clean_space_stats KEEP_DAYS=90
  7. Track Historical Space Stats

    master

    To track database space usage over time, follow these steps:

    1. Run migrations: rails generate pghero:space_stats followed by rails db:migrate.
    2. Schedule the capture task once a day:
      • Via Rake: rake pghero:capture_space_stats
      • Via Clockwork: PgHero.capture_space_stats

    To remove old stats, use rake pghero:clean_space_stats KEEP_DAYS=90 or PgHero.clean_space_stats(before: 90.days.ago).

    rails generate pghero:space_stats
    rails db:migrate
    # Schedule this once a day
    rake pghero:capture_space_stats
    
    # To clean old stats
    rake pghero:clean_space_stats KEEP_DAYS=90
  8. Track Historical Query Stats

    master

    To track query performance over time, generate the necessary migrations and schedule a task to capture stats every 5 minutes.

    1. Run migrations: rails generate pghero:query_stats followed by rails db:migrate.
    2. Schedule the capture task:
      • Via Rake: rake pghero:capture_query_stats
      • Via Clockwork: PgHero.capture_query_stats

    To prevent the stats table from growing too large, you can clean old stats using rake pghero:clean_query_stats KEEP_DAYS=14 or PgHero.clean_query_stats(before: 14.days.ago).

    By default, stats are stored in your app's database. You can change this by setting the PGHERO_STATS_DATABASE_URL environment variable.

    rails generate pghero:query_stats
    rails db:migrate
    # Schedule this every 5 minutes
    rake pghero:capture_query_stats
    
    # To clean old stats
    rake pghero:clean_query_stats KEEP_DAYS=14
  9. Set up Historical Query Stats

    master

    To track query performance over time, you must create a specific table and schedule a capture task.

    1. Create the table in your database (or a separate database):
    CREATE TABLE "pghero_query_stats" (
      "id" bigserial primary key,
      "database" text,
      "user" text,
      "query" text,
      "query_hash" bigint,
      "total_time" float,
      "calls" bigint,
      "captured_at" timestamp
    );
    CREATE INDEX ON "pghero_query_stats" ("database", "captured_at");
    1. Capture stats: Run the following command every 5 minutes. If using a different database for stats, set the PGHERO_STATS_DATABASE_URL environment variable.
    docker run -ti -e DATABASE_URL=... ankane/pghero bin/rake pghero:capture_query_stats
    1. Cleanup: To prevent the table from growing too large, run the clean task with a KEEP_DAYS parameter:
    docker run -ti -e DATABASE_URL=... ankane/pghero bin/rake pghero:clean_query_stats KEEP_DAYS=14
  10. Configure non-superuser permissions for PgHero

    master

    To avoid running PgHero as a superuser, you can use SECURITY DEFINER functions within a dedicated schema to grant non-superusers access to restricted Postgres functions.

    Follow these steps:

    1. As a superuser: Create a pghero schema and define SECURITY DEFINER functions for viewing queries, killing queries, accessing query stats, resetting stats, and viewing suggested indexes.
    2. As a superuser: Create a dedicated pghero user and grant it access to the pghero schema and necessary public sequences.
    3. As a superuser: Set the search_path and lock_timeout for the pghero user to ensure it looks in the correct schema and fails quickly on locks.
    4. As the migrations user: Ensure future sequences in the public schema are accessible to the pghero user using ALTER DEFAULT PRIVILEGES.
    -- 1. Run as superuser to setup schema and security definer functions
    CREATE SCHEMA pghero;
    
    -- view queries
    CREATE OR REPLACE FUNCTION pghero.pg_stat_activity() RETURNS SETOF pg_stat_activity AS
    $$
      SELECT * FROM pg_catalog.pg_stat_activity;
    $$ LANGUAGE sql VOLATILE SECURITY DEFINER;
    
    CREATE VIEW pghero.pg_stat_activity AS SELECT * FROM pghero.pg_stat_activity();
    
    -- kill queries
    CREATE OR REPLACE FUNCTION pghero.pg_terminate_backend(pid int) RETURNS boolean AS
    $$
      SELECT * FROM pg_catalog.pg_terminate_backend(pid);
    $$ LANGUAGE sql VOLATILE SECURITY DEFINER;
    
    -- query stats
    CREATE OR REPLACE FUNCTION pghero.pg_stat_statements() RETURNS SETOF pg_stat_statements AS
    $$
      SELECT * FROM public.pg_stat_statements;
    $$ LANGUAGE sql VOLATILE SECURITY DEFINER;
    
    CREATE VIEW pghero.pg_stat_statements AS SELECT * FROM pghero.pg_stat_statements();
    
    -- query stats reset
    CREATE OR REPLACE FUNCTION pghero.pg_stat_statements_reset(userid oid, dbid oid, queryid bigint) RETURNS void AS
    $$
      SELECT public.pg_stat_statements_reset(userid, dbid, queryid);
    $$ LANGUAGE sql VOLATILE SECURITY DEFINER;
    
    -- suggested indexes
    CREATE OR REPLACE FUNCTION pghero.pg_stats() RETURNS
    TABLE(schemaname name, tablename name, attname name, null_frac real, avg_width integer, n_distinct real) AS
    $$
      SELECT schemaname, tablename, attname, null_frac, avg_width, n_distinct FROM pg_catalog.pg_stats;
    $$ LANGUAGE sql VOLATILE SECURITY DEFINER;
    
    CREATE VIEW pghero.pg_stats AS SELECT * FROM pghero.pg_stats();
    
    -- 2. Create the pghero user
    CREATE ROLE pghero WITH LOGIN ENCRYPTED PASSWORD 'secret';
    GRANT CONNECT ON DATABASE <dbname> TO pghero;
    ALTER ROLE pghero SET search_path = pghero, pg_catalog, public;
    ALTER ROLE pghero SET lock_timeout = '1s';
    GRANT USAGE ON SCHEMA pghero TO pghero;
    GRANT SELECT ON ALL TABLES IN SCHEMA pghero TO pghero;
    
    -- 3. Grant permissions for current sequences
    GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO pghero;
    
    -- 4. Grant permissions for future sequences (Run as <migrations-user>)
    ALTER DEFAULT PRIVILEGES FOR ROLE <migrations-user> IN SCHEMA public GRANT SELECT ON SEQUENCES TO pghero;
  11. Configure and Start PgHero

    master

    Set the DATABASE_URL (using URL-encoding for special characters in credentials), configure the port and logging, and scale the web process. You can verify the server is running using curl.

    sudo pghero config:set DATABASE_URL=postgres://user:password@hostname:5432/dbname
    
    sudo pghero config:set PORT=3001
    sudo pghero config:set RAILS_LOG_TO_STDOUT=disabled
    sudo pghero scale web=1
    
    # Confirm it's running
    curl -v http://localhost:3001/
  12. Configure System Stats for Amazon RDS

    master

    To view CPU and IOPS metrics for Amazon RDS, add aws-sdk-cloudwatch to your Gemfile. You can use your application's default AWS credentials or provide specific ones via environment variables. You must also specify the database instance identifier.

    Required IAM Policy:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "cloudwatch:GetMetricStatistics",
                "Resource": "*"
            }
        ]
    }