A search index is never truly "up-to-date" in the way a database is; it’s a snapshot, and the cost of refreshing that snapshot is a fundamental trade-off.

Imagine you’ve got a system that indexes products for an e-commerce site. When a product’s price changes, or a new product is added, we want that change to be reflected in search results as quickly as possible. This is where the concept of "refresh intervals" comes into play.

Let’s look at a simplified example. We’ll use Elasticsearch, a popular search engine.

PUT /my_products
{
  "settings": {
    "index": {
      "refresh_interval": "1s"
    }
  },
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "price": { "type": "float" },
      "description": { "type": "text" }
    }
  }
}

POST /my_products/_doc
{
  "name": "Wireless Mouse",
  "price": 25.99,
  "description": "Ergonomic design, long battery life."
}

In this setup, refresh_interval is set to 1s. This means that every second, Elasticsearch will make the documents indexed in the last second visible to search queries. So, if we index that "Wireless Mouse" and immediately query for it, it should appear within one second.

Now, let’s say we’re doing a massive bulk import of 10,000 products. If the refresh_interval is too aggressive, say 1s, Elasticsearch has to perform a refresh operation for every second that passes, even while we’re still indexing. Each refresh involves making the newly indexed data searchable, which is an I/O-intensive operation. Doing this repeatedly during a large write operation can significantly slow down the indexing process and consume a lot of resources.

This is where the mental model of the "search index as a replicated, eventually consistent view" becomes crucial. Unlike a traditional database ACID transaction where a write is immediately visible and guaranteed, a search index has an inherent delay. When you index a document, it’s first written to an in-memory buffer. A refresh operation then "flushes" these buffered documents to disk in a format that’s optimized for searching (Lucene segments). These segments are then made available for search queries.

The refresh_interval controls how often this flushing and segment merging process happens to make new data searchable. A shorter interval means fresher search results but higher overhead during writes. A longer interval means less overhead during writes but stale search results for a longer period.

Here are the key levers you control:

  • index.refresh_interval: This is the primary setting. You can set it to a time-based value (e.g., "30s", "5s") or to "-1" to disable automatic refreshes entirely.
  • index.number_of_shards: While not directly controlling freshness, the number of shards impacts how many parallel refresh operations might be occurring, influencing overall system load.
  • Manual Refresh: You can force a refresh at any time by sending a POST /<index_name>/_refresh request. This is useful after a bulk operation if you need immediate visibility.

Consider this scenario: you’re running a flash sale and need to update prices for 50,000 items. You also want these updated prices to be reflected in search results immediately after the sale starts.

You could temporarily disable refreshes during the price update phase:

PUT /my_products/_settings
{
  "index": {
    "refresh_interval": "-1"
  }
}

Then, perform your 50,000 price updates. Once all updates are done, you can force a single, efficient refresh:

POST /my_products/_refresh

And then, re-enable automatic refreshes with your desired interval, perhaps a slightly longer one for general operations:

PUT /my_products/_settings
{
  "index": {
    "refresh_interval": "30s"
  }
}

This approach balances the need for immediate visibility during critical events with the efficiency of bulk writes.

The one thing most people don’t realize is that a refresh operation isn’t just about making data visible; it also involves creating new Lucene segments and potentially merging existing ones. If you have a very high indexing rate with a short refresh interval, you can end up in a state where Elasticsearch is constantly creating new segments and struggling to merge them efficiently, leading to increased disk I/O and CPU usage. This can make your index effectively "stuck" and unresponsive to both writes and searches.

The next concept you’ll grapple with is how indexing performance itself scales with hardware and cluster configuration, beyond just the refresh interval.

Want structured learning?

Take the full Elasticsearch course →