Performance Review Overview
Performance Review Areas
Performance Review Checklist:
├── Database
│ ├── N+1 queries
│ ├── Missing indexes
│ ├── Full table scans
│ ├── Large result sets
│ └── Unoptimized joins
├── Caching
│ ├── Missing cache usage
│ ├── Inefficient cache keys
│ ├── Cache stampede risk
│ └── Cache invalidation
├── Code
│ ├── Inefficient algorithms
│ ├── Memory leaks
│ ├── Excessive loops
│ └── Blocking operations
└── Frontend
├── Render-blocking resources
├── Large assets
├── Unoptimized images
└── Excessive DOM operations
Performance Metrics
// Key performance metrics
$metrics = [
'response_time' => [
'p50' => 200, // ms
'p95' => 500,
'p99' => 1000
],
'throughput' => [
'requests_per_second' => 1000,
'concurrent_users' => 100
],
'resources' => [
'cpu_usage' => 70, // percent
'memory_usage' => 70,
'disk_io' => 50
]
];
Review Process
// Performance review checklist
$performanceChecklist = [
'database' => [
'Check for N+1 queries',
'Verify index usage',
'Review query complexity',
'Check result set sizes'
],
'caching' => [
'Verify cache usage',
'Check cache keys',
'Review cache invalidation',
'Check for cache stampede'
],
'code' => [
'Review algorithms',
'Check memory usage',
'Review loop efficiency',
'Check for blocking operations'
],
'frontend' => [
'Check render-blocking',
'Review asset sizes',
'Check image optimization',
'Review DOM operations'
]
];
N+1 Query Detection
Identify N+1 Queries
// Bad: N+1 query
$orders = $this->orderCollection->create();
foreach ($orders as $order) {
// This runs a query for each order
$customer = $this->customerRepository->get($order->getCustomerId());
echo $customer->getName();
}
// Good: Join in collection
$orders = $this->orderCollection->create()
->join(['customer' => 'customer_entity'], 'customer.entity_id = main_table.customer_id');
foreach ($orders as $order) {
// No additional query needed
echo $order->getData('customer_name');
}
// Good: Use resource model
$orders = $this->orderResourceModel->getOrdersWithCustomers();
Detect N+1 in Code Review
// Look for these patterns
$nPlusOnePatterns = [
// Loop with database call inside
'foreach.*->get(',
// Loop with repository call
'foreach.*Repository->get(',
// Loop with model load
'foreach.*Model->load(',
// Loop with collection addFieldToFilter
'foreach.*addFieldToFilter('
];
// Example review comment
"// Performance issue: N+1 query detected\n// This loop runs a database query for each order\n// Consider using a join or batch loading\n// See: https://devdocs.magento.com/guides/v2.4/performance-best-practices/algorithm.html"
Fix N+1 Queries
// Option 1: Join in collection
$collection = $this->collectionFactory->create();
$collection->getSelect()->join(
['customer' => 'customer_entity'],
'customer.entity_id = main_table.customer_id',
['customer_name' => 'name']
);
// Option 2: Batch loading
$orderIds = array_column($orders, 'entity_id');
$customers = $this->customerRepository->getByIds($orderIds);
// Option 3: Use resource model
$orders = $this->orderResourceModel->getOrdersWithDetails($orderIds);
Missing Index Detection
Identify Missing Indexes
-- Check for full table scans
SELECT * FROM mysql.slow_log
WHERE query LIKE '%catalog_product_entity%'
AND rows_examined > 10000;
-- Check index usage
SELECT * FROM sys.schema_unused_indexes;
-- Check missing indexes
SELECT * FROM sys.schema_redundant_indexes;
-- Analyze query
EXPLAIN SELECT * FROM catalog_product_entity WHERE sku = '24-WB04';
-- If type = 'ALL', missing index on sku column
Add Missing Indexes
// db_schema.xml
<schema>
<table name="custom_product">
<index referenceId="CUSTOM_PRODUCT_SKU" indexType="btree">
<column name="sku"/>
</index>
<index referenceId="CUSTOM_PRODUCT_STATUS_QTY" indexType="btree">
<column name="status"/>
<column name="qty"/>
</index>
</table>
</schema>
Review Index Strategy
// Index review checklist
$indexChecklist = [
'primary_key' => 'Always indexed',
'foreign_keys' => 'Should be indexed',
'where_clause' => 'Columns in WHERE should be indexed',
'join_columns' => 'Columns in JOIN should be indexed',
'order_by' => 'Columns in ORDER BY should be indexed'
];
// Example review
"// Missing index detected\n// Query filters by sku but no index exists\n// Add index: <index referenceId='PRODUCT_SKU'><column name='sku'/></index>"
Caching Strategy Review
Check Cache Usage
// Bad: No caching for expensive operation
public function getProductRecommendations($categoryId)
{
$products = $this->db->fetchAll(
'SELECT * FROM recommendations WHERE category_id = ?',
[$categoryId]
);
return $products;
}
// Good: Cache expensive operation
public function getProductRecommendations($categoryId)
{
$cacheKey = 'recommendations_' . $categoryId;
$cached = $this->cache->load($cacheKey);
if ($cached) {
return unserialize($cached);
}
$products = $this->db->fetchAll(
'SELECT * FROM recommendations WHERE category_id = ?',
[$categoryId]
);
$this->cache->save(serialize($products), $cacheKey, [], 3600);
return $products;
}
Cache Key Review
// Bad: Inefficient cache keys
$cacheKey = 'product_' . json_encode($filters);
// Good: Deterministic cache keys
$cacheKey = 'product_' . md5(serialize($filters));
// Or use sorted parameters
$cacheKey = 'product_' . implode('_', array_values($filters));
Cache Invalidation Review
// Bad: No cache invalidation
public function saveProduct($product)
{
$this->resourceModel->save($product);
// Cache not invalidated!
}
// Good: Cache invalidation on save
public function saveProduct($product)
{
$this->resourceModel->save($product);
$this->cache->remove('product_' . $product->getId());
}
// Review: Check all save/update/delete operations
// Ensure cache is invalidated
Algorithm Efficiency Review
Identify Inefficient Algorithms
// Bad: O(n²) algorithm
function findDuplicates($array)
{
$duplicates = [];
for ($i = 0; $i < count($array); $i++) {
for ($j = $i + 1; $j < count($array); $j++) {
if ($array[$i] === $array[$j]) {
$duplicates[] = $array[$i];
}
}
}
return $duplicates;
}
// Good: O(n) algorithm
function findDuplicates($array)
{
$counts = array_count_values($array);
return array_keys(array_filter($counts, function($count) {
return $count > 1;
}));
}
Memory Leak Detection
// Potential memory leak
class DataProcessor
{
private $data = []; // Grows indefinitely
public function process($item)
{
$this->data[] = $item; // Never cleared
// Process...
}
}
// Fix: Clear data after processing
class DataProcessor
{
private $data = [];
public function process($item)
{
$this->data[] = $item;
$this->processData();
$this->data = []; // Clear after processing
}
}
Blocking Operations
// Bad: Blocking operation
public function syncData()
{
$this->httpClient->setTimeout(30); // Blocks for 30 seconds
$this->httpClient->get('https://api.example.com/data');
}
// Good: Non-blocking with timeout
public function syncData()
{
$this->httpClient->setTimeout(5); // Short timeout
try {
$result = $this->httpClient->get('https://api.example.com/data');
$this->processData($result);
} catch (TimeoutException $e) {
$this->logger->warning('Sync timeout, will retry later');
$this->queue->sendMessage('sync.retry', ['item' => $item]);
}
}
Practice Problems
0 / 1 solved
Performance Review Exercise
Review code for N+1 queries, missing indexes, and inefficient algorithms.
Solution
// Findings:
// 1. N+1: Loop with get() → Use join
// 2. Index: sku column not indexed → Add index
// 3. Algorithm: O(n²) duplicate check → Use array_count_values
// 4. Cache: Missing cache → Add cache layer
// Results: 10x performance improvement Quiz
1. What is an N+1 query?
2. How to detect missing indexes?
3. What is cache stampede?
4. What is the time complexity of O(n²)?
Flashcards
Question
N+1 query?
Click to reveal answer
Answer
One query + N queries in loop, use joins/batch loading
Question
Missing index detection?
Click to reveal answer
Answer
EXPLAIN shows type='ALL' (full table scan)
Question
Cache stampede?
Click to reveal answer
Answer
Multiple requests rebuild cache simultaneously
Question
O(n²) complexity?
Click to reveal answer
Answer
Quadratic, slow for large datasets
Question
Memory leak sign?
Click to reveal answer
Answer
Memory usage growing indefinitely without cleanup
Revision Notes
Key Takeaways
- 1. N+1 queries: One + N in loop, use joins/batch
- 2. Missing indexes: EXPLAIN shows full table scan
- 3. Cache stampede: Multiple requests rebuild cache
- 4. O(n²): Quadratic, slow for large datasets
- 5. Memory leak: Growing usage without cleanup
Interview Tips
- • Explain N+1 query detection
- • Discuss index strategy
- • Know caching best practices
- • Understand algorithm complexity
Cheat Sheet
Performance Review
- N+1: One + N in loop → Join/batch
- Index: EXPLAIN → type=ALL → Add index
- Cache: Stampede → Lock/early expiry
- O(n²): Quadratic → Optimize
- Memory: Growing → Clean up