Skip to content

feat(database): add automatic index optimizer#660

Open
distributed-nerd wants to merge 1 commit into
rinafcode:mainfrom
distributed-nerd:feat/auto-index-optimization
Open

feat(database): add automatic index optimizer#660
distributed-nerd wants to merge 1 commit into
rinafcode:mainfrom
distributed-nerd:feat/auto-index-optimization

Conversation

@distributed-nerd
Copy link
Copy Markdown

@distributed-nerd distributed-nerd commented May 27, 2026

Summary

Adds a PostgreSQL automatic index optimizer (new module src/database/index-optimization/) covering all acceptance criteria of the issue. All analysis is read-only; the only writes are explicit CREATE INDEX / DROP INDEX, both gated behind configuration flags.

Acceptance criterion Implementation
Query analysis for recommendations QueryAnalysisService finds foreign-key columns lacking a covering index (Postgres does not index FK columns automatically), scores/prioritises them by pg_stat_user_tables seq-scan activity, and surfaces slow statements from pg_stat_statements when available. GET .../recommendations, GET .../slow-queries
Automatic index creation IndexCreationService applies recommendations via CREATE INDEX CONCURRENTLY IF NOT EXISTS, caps creations per run, verifies indisvalid and drops failed concurrent builds
Index usage monitoring IndexUsageMonitorService samples pg_stat_user_indexes scan counts/sizes and classifies indexes (primary/unique/constraint-backed). GET .../usage
Stale index removal StaleIndexService drops unused, sufficiently large indexes via DROP INDEX CONCURRENTLY, never touching primary, unique or constraint-backed indexes. GET .../stale

Safety

  • IndexOptimizationService orchestrates the cycle on a weekly @Cron, inert unless INDEX_OPT_ENABLED=true.
  • Every destructive action is gated behind dry-run plus explicit INDEX_OPT_AUTO_CREATE / INDEX_OPT_AUTO_DROP_STALE flags (all default off).
  • Manual runs via POST /database/index-optimization/run?apply=true; admin-only endpoints.
  • IndexOptimizationModule + ScheduleModule.forRoot() wired into AppModule. See src/database/index-optimization/README.md.

Closes #647

Implements a PostgreSQL index optimizer that recommends, creates,
monitors and retires indexes from the catalog and pg_stat_* views.

- Query analysis for index recommendations: QueryAnalysisService finds
  foreign-key columns lacking a covering index (Postgres does not index
  FK columns automatically), scores/prioritises them by seq-scan
  activity from pg_stat_user_tables, and surfaces slow statements from
  pg_stat_statements when available.
- Automatic index creation: IndexCreationService applies recommendations
  via CREATE INDEX CONCURRENTLY IF NOT EXISTS, caps creations per run,
  verifies indisvalid and drops invalid concurrent builds.
- Index usage monitoring: IndexUsageMonitorService samples
  pg_stat_user_indexes scan counts/sizes and classifies indexes
  (primary/unique/constraint-backed).
- Stale index removal: StaleIndexService drops unused, sufficiently
  large indexes via DROP INDEX CONCURRENTLY, never touching primary,
  unique or constraint-backed indexes.
- IndexOptimizationService orchestrates the cycle on a weekly @Cron,
  inert unless INDEX_OPT_ENABLED=true; every destructive action is gated
  behind dry-run and explicit auto-create/auto-drop flags.

Exposes admin-only endpoints under /database/index-optimization and
wires the module plus ScheduleModule.forRoot() into AppModule.
@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented May 27, 2026

@distributed-nerd Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement automatic index optimization

2 participants