Rake Tasks

The engine ships a set of Rake tasks for creating, refreshing, and deleting PostgreSQL materialized views. Tasks log via Rails.logger, ask for confirmation (unless skipped), and enqueue background jobs through the Adapter.

Adapter call (used under the hood):

MatViews::Jobs::Adapter.enqueue(job_class, queue: MatViews.configuration.job_queue, args: [...])

The adapter does not guess a backend; it uses whatever you configured (ActiveJob, Sidekiq, Resque).


Available tasks

Create

  • mat_views:create_by_name[view_name, force, --yes]
  • mat_views:create_by_id[definition_id, force, --yes]
  • mat_views:create_all[force, --yes]

Refresh

  • mat_views:refresh_by_name[view_name, row_count_strategy, --yes]
  • mat_views:refresh_by_id[definition_id, row_count_strategy, --yes]
  • mat_views:refresh_all[row_count_strategy, --yes]

row_count_strategy can be:

  • estimated (default; fast, uses reltuples)
  • exact (uses COUNT(*))
  • blank to skip counting (if you pass nothing)

Delete

  • mat_views:delete_by_name[view_name, cascade, --yes]
  • mat_views:delete_by_id[definition_id, cascade, --yes]
  • mat_views:delete_all[cascade, --yes]

cascade is boolean-ish:

  • false (default) β†’ RESTRICT (fails if dependents exist)
  • true β†’ CASCADE

Arguments & env overrides

Tasks accept positional args and optional env overrides:

Purpose Positional arg Env var (override) Accepted values
Skip confirmation --yes (3rd arg) YES=1 --yes, yes, y, 1
Force create force FORCE=true true/false/1/0/yes/no
Row count strategy row_count_strategy ROW_COUNT_STRATEGY=... estimated, exact, (blank)
Cascade delete cascade CASCADE=true (if implemented in helpers) true/false/1/0/yes/no

Boolean-ish parsing accepts: 1, true, yes, y, --yes (case-insensitive). If you pass both arg and env, arg wins.


Confirmation behavior

  • Tasks log a context line and prompt: Proceed? [y/N]:
  • Input y / Y / yes β†’ proceed
  • Empty line or EOF (Ctrl+D) β†’ abort with Aborted.
  • To skip the prompt entirely, pass --yes arg or YES=1.

All log output uses Rails.logger. In development, ensure your logger is visible (e.g., to STDOUT).


Name resolution & safety

  • view_name may be either:
    • the definition name (unqualified, e.g., mv_user_activity), or
    • a schema-qualified materialized view (e.g., public.mv_user_activity).
  • If you pass a schema-qualified name that physically exists but there’s no definition with that name, the task raises a helpful error (prevents operating on orphaned MVs silently).
  • Definition names must be simple identifiers (no dots). Schema is resolved from the DB search_path at runtime by services.

Examples

Shell tip: for Zsh/Bash, quote the task invocation when passing commas.

Create

# Create by name (no force), skip confirm
bin/rake 'mat_views:create_by_name[mv_user_activity,,--yes]'

# Create by id with force=true, skip confirm
bin/rake 'mat_views:create_by_id[42,true,--yes]'

# Create all with force=false (default), skip confirm
bin/rake 'mat_views:create_all[,--yes]'

# Using env overrides: YES=1, FORCE=true
YES=1 FORCE=true bin/rake 'mat_views:create_by_name[mv_user_activity]'

Refresh

# Refresh by name with default row_count_strategy (:estimated), skip confirm
bin/rake 'mat_views:refresh_by_name[mv_user_activity,,--yes]'

# Refresh by id with exact row count, skip confirm
bin/rake 'mat_views:refresh_by_id[42,exact,--yes]'

# Refresh all with no row count at all (blank arg), skip confirm
bin/rake 'mat_views:refresh_all[,--yes]'

# Using env override for strategy
YES=1 ROW_COUNT_STRATEGY=exact bin/rake 'mat_views:refresh_by_name[mv_user_activity]'

Delete

# Delete by name with cascade=false (default), skip confirm
bin/rake 'mat_views:delete_by_name[mv_user_activity,,--yes]'

# Delete by id with cascade=true, skip confirm
bin/rake 'mat_views:delete_by_id[42,true,--yes]'

# Delete all with cascade=false (default), skip confirm
bin/rake 'mat_views:delete_all[,--yes]'

# Using env override for cascade
YES=1 CASCADE=true bin/rake 'mat_views:delete_by_name[mv_user_activity]'

Schema-qualified name

# Will check for a corresponding definition; if none exists but the MV exists β†’ raises
YES=1 bin/rake 'mat_views:refresh_by_name[public.mv_user_activity]'

What gets enqueued

Each task enqueues one job per definition:

  • Create β†’ MatViews::CreateViewJob with args [definition_id, force]
  • Refresh β†’ MatViews::RefreshViewJob with args [definition_id, row_count_strategy]
  • Delete β†’ MatViews::DeleteViewJob with args [definition_id, cascade]

Queue is taken from MatViews.configuration.job_queue (default :default unless you changed it).


Output & logging

  • Tasks use Rails.logger.info (no puts), e.g.:

    [mat_views] Enqueued RefreshViewJob for definition #42 (mv_user_activity), row_count_strategy=estimated
    
  • If no definitions are found (e.g., *_all), they log:

    [mat_views] No mat view definitions found.
    

Error cases you may see

  • view_name is required β€” missing/blank name.
  • No MatViews::MatViewDefinition found for "foo" β€” unknown definition.
  • Materialized view public.foo exists, but no MatViews::MatViewDefinition record was found for name="foo" β€” schema-qualified MV exists but no definition; intentional guardrail.
  • definition_id is required β€” missing/blank id.
  • Abort on confirm β€” pressing Enter or EOF at the prompt without --yes / YES=1.

Under the hood

The tasks are implemented in lib/tasks/mat_views_tasks.rake and rely on MatViews::Tasks::Helpers:

  • confirm!(message, skip:) β€” logs and prompts (or skips with --yes / YES=1)
  • find_definition_by_name! β€” resolves names (definition vs schema-qualified MV)
  • parse_force?, parse_row_count_strategy, parse_cascade? β€” boolean/enum parsing
  • enqueue_create!, enqueue_refresh!, enqueue_delete! β€” adapter calls

Troubleshooting

  • No logs appear: ensure your Rails logger outputs to STDOUT/stderr in your environment.
  • Command parsing in shell: quote the whole task invocation when passing commas or blank args.
  • Background jobs don’t run: verify your queue backend is configured and running; in development, consider config.active_job.queue_adapter = :inline for immediate execution (jobs still record run rows).
  • Concurrent refresh fails: ensure your MV has a unique index that covers all rows.

See also