home / skills / venkateshvenki404224 / frappe-apps-manager / frappe-performance-optimizer

frappe-performance-optimizer skill

/frappe-apps-manager/skills/frappe-performance-optimizer

This skill generates optimized queries, caching, and indexes to boost Frappe performance and eliminate slow queries.

npx playbooks add skill venkateshvenki404224/frappe-apps-manager --skill frappe-performance-optimizer

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.8 KB
---
name: frappe-performance-optimizer
description: Generate optimized queries, caching, and indexes for Frappe performance. Use when optimizing slow queries, implementing caching, or improving performance.
---

# Frappe Performance Optimizer

Generate performance-optimized code including efficient queries, caching strategies, and database indexes for Frappe applications.

## When to Use This Skill

Claude should invoke this skill when:
- User reports slow queries or performance issues
- User wants to add caching
- User needs database indexing
- User mentions performance, optimization, or slow queries
- User wants to eliminate N+1 queries

## Capabilities

### 1. Query Optimization

**Optimized Report Query:**
```python
# Efficient query with proper indexing
def get_sales_summary(from_date, to_date):
    return frappe.db.sql("""
        SELECT
            si.customer,
            c.customer_name,
            c.customer_group,
            COUNT(si.name) as invoice_count,
            SUM(si.grand_total) as total_amount
        FROM `tabSales Invoice` si
        INNER JOIN `tabCustomer` c ON c.name = si.customer
        WHERE si.posting_date BETWEEN %s AND %s
            AND si.docstatus = 1
        GROUP BY si.customer
        ORDER BY total_amount DESC
        LIMIT 100
    """, (from_date, to_date), as_dict=True)

# Add index for performance
frappe.db.add_index('Sales Invoice', ['customer', 'posting_date', 'docstatus'])
```

### 2. Caching Implementation

**Cache Expensive Calculations:**
```python
def get_item_price(item_code, price_list, customer=None):
    """Get price with caching"""
    cache_key = f"price:{item_code}:{price_list}:{customer or 'default'}"

    # Try cache
    cached_price = frappe.cache().get_value(cache_key)
    if cached_price is not None:
        return cached_price

    # Calculate price (expensive)
    price = frappe.db.get_value('Item Price',
        filters={'item_code': item_code, 'price_list': price_list},
        fieldname='price_list_rate'
    )

    # Cache for 1 hour
    if price:
        frappe.cache().set_value(cache_key, price, expires_in_sec=3600)

    return price
```

### 3. Batch Operations

**Bulk Update Pattern:**
```python
def bulk_update_items(updates):
    """Update multiple items efficiently"""
    # updates = [{'item_code': 'ITEM-001', 'is_active': 1}, ...]

    # Build single query
    item_codes = [u['item_code'] for u in updates]

    frappe.db.sql("""
        UPDATE `tabItem`
        SET is_active = 1,
            modified = NOW(),
            modified_by = %s
        WHERE name IN %s
    """, (frappe.session.user, tuple(item_codes)))

    frappe.db.commit()
```

## References

**Performance Examples:**
- Stock Ledger: https://github.com/frappe/erpnext/blob/develop/erpnext/stock/stock_ledger.py
- Get Item Details: https://github.com/frappe/erpnext/blob/develop/erpnext/stock/get_item_details.py

Overview

This skill generates performance-optimized code, queries, caching patterns, and index recommendations tailored for Frappe applications. It focuses on eliminating slow queries, preventing N+1 patterns, and applying practical caching and bulk-operation strategies. The goal is measurable speed-ups with minimal disruption to existing code.

How this skill works

The skill inspects reported slow queries, schema usage, and common access patterns to propose optimized SQL, appropriate database indexes, and caching layers. It produces concrete code snippets for Frappe: efficient JOIN queries, cache wrappers using frappe.cache(), and bulk update patterns with single SQL operations. It can also recommend index definitions and where to place caches (in-memory vs. TTL).

When to use it

  • When users report slow page loads or reports tied to database queries
  • When audit or profiler shows N+1 queries or repeated lookups
  • Before deploying reports or heavy-reporting views to production
  • When implementing expensive calculations that can be reused
  • When preparing bulk data migrations or updates

Best practices

  • Prefer SELECT with JOINs and GROUP BY instead of per-row queries to avoid N+1 issues
  • Add composite indexes that match WHERE and ORDER BY columns used by heavy queries
  • Cache idempotent, read-heavy computations with explicit TTL and cache keys tied to relevant records
  • Use bulk SQL updates/inserts for large batches and call frappe.db.commit() once after the batch
  • Measure performance before and after changes using query profiling or explain plans

Example use cases

  • Optimize a slow sales summary report by replacing per-invoice loops with a single aggregated JOIN and adding an index on (customer, posting_date, docstatus)
  • Cache item price lookups per price list and customer using frappe.cache() with a 1-hour TTL
  • Implement bulk activation/deactivation of Item records using a single UPDATE WHERE name IN (...) to reduce round-trips
  • Add indexes and rewrite queries for stock ledger or other report-generating modules to drop execution time from minutes to seconds
  • Detect and rewrite code paths that fetch child records in loops into batched queries

FAQ

Will adding indexes always improve performance?

No. Indexes speed reads but add overhead to writes and increase storage. Add indexes targeted to heavy read queries and monitor write latency and index size.

How do I choose what to cache and for how long?

Cache read-heavy, expensive-to-compute values that change infrequently. Use short TTLs for frequently changing data and invalidate caches when related records change.