Handle insufficient privilege in vacuum activity query

What

Fixes the Admin area Database Diagnostics page (/admin/database_diagnostics), where the "Database information" panel showed only the generic banner "Failed to gather information for database: main" on gitlab.com.

Introduced by !239928 (merged), which added the vacuum activity query with the pg_stat_activity join.

Why

The application database role on gitlab.com is not a superuser and is not a member of pg_monitor. The in-progress vacuum query in lib/gitlab/database/database_information.rb joins pg_stat_activity to read the vacuum type, anti-wraparound status, and running time. Postgres raises PG::InsufficientPrivilege for that join under this role. The panel's single catch-all rescue turned that error into a failure for the entire database payload, so no information was shown at all, not even the parts the role can read.

How

The vacuum query now catches PG::InsufficientPrivilege errors raised as ActiveRecord::StatementInvalid and retries without the pg_stat_activity join. The progress data from pg_stat_progress_vacuum, which the role can read, is still returned. The three activity-derived fields (type, running time, anti-wraparound status) are set to null in that case.

The payload now includes a vacuum_activity_available boolean. When it is false, the Vacuum information panel shows a warning banner explaining that the role cannot read pg_stat_activity and that granting pg_monitor membership restores the full details. Progress metrics are still shown in that state.

Any other error is unaffected and still surfaces as before.

No database migration and no feature flag are needed.

How to test locally

Automated specs

bundle exec rspec spec/lib/gitlab/database/database_information_spec.rb
yarn jest spec/frontend/admin/database_diagnostics/components/db_vacuum_section_spec.js \
  spec/frontend/admin/database_diagnostics/components/vacuum_information_app_spec.js

Happy path (UI)

Visit /admin/database_diagnostics as an admin. The "Database information" and "Vacuum information" panels should render. To see a live row, start a manual vacuum in another session first, for example VACUUM (VERBOSE) some_large_table;.

image

Reproducing the warning banner (UI)

GDK's database role is a superuser, which bypasses table ACLs, so the error only appears when the app runs as a non-superuser role that is denied the view. This is why it surfaced on gitlab.com and not in a default GDK.

Do not grant the new role membership in the owner role. A member inherits the owner's privileges, including its access to pg_stat_activity, so the restriction would not take effect. Grant the app privileges explicitly instead.

  1. Create the role and deny the view (as the GDK superuser, via gdk psql -d gitlabhq_development). Adjust the schema list if your instance has other schemas holding tables:

    CREATE ROLE diag_limited LOGIN PASSWORD 'diag' NOSUPERUSER;
    GRANT CONNECT, TEMPORARY ON DATABASE gitlabhq_development TO diag_limited;
    GRANT USAGE ON SCHEMA public, gitlab_partitions_dynamic, gitlab_partitions_static TO diag_limited;
    GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public, gitlab_partitions_dynamic, gitlab_partitions_static TO diag_limited;
    GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public, gitlab_partitions_dynamic, gitlab_partitions_static TO diag_limited;
    REVOKE SELECT ON pg_catalog.pg_stat_activity FROM PUBLIC;
  2. Confirm the role is restricted but can still run the other queries:

    SELECT has_table_privilege('diag_limited', 'pg_catalog.pg_stat_activity', 'SELECT'); -- f
    SET ROLE diag_limited;
    SELECT count(*) FROM pg_stat_progress_vacuum;                                        -- ok
    SELECT count(*) FROM pg_stat_progress_vacuum v
      LEFT JOIN pg_stat_activity a ON a.pid = v.pid;    -- ERROR: permission denied for view pg_stat_activity
    RESET ROLE;
  3. Point the main connection at the role in config/database.yml, under development.main, add:

    username: diag_limited
    password: diag

    Only main needs this; ci and sec can stay on the default role. The default GDK pg_hba.conf trusts local socket connections, so the password may be ignored.

  4. Run gdk restart rails, then visit /admin/database_diagnostics. The Vacuum information panel shows the warning banner. With no vacuum running you also see "No vacuum operations are currently running"; the Database information panel renders normally.

  5. Restore state when done: revert the config/database.yml change, then:

    GRANT SELECT ON pg_catalog.pg_stat_activity TO PUBLIC;
    DROP OWNED BY diag_limited;
    DROP ROLE diag_limited;

    Run gdk restart rails again to reconnect as the original role.

image

Testing

  • Added backend specs in spec/lib/gitlab/database/database_information_spec.rb covering the insufficient privilege retry path.
  • Added frontend specs in db_vacuum_section_spec.js and vacuum_information_app_spec.js covering the warning banner.
  • RuboCop and ESLint are clean.
Edited by Stan Hu

Merge request reports

Loading
Loading