Quiet Performance Wins: Scheduling SQL Index Maintenance in Optimizely CMS

Quiet Performance Wins: Scheduling SQL Index Maintenance in Optimizely CMS

Alex Rollin
Alex Rollin
October 16, 2025
Last updated : February 15, 2026
October 16, 2025

Your Optimizely CMS site might be running slower than it should, and the culprit could be hiding in plain sight: fragmented SQL indexes. Just like a messy filing cabinet makes finding documents harder, fragmented indexes force your database to work overtime searching for content. The good news? Setting up automated index maintenance takes about 15 minutes and can prevent those mysterious performance drops that frustrate editors and visitors alike.

This guide walks you through configuring SQL index maintenance in Optimizely CMS (version 12 and newer), from basic scheduling to advanced customization. You'll learn how to set up maintenance windows, adjust fragmentation thresholds, and monitor the results, all without disrupting your users.

Prerequisites

Before you start configuring index maintenance, make sure you have:

  • Admin access to your Optimizely CMS instance (you'll need to access the Scheduled Jobs section)
  • Basic understanding of SQL Server indexes and how they affect performance
  • Access to application settings (for customizing thresholds and timeouts)
  • SQL Server 2016 or newer (earlier versions work but have limited online rebuild options)
  • Knowledge of your site's traffic patterns (to identify the best maintenance windows)

If you're using Optimizely Commerce alongside CMS, the same scheduled job handles both databases, but you might need to adjust timeout settings for larger catalogs.

Step-by-Step Implementation

Step 1: Locate the Built-in Maintenance Job

Navigate to your Optimizely admin panel and click on Admin > Scheduled Jobs. Look for a job called "Maintain database indexes" or something similar. This built-in job comes pre-installed with Optimizely CMS and handles both reorganizing and rebuilding indexes based on fragmentation levels.

If you don't see this job, check that your Optimizely installation is complete and that database maintenance features weren't disabled during setup.

Step 2: Configure the Schedule

Click on the maintenance job to open its settings. You'll see options for:

  • Active: Toggle this on to enable automatic execution
  • Schedule: Set when and how often the job runs
  • Next Execution: Shows when the job will run next

For the schedule, pick a time when your site has minimal traffic. Most sites see the lowest activity between 2 AM and 4 AM local time. Start with a weekly schedule, Sunday morning at 3 AM is a popular choice.

Step 3: Customize Fragmentation Thresholds

By default, Optimizely rebuilds indexes with over 30% fragmentation and reorganizes those between 10% and 30%. These defaults work well for most sites, but you can adjust them in your appsettings.json:

{
  "EPiServer": {
    "Commerce": {
      "HighFragmentationThreshold": 30,
      "LowFragmentationThreshold": 10,
      "DataBaseIndicesJobCommandTimeOut": 120
    }
  }
}

What these settings mean:

  • HighFragmentationThreshold: Indexes above this percentage get rebuilt (complete recreation)
  • LowFragmentationThreshold: Indexes between this and the high threshold get reorganized (lighter cleanup)
  • DataBaseIndicesJobCommandTimeOut: Maximum seconds the job can run before timing out

For content-heavy sites with frequent updates, consider lowering the high threshold to 25% for more aggressive maintenance.

Step 4: Handle Large Databases

If your database is over 50GB or contains millions of content items, the default 30-second timeout won't be enough. Increase the timeout value gradually, start with 120 seconds and adjust based on job completion logs.

For very large databases, consider splitting maintenance across multiple nights:

{
  "EPiServer": {
    "Cms": {
      "DataBaseIndicesJobCommandTimeOut": 180
    },
    "Commerce": {
      "DataBaseIndicesJobCommandTimeOut": 240
    }
  }
}

Step 5: Set Up Monitoring

After configuring the job, monitor its execution through the job history. Look for:

  • Completion time: Jobs taking over 5 minutes might impact morning users
  • Error messages: Timeouts or permission issues need immediate attention
  • Skipped indexes: Some indexes might be too large for the timeout window

Create a simple SQL query to check fragmentation levels manually:

SELECT 
    OBJECT_NAME(ips.object_id) AS TableName,
    i.name AS IndexName,
    ips.avg_fragmentation_in_percent,
    ips.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id 
    AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 10
    AND ips.page_count > 1000
ORDER BY ips.avg_fragmentation_in_percent DESC;

Run this query before and after maintenance to verify the job's effectiveness.

Code Examples and Configuration

Custom Maintenance Script for Advanced Control

While the built-in job handles most scenarios, some teams prefer custom scripts for granular control. Here's a basic maintenance procedure you can schedule through SQL Server Agent:

CREATE PROCEDURE [dbo].[CustomIndexMaintenance]
AS
BEGIN
    DECLARE @TableName NVARCHAR(255)
    DECLARE @IndexName NVARCHAR(255)
    DECLARE @Fragmentation FLOAT
    DECLARE @SQL NVARCHAR(MAX)
    
    DECLARE index_cursor CURSOR FOR
    SELECT 
        OBJECT_NAME(ips.object_id),
        i.name,
        ips.avg_fragmentation_in_percent
    FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
    INNER JOIN sys.indexes i ON ips.object_id = i.object_id 
        AND ips.index_id = i.index_id
    WHERE ips.avg_fragmentation_in_percent > 10
        AND ips.page_count > 1000
        AND i.name IS NOT NULL
    
    OPEN index_cursor
    FETCH NEXT FROM index_cursor INTO @TableName, @IndexName, @Fragmentation
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF @Fragmentation > 30
            SET @SQL = 'ALTER INDEX ['   @IndexName   '] ON [dbo].['   @TableName   '] REBUILD'
        ELSE
            SET @SQL = 'ALTER INDEX ['   @IndexName   '] ON [dbo].['   @TableName   '] REORGANIZE'
        
        EXEC sp_executesql @SQL
        
        FETCH NEXT FROM index_cursor INTO @TableName, @IndexName, @Fragmentation
    END
    
    CLOSE index_cursor
    DEALLOCATE index_cursor
END

Adjusting for Different Database Sizes

Small databases (under 10GB) can use aggressive settings:

{
  "EPiServer": {
    "Commerce": {
      "HighFragmentationThreshold": 20,
      "LowFragmentationThreshold": 5,
      "DataBaseIndicesJobCommandTimeOut": 60
    }
  }
}

Large databases (over 100GB) need conservative settings:

{
  "EPiServer": {
    "Commerce": {
      "HighFragmentationThreshold": 40,
      "LowFragmentationThreshold": 15,
      "DataBaseIndicesJobCommandTimeOut": 600
    }
  }
}

Common Mistakes to Avoid

Running Maintenance During Business Hours

The biggest mistake is scheduling maintenance during active hours. Even "light" reorganization operations can cause blocking on busy tables. Always verify your timezone settings. A job scheduled for 3 AM UTC might run at 10 PM EST.

Over-Maintaining Indexes

Daily rebuilds on databases with minimal changes waste resources and increase SSD wear. We've found that most Optimizely sites perform well with weekly maintenance unless they process thousands of content updates daily.

Ignoring Job Failures

Silent failures are dangerous. If the maintenance job times out regularly, fragmentation accumulates until performance degrades noticeably. Set up email alerts for job failures:

  • Go to the scheduled job settings
  • Enable "Send email on failure"
  • Configure SMTP settings in your application
  • Add your team's monitoring email address

Forgetting About Statistics

Index maintenance doesn't always update statistics, which help SQL Server choose efficient query plans. If you notice slow queries despite low fragmentation, update statistics manually:

EXEC sp_updatestats;

Not Testing After Updates

Optimizely updates sometimes reset scheduled job configurations. After platform updates, verify your maintenance schedule and custom settings remain intact.

Testing and Verification Steps

Pre-Maintenance Baseline

Before your first scheduled maintenance, capture baseline metrics:

  • Run the fragmentation check query provided earlier
  • Note average page load times in Application Insights or your monitoring tool
  • Record database file sizes
  • Check current CPU and memory usage patterns

Post-Maintenance Validation

After the job runs successfully:

  • Check job history: Confirm completion without errors
  • Verify fragmentation reduction: Re-run the fragmentation query
  • Monitor performance: Look for improved response times over the next 24 hours
  • Review logs: Check for any blocking or timeout warnings

Performance Testing

Create a simple load test to measure improvement:

[Fact]
public async Task IndexMaintenanceImprovesFindPagesQuery()
{
    // Arrange
    var stopwatch = new Stopwatch();
    var iterations = 100;
    var timings = new List();
    
    // Act
    for (int i = 0; i < iterations; i  )
    {
        stopwatch.Restart();
        var pages = await _contentLoader
            .GetChildren(ContentReference.StartPage)
            .ToListAsync();
        stopwatch.Stop();
        timings.Add(stopwatch.ElapsedMilliseconds);
    }
    
    // Assert
    var averageTime = timings.Average();
    var baseline = 50; // milliseconds, adjust based on your baseline
    Assert.True(averageTime < baseline, 
        $"Query averaging {averageTime}ms, expected under {baseline}ms");
}

Long-term Monitoring

Set up a monthly review process:

  • Export job history to track completion rates
  • Graph fragmentation levels over time
  • Correlate maintenance windows with performance metrics
  • Adjust schedules based on actual fragmentation patterns

Our experience shows that sites often need schedule adjustments after major content migrations or seasonal traffic changes.

Advanced Considerations

Online vs. Offline Rebuilds

SQL Server Standard Edition performs offline rebuilds by default, making tables unavailable during the operation. If you have SQL Server Enterprise Edition, enable online rebuilds in your custom scripts:

ALTER INDEX [IndexName] ON [TableName] 
REBUILD WITH (ONLINE = ON, MAXDOP = 4);

The MAXDOP setting limits parallel processing to prevent CPU saturation.

Handling Commerce Catalogs

Optimizely Commerce databases often need different maintenance patterns than CMS databases. Product catalogs with frequent price updates might benefit from twice-weekly maintenance, while CMS content remains on a weekly schedule.

Integration with Deployment Pipelines

Consider triggering index maintenance after major deployments:

# PowerShell script for post-deployment maintenance
Invoke-Sqlcmd -Query "EXEC dbo.CustomIndexMaintenance" `
              -ServerInstance "your-server" `
              -Database "your-database" `
              -QueryTimeout 600

Conclusion

Setting up proper SQL index maintenance might not be the most exciting task, but it's one of those investments that pays dividends through consistent performance and fewer emergency debugging sessions. Start with the built-in Optimizely job, schedule it for quiet hours, and adjust based on your specific workload patterns.

Remember that perfect index maintenance doesn't exist. You're aiming for good enough performance without excessive resource usage. Monitor your metrics, adjust gradually, and don't forget about those statistics updates.

Working with teams has taught us that the sites with the best performance aren't necessarily those with the most aggressive maintenance schedules, but rather those with consistent, well-monitored maintenance routines that match their actual usage patterns.

Need help analyzing your Optimizely database performance or setting up a maintenance plan that fits your specific content patterns and traffic loads? Our team can review your current index fragmentation levels, identify bottlenecks in your queries, and create a customized maintenance schedule that keeps your site running smoothly without unnecessary overhead. Reach out to discuss how we can help you achieve those quiet performance wins.

Share this article