Upgrading

How to upgrade an existing Rails Pulse install, including the one-time route migration required when moving from any 0.3.x release to 0.4.0.

Routine upgrades

Most releases only need the upgrade generator and a migrate:

bundle update rails_pulse
rails generate rails_pulse:upgrade
rails db:migrate                  # separate Pulse database: rails db:migrate:rails_pulse
rails rails_pulse:status          # exits 1 while anything still needs action

The upgrade generator copies any new migrations into your app, compares your tables against the gem’s schema as a safety net, and appends new settings to config/initializers/rails_pulse.rb without rewriting values you already set. Review the initializer with git diff and keep or discard hunks.

rails rails_pulse:status reports the schema, pending migrations, route backfill, initializer and summary state for this install, and ends with an “Action needed” list when something is outstanding. Run it after migrating, and from a deploy check if you want a release to fail before it serves traffic.

Always restart your web and worker processes after migrating.

If the gem is deployed before its migrations

Rails Pulse checks its tables once per process at boot. When the gem is newer than the schema it is connected to, it logs one warning, pauses tracking, and answers every dashboard page with a 503 that lists the upgrade commands, instead of raising on each request. Tracking resumes on the next restart after the schema is current. config.schema_check_enabled = false turns the guard off.

Upgrading from 0.3.x to 0.4.0

This applies to every 0.3.x release (0.3.0 through 0.3.3). 0.4.0 contains a breaking schema change and a mandatory one-time data migration.

Back up your database first. The route migration is irreversible. db:rollback raises ActiveRecord::IrreversibleMigration on ChangeRailsPulseRoutesToMultiVerbModel, and routes merged by the backfill are deleted with no audit trail. Recovery from a bad upgrade is restore-from-backup only.

What changed

  • Route identity is now [controller_action, path]. GET /users (users#index) and POST /users (users#create) are separate routes, and dynamic paths are normalized at capture time (/posts/42 becomes /posts/:id).
  • rails_pulse_routes.method is dropped. The HTTP verb now lives on each request, and a route carries an http_methods array instead.
  • Every application, worker, and dashboard process must be restarted together against the migrated schema.

Single database

bundle update rails_pulse
rails generate rails_pulse:upgrade
rails db:migrate
rails rails_pulse:migrate_routes  # required
rails rails_pulse:status          # expect "OK — nothing to do."

Separate database

bundle update rails_pulse
rails generate rails_pulse:upgrade --database=separate
rails db:migrate:rails_pulse
rails rails_pulse:migrate_routes  # required
rails rails_pulse:status          # expect "OK — nothing to do."

Before migrating, add schema_dump: false to the rails_pulse entry in config/database.yml and delete db/rails_pulse_structure.sql if it exists. Without that setting Rails dumps or loads a structure file for the Pulse database and db:migrate can fail with relation already exists. The upgrade generator warns when the key is missing.

Do not run db:setup or db:prepare as a substitute for db:migrate:rails_pulse on an existing install.

Why migrate_routes is required

A schema migrate alone adds the new columns but leaves the Action column empty and leaves GET /sign_in and POST /sign_in as separate rows. rails rails_pulse:migrate_routes:

  1. Backfills controller_action from your live router, then from request history
  2. Collapses historical literal paths (/posts/42) into their parameterized form
  3. Merges routes that share the same action
  4. Adds a unique index for unrecognised paths (404s and other requests with no controller action)

The task prints what it updated, merged, and skipped. If it reports duplicate null-action paths that it could not resolve, fix those rows manually and re-run it.

Until the backfill has run, the dashboard shows a banner with the exact command, and the upgrade generator prints the same warning.

Restart everything together

A rolling restart that leaves 0.3.x processes running against the new schema will silently drop request tracking in those processes and return 500s on the routes page. Release-phase migrations (Heroku, Kamal) are fine as long as no old process survives the release. Deploy order:

  1. Deploy the new gem
  2. rails generate rails_pulse:upgrade
  3. rails db:migrate (or db:migrate:rails_pulse)
  4. rails rails_pulse:migrate_routes
  5. rails rails_pulse:status (exits 1 if anything above was skipped)
  6. Restart all processes before serving traffic

Initializer changes

The upgrade generator appends settings your initializer does not mention. In 0.4.0 that includes:

  • config.track_exceptions = false. Exception tracking is off for existing installs so nothing starts capturing on upgrade. Opt in once you have reviewed what is stored:

    config.track_exceptions = true
    config.capture_exception_params = true  # filtered via Rails' filter_parameters

    See Exception Tracking.

  • New max_table_records entries for rails_pulse_exception_occurrences, rails_pulse_exception_groups, and rails_pulse_deployments. The hash you set is now merged with the gem defaults, so a partial hash no longer removes caps for tables you did not list.

  • config.async = false if Rails.env.test?. Tracking writes now run on a background thread. Transactional tests share one database connection across threads, so the tracker writes inline there. The tracker also detects a shared connection on its own, but add the line to be explicit. Without it, PostgreSQL test suites could fail with message type 0x5a arrived from server while idle.

  • Historical comparison settings (baseline_window, comparison_window, hourly_summary_retention, regression_thresholds) are added as comments. See Advanced Configuration.

Other changes to check

  • Authentication is now on by default outside development and test, not only in production. Staging environments that previously served the dashboard unauthenticated now fall back to HTTP Basic against RAILS_PULSE_USERNAME / RAILS_PULSE_PASSWORD, and deny everything if the password is unset. Configure config.authorize before deploying. See Authentication.
  • Authentication hooks fail closed. An authentication_method that returns false without rendering or redirecting is now a 403. Predicate-style checks belong in the new config.authorize.
  • Assets are no longer registered with Sprockets. If you added rails-pulse.js or rails-pulse.css to config.assets.precompile, remove those entries. assets:precompile now runs rails_pulse:install_assets, which copies the pre-built files into public/assets so config.asset_host and CDN-only content security policies keep working.
  • Ruby 3.1 is the minimum. The gem previously declared 3.0 but could not actually install there.
  • Tagging by path. RailsPulse::Route.find_by(path:) can now return several rows. Look routes up by controller_action instead. See Tagging.
  • Charts. ECharts is now tree-shaken and the JavaScript bundle is a third of its previous size. This only matters if you have customised the dashboard’s charts.
  • Standalone dashboard authentication. rails_pulse_server no longer runs authorize or authentication_method. It uses HTTP Basic against RAILS_PULSE_USERNAME / RAILS_PULSE_PASSWORD unless you set config.standalone_authentication_method. See Standalone Dashboard.
  • Removed methods. RailsPulse.warm_metric_cache!, RailsPulse.clear_metric_cache!, and the group_by_date / group_by_hour scopes the gem previously added to every ActiveRecord::Relation are gone. Nothing in the dashboard used them; check your own code if you called them.

Troubleshooting an upgrade

Common failure modes and their fixes are listed under Troubleshooting.

Next Steps