diff --git a/Gemfile b/Gemfile index 47de8e1a5..52208846a 100644 --- a/Gemfile +++ b/Gemfile @@ -69,7 +69,9 @@ gem "omniauth-twitter", "~> 1.4" # OmniAuth strategy for Twitter gem "parallel", "~> 1.21" # Run any kind of code in parallel processes gem "patron", "~> 0.13.3" # HTTP client library based on libcurl, used with GitHub OAuth client gem "pg", "~> 1.2" # Pg is the Ruby interface to the PostgreSQL RDBMS +gem "pg_query", ">= 0.9.0" # Allows PGHero to analyze queries gem "pg_search", "~> 2.3.5" # PgSearch builds Active Record named scopes that take advantage of PostgreSQL's full text search +gem "pghero", "~> 2.8" # Dashboard for Postgres gem "puma", "~> 5.5.2" # Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server gem "pundit", "~> 2.1" # Object oriented authorization for Rails applications gem "pusher", "~> 2.0" # Ruby library for Pusher Channels HTTP API diff --git a/Gemfile.lock b/Gemfile.lock index 6adb119d0..ac65c4a13 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -334,6 +334,9 @@ GEM multi_json (>= 1.11.0) globalid (0.5.2) activesupport (>= 5.0) + google-protobuf (3.18.1) + google-protobuf (3.18.1-universal-darwin) + google-protobuf (3.18.1-x86_64-linux) guard (2.18.0) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) @@ -539,9 +542,13 @@ GEM ast (~> 2.4.1) patron (0.13.3) pg (1.2.3) + pg_query (2.1.1) + google-protobuf (>= 3.17.1) pg_search (2.3.5) activerecord (>= 5.2) activesupport (>= 5.2) + pghero (2.8.1) + activerecord (>= 5) pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) @@ -967,7 +974,9 @@ DEPENDENCIES parallel (~> 1.21) patron (~> 0.13.3) pg (~> 1.2) + pg_query (>= 0.9.0) pg_search (~> 2.3.5) + pghero (~> 2.8) pry (~> 0.14) pry-rails (~> 0.3) puma (~> 5.5.2) diff --git a/app/workers/capture_query_stats_worker.rb b/app/workers/capture_query_stats_worker.rb new file mode 100644 index 000000000..c3b7cb954 --- /dev/null +++ b/app/workers/capture_query_stats_worker.rb @@ -0,0 +1,9 @@ +class CaptureQueryStatsWorker + include Sidekiq::Worker + + def perform + return unless ENV["PG_HERO_CAPTURE_QUERY_STATS"] == "true" + + PgHero.capture_query_stats + end +end diff --git a/config/routes/admin.rb b/config/routes/admin.rb index d98fcd0f2..a80a0cf24 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -9,6 +9,7 @@ namespace :admin do { rack_protection: { except: %i[authenticity_token form_token json_csrf remote_token http_origin session_hijacking] } }) mount flipper_ui, at: "feature_flags" + mount PgHero::Engine, at: "pghero" end resources :invitations, only: %i[index new create destroy] resources :organization_memberships, only: %i[update destroy create] diff --git a/config/schedule.yml b/config/schedule.yml index 120de61df..e8c99e0fa 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -140,3 +140,7 @@ push_notifications_cleanup: description: "Cleans up Push Notifications from Redis (runs every 8 hours on the 20th minute after the hour)" cron: "20 */8 * * *" class: "PushNotifications::CleanupWorker" +capture_query_stats: + description: "Collects Postgres query stats for PGHero (runs every 5 minutes)" + cron: "*/5 * * * *" + class: "CaptureQueryStatsWorker" diff --git a/db/migrate/20211013153230_create_pghero_query_stats.rb b/db/migrate/20211013153230_create_pghero_query_stats.rb new file mode 100644 index 000000000..4348f2de7 --- /dev/null +++ b/db/migrate/20211013153230_create_pghero_query_stats.rb @@ -0,0 +1,15 @@ +class CreatePgheroQueryStats < ActiveRecord::Migration[6.1] + def change + create_table :pghero_query_stats do |t| + t.text :database + t.text :user + t.text :query + t.integer :query_hash, limit: 8 + t.float :total_time + t.integer :calls, limit: 8 + t.timestamp :captured_at + end + + add_index :pghero_query_stats, [:database, :captured_at] + end +end diff --git a/db/schema.rb b/db/schema.rb index bf4d3d93b..0a0e573ae 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -839,6 +839,17 @@ ActiveRecord::Schema.define(version: 2021_10_19_151431) do t.index ["slug"], name: "index_pages_on_slug", unique: true end + create_table "pghero_query_stats", force: :cascade do |t| + t.bigint "calls" + t.datetime "captured_at" + t.text "database" + t.text "query" + t.bigint "query_hash" + t.float "total_time" + t.text "user" + t.index ["database", "captured_at"], name: "index_pghero_query_stats_on_database_and_captured_at" + end + create_table "podcast_episode_appearances", force: :cascade do |t| t.boolean "approved", default: false, null: false t.datetime "created_at", precision: 6, null: false diff --git a/vendor/cache/google-protobuf-3.18.1-universal-darwin.gem b/vendor/cache/google-protobuf-3.18.1-universal-darwin.gem new file mode 100644 index 000000000..16de1d8bf Binary files /dev/null and b/vendor/cache/google-protobuf-3.18.1-universal-darwin.gem differ diff --git a/vendor/cache/google-protobuf-3.18.1-x86_64-linux.gem b/vendor/cache/google-protobuf-3.18.1-x86_64-linux.gem new file mode 100644 index 000000000..ad65c11a2 Binary files /dev/null and b/vendor/cache/google-protobuf-3.18.1-x86_64-linux.gem differ diff --git a/vendor/cache/google-protobuf-3.18.1.gem b/vendor/cache/google-protobuf-3.18.1.gem new file mode 100644 index 000000000..b7d1228ef Binary files /dev/null and b/vendor/cache/google-protobuf-3.18.1.gem differ diff --git a/vendor/cache/pg_query-2.1.1.gem b/vendor/cache/pg_query-2.1.1.gem new file mode 100644 index 000000000..4da22f899 Binary files /dev/null and b/vendor/cache/pg_query-2.1.1.gem differ diff --git a/vendor/cache/pghero-2.8.1.gem b/vendor/cache/pghero-2.8.1.gem new file mode 100644 index 000000000..010875c84 Binary files /dev/null and b/vendor/cache/pghero-2.8.1.gem differ