Files
orion/.performance-rules/database.yaml
Samir Boulahtit 26b3dc9e3b feat: add unified code quality dashboard with multiple validators
- Add validator_type field to scans and violations (architecture,
  security, performance)
- Create security validator with SEC-xxx rules
- Create performance validator with PERF-xxx rules
- Add base validator class for shared functionality
- Add validate_all.py script to run all validators
- Update code quality service with validator type filtering
- Add validator type tabs to dashboard UI
- Add validator type filter to violations list
- Update stats response with per-validator breakdown
- Add security and performance rules documentation
- Add chat-bubble icons to icon library

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:57:47 +01:00

224 lines
7.3 KiB
YAML

# Database Performance Rules
# ==========================
database_rules:
- id: "PERF-001"
name: "N+1 query detection"
severity: warning
description: |
Accessing relationships in loops causes N+1 queries.
For each item in a list, a separate query is executed.
Solutions:
- joinedload(): Eager load with JOIN
- selectinload(): Eager load with IN clause
- subqueryload(): Eager load with subquery
file_pattern: "**/service*.py|**/api/**/*.py"
anti_patterns:
- 'for\s+\w+\s+in\s+\w+\.all\(\):\s*\n[^}]*\.\w+\.\w+'
suggested_patterns:
- "joinedload|selectinload|subqueryload"
example_bad: |
orders = db.query(Order).all()
for order in orders:
customer_name = order.customer.name # N+1 query!
example_good: |
orders = db.query(Order).options(
joinedload(Order.customer)
).all()
for order in orders:
customer_name = order.customer.name # Already loaded
- id: "PERF-002"
name: "Eager loading for known relationships"
severity: info
description: |
When you always need related data, use eager loading
to reduce the number of database round trips.
file_pattern: "**/service*.py"
suggested_patterns:
- "joinedload|selectinload|subqueryload"
- id: "PERF-003"
name: "Query result limiting"
severity: warning
description: |
All list queries should have pagination or limits.
Unbounded queries can cause memory issues and slow responses.
file_pattern: "**/service*.py|**/api/**/*.py"
anti_patterns:
- '\\.all\\(\\)(?![^\\n]*limit|[^\\n]*\\[:)'
exclude_patterns:
- "# noqa: PERF-003"
- "# bounded query"
- ".filter("
suggested_patterns:
- "limit|offset|skip|paginate"
example_bad: |
all_products = db.query(Product).all()
example_good: |
products = db.query(Product).limit(100).all()
# Or with pagination
products = db.query(Product).offset(skip).limit(limit).all()
- id: "PERF-004"
name: "Index usage for filtered columns"
severity: info
description: |
Columns frequently used in WHERE clauses should have indexes:
- Foreign keys (vendor_id, customer_id)
- Status fields
- Date fields used for filtering
- Boolean flags used for filtering
file_pattern: "**/models/database/*.py"
suggested_patterns:
- "index=True|Index\\("
- id: "PERF-005"
name: "Select only needed columns"
severity: info
description: |
For large tables, select only the columns you need.
Use .with_entities() or load_only() to reduce data transfer.
file_pattern: "**/service*.py"
suggested_patterns:
- "with_entities|load_only|defer"
example_good: |
# Only load id and name columns
products = db.query(Product).options(
load_only(Product.id, Product.name)
).all()
- id: "PERF-006"
name: "Bulk operations for multiple records"
severity: warning
description: |
Use bulk operations instead of individual operations in loops:
- bulk_insert_mappings() for inserts
- bulk_update_mappings() for updates
- add_all() for ORM inserts
file_pattern: "**/service*.py"
anti_patterns:
- 'for\\s+\\w+\\s+in\\s+\\w+:\\s*\\n[^}]*db\\.add\\s*\\('
- 'for\\s+\\w+\\s+in\\s+\\w+:\\s*\\n[^}]*\\.save\\s*\\('
suggested_patterns:
- "bulk_insert_mappings|bulk_update_mappings|add_all"
example_bad: |
for item in items:
product = Product(**item)
db.add(product)
example_good: |
products = [Product(**item) for item in items]
db.add_all(products)
- id: "PERF-007"
name: "Connection pool configuration"
severity: info
description: |
Configure database connection pool for optimal performance:
- pool_size: Number of persistent connections
- max_overflow: Additional connections allowed
- pool_pre_ping: Check connection health
- pool_recycle: Recycle connections periodically
file_pattern: "**/database.py|**/config*.py"
suggested_patterns:
- "pool_size|pool_pre_ping|pool_recycle|max_overflow"
- id: "PERF-008"
name: "Use EXISTS for existence checks"
severity: info
description: |
Use EXISTS or .first() is not None instead of count() > 0.
EXISTS stops at first match, count() scans all matches.
file_pattern: "**/service*.py"
anti_patterns:
- '\\.count\\(\\)\\s*>\\s*0'
- '\\.count\\(\\)\\s*>=\\s*1'
- '\\.count\\(\\)\\s*!=\\s*0'
suggested_patterns:
- "exists\\(\\)|scalar\\(exists"
example_bad: |
if db.query(Order).filter_by(customer_id=id).count() > 0:
example_good: |
exists_query = db.query(exists().where(Order.customer_id == id))
if db.scalar(exists_query):
- id: "PERF-009"
name: "Batch updates instead of loops"
severity: warning
description: |
Use .update() with filters instead of updating in a loop.
One UPDATE statement is faster than N individual updates.
file_pattern: "**/service*.py"
anti_patterns:
- 'for\\s+\\w+\\s+in\\s+\\w+:\\s*\\n[^}]*\\w+\\.\\w+\\s*='
suggested_patterns:
- "\\.update\\(\\{"
example_bad: |
for product in products:
product.is_active = False
db.add(product)
example_good: |
db.query(Product).filter(
Product.id.in_(product_ids)
).update({"is_active": False}, synchronize_session=False)
- id: "PERF-010"
name: "Avoid SELECT * patterns"
severity: info
description: |
When you only need specific columns, don't load entire rows.
This reduces memory usage and network transfer.
file_pattern: "**/service*.py"
- id: "PERF-011"
name: "Use appropriate join strategies"
severity: info
description: |
Choose the right join strategy:
- joinedload: Few related items, always needed
- selectinload: Many related items, always needed
- subqueryload: Complex queries, many related items
- lazyload: Rarely accessed relationships
file_pattern: "**/service*.py"
- id: "PERF-012"
name: "Transaction scope optimization"
severity: warning
description: |
Keep transactions short and focused:
- Don't hold transactions during I/O
- Commit after bulk operations
- Use read-only transactions when possible
file_pattern: "**/service*.py"
- id: "PERF-013"
name: "Query result caching"
severity: info
description: |
Consider caching for:
- Frequently accessed, rarely changed data
- Configuration tables
- Reference data (categories, statuses)
file_pattern: "**/service*.py"
suggested_patterns:
- "@cache|@lru_cache|redis|memcache"
- id: "PERF-014"
name: "Composite indexes for multi-column filters"
severity: info
description: |
Queries filtering on multiple columns benefit from composite indexes.
Order columns by selectivity (most selective first).
file_pattern: "**/models/database/*.py"
suggested_patterns:
- "Index\\([^)]*,[^)]*\\)"
- id: "PERF-015"
name: "Avoid correlated subqueries"
severity: info
description: |
Correlated subqueries execute once per row.
Use JOINs or window functions instead when possible.
file_pattern: "**/service*.py"