The figures in the answer are a quarter old and nobody noticed
In short
Nothing is broken. The answer is a quarter old because one stage runs on a cadence nobody ever agreed to, and the end-to-end worst case is the sum of every stage's worst case rather than its average. Measure it directly: sample 100 passages the assistant actually served, subtract source-modified from indexed-at, and read the age distribution instead of the job dashboard.
Key takeaways
- Green dashboards measure whether jobs ran. Age measures what the user got. Only the second is the complaint.
- Sample served passages, not indexed rows. The corpus average hides the age of what people actually read.
- End-to-end worst case is the sum of every stage's worst case, and it is usually days longer than anyone guesses.
- A bimodal age histogram means a stage stopped. A long right tail means a stage is merely slow.
- Most of the lag is upstream of your pipeline, in an export cadence somebody set once and never revisited.
Somebody notices the assistant quoting a headcount that was true in March. The ingest dashboard is green, every job ran last night, and the index has more documents in it than ever. All of that is true at once, because none of those numbers measure the thing being complained about: the age of the content that reached the answer.
Job monitoring answers whether a stage ran. Freshness is a property of a served passage, and it is the sum of every delay between a person editing a document and that edit becoming retrievable. Nobody owns that sum. Each stage was configured by someone who chose a sensible cadence for their own stage, and the total was never anybody's decision.
Measure the age of what was served, not the health of the jobs
The measurement has to start from the answer end. A corpus-wide average is dominated by whatever is largest, and the passages people actually read are a small, heavily skewed subset of it.
- Take 100 real answers from the last 2 weeks of logs, weighted toward the questions people ask most, and pull the passage ids each one cited.
- For every passage, collect 3 timestamps: when the source record was last modified, when the pipeline ingested it, and when it became visible to search. If you cannot collect all 3, that gap is the first finding.
- Compute 2 ages per passage — source-modified to indexed, and source-modified to now — and keep them separate. The first is pipeline lag; the second is how old the underlying fact was even before your pipeline saw it.
- Plot the distribution rather than the mean. Report the median, the 90th percentile and the maximum, because the complaint is always about the tail.
- Break the same figures down by source system. A single stale connector inside 6 healthy ones is invisible in the pooled number and obvious in the split.
- Do the same for a control set of passages you know were edited yesterday. If those are also old, the problem is a stage; if only old material is old, the problem is a backfill that never ran.
The lag budget, one line per stage
Write the chain down and put a number against every hop. Most teams discover 2 things doing this: a stage nobody had counted, and a cadence set once during a proof of concept that has been running ever since.
| Stage | What sets its cadence | How to measure it | The value that surprises people |
|---|---|---|---|
| Source export | A schedule inside somebody else's system, or a person running a report | Compare the newest record in the export against the export's own timestamp | A nightly export that runs at 22:00 misses everything entered after 22:00 by a full day |
| Arrival to ingest | Your scheduler, plus whatever waits for a file to appear | File modified time against the ingest run start | Weekend and holiday gaps, which double the ceiling without changing the average |
| Parse and chunk | Queue depth, and the slowest document format in the batch | Per-document queue wait, split by format | A backlog of scanned documents blocking a queue everything else shares |
| Embedding | Throughput against a rate limit, and whether unchanged content is re-embedded | Records committed per minute over the life of the run | A run that has not finished since the last full rebuild started |
| Index visibility | The engine's refresh behaviour, which is not the same as write acknowledgement | Write a record, then poll a search for it and time the gap | Writes acknowledged instantly and not searchable for seconds, or longer under bulk load |
| Answer cache | An expiry someone set for cost or latency reasons | Ask a cached question, then repeat it with caching bypassed | A long expiry that quietly overrides every freshness improvement upstream |
Index visibility is the row most often assumed to be zero. Search engines buffer writes and make them visible on a refresh cycle: Elasticsearch documents index.refresh_interval with a default of 1 second, and also notes that shards which have received no search traffic recently skip background refreshes until a search arrives. Both details matter, and both change between engines and between versions — measure yours with a write-then-poll test rather than trusting the number in somebody's blog post.
A wall in the histogram means stopped, a long tail means slow
The shape of the age distribution separates the 2 diagnoses that need completely different responses, and it does it without anyone reading a log.
- A clean wall. Everything is fresh up to a date and there is nothing newer, so a stage stopped on that date. Go and find what changed that day: a rotated credential, a renamed export folder, a disabled schedule.
- A long right tail. Most content is current, a minority is very old, and the old material is concentrated in 1 source or 1 format. That is a per-item failure, not a stopped stage.
- A uniform shift. Everything is old by roughly the same amount, which points at a cadence rather than a fault — usually the export or the cache, both of which delay everything equally.
- Fresh index, old figures. The pipeline is current and the underlying document is a quarterly report, so the number was already 3 months old when it arrived. No pipeline change fixes this one.
If the histogram shows a wall at the embedding stage rather than at the source, you are looking at a job that is not progressing rather than one that is slow, which is a different investigation — the instrumentation for it is in a reindex that has been running for three days.
Every stage was given a sensible cadence by somebody who owned that stage. Nobody was ever asked to approve the total.
Most of the delay happens before your pipeline sees anything
Teams instrument their own stages first because those are the ones they can see, and then find that the majority of the age was accumulated upstream. The export ran on time and contained data the source system had already been sitting on. There are 4 shapes this takes, and each one is negotiated rather than engineered.
- A report somebody runs. A human exports a file when they remember, so freshness is a function of somebody's calendar and their annual leave.
- A batch window aligned to the wrong close. The export runs before the day's entries are posted, so every record arrives one full cycle after it was created.
- A source that only recomputes periodically. The figure itself is quarterly, and no ingest cadence makes a quarterly number monthly.
- A platform sync you do not control. The connector's schedule is a setting in someone else's product, sometimes with a floor you cannot go below.
The last one is the moment a lot of teams stop tuning and start rebuilding, and it deserves an explicit decision rather than a drift — the trade is set out in building your own or configuring the platform you already have. The honest version of that conversation includes the possibility that a slower cadence is acceptable and the expectation was simply never written down.
Carry three timestamps and this stops being an argument
The reason this diagnosis is expensive the first time is that the evidence was never recorded. Fixing that is small: every chunk carries the source record's modified time, the time it was ingested, and the identifier of the run that produced it. With those 3 fields, the age distribution is a query rather than a project.
- Expose age percentiles per source, on a dashboard next to the job status, so the 2 different questions stop being confused for each other.
- Alert on the 95th percentile of age crossing your stated ceiling, not on job failure. A pipeline that stops is a pipeline whose ages start climbing hours before anyone notices, which is the alerting philosophy in what to alert on in an ingest pipeline.
- Keep the run identifier on the chunk. Tracing a served passage back to the run that wrote it is the point of lineage for an answer, and it is the field teams leave out and then need.
- Show the age in the interface. A quoted passage with an as-at date lets the reader apply their own judgement, which is cheaper than making every pipeline real-time.
The dashboard, the as-at label and the per-source breakdown are a couple of days of ordinary software rather than a data platform, which is how we tend to treat them in internal tools and operations software. The measurement is worth more than any of the fixes it prompts, because it converts a subjective complaint into a number two people can disagree about productively.
What an age measurement deliberately leaves open
You now know how old the served content is and which stage contributed most. You do not know how old it is allowed to be, and that is a business decision with an engineering price rather than something to infer from the histogram. It is argued out in deciding how stale an answer may be, and what a committed number obliges you to run is what a freshness SLA commits you to.
Two other failures wear the same costume. If the ages are healthy and the content is still wrong, the material may have changed shape rather than aged, which shows up as a step change in field completeness — the method is in an upstream field that changed while the pipeline carried on. And if a current document and an old one are both in the index, the answer alternating between them is duplication rather than lag, handled in two copies and one contradiction. All 3 sit in data readiness and pipelines, part of the engineering library.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
Why is my AI assistant using old data when the pipeline reports success?
Because job success measures whether a stage ran, not how old the content it served is. The age of a served passage is the sum of every delay in the chain — the source's export cadence, the ingest schedule, the embedding queue, the index's refresh behaviour and the answer cache's expiry — and each one was set independently by someone reasoning about their own stage. Measure age directly on a sample of served passages, and the guilty stage is usually obvious within an hour.
How do I measure end-to-end data freshness for a retrieval system?
Sample passages the system actually served and compute the gap between source-modified time and indexed-at time. Take 100 cited passages from recent answers, collect both timestamps for each, then report the median, the 90th percentile and the maximum, broken down by source system. Report the distribution rather than the mean, because the complaint is always about the tail.
What does it mean if all the content is fresh up to one date and nothing after it?
A stage stopped on that date, and the date is your best clue. That shape rules out slowness — a slow pipeline produces a long tail, not a clean wall — so look for a discrete event: an expired credential, a renamed folder or bucket, a disabled schedule, a permission change on a service account. Check what else happened that day before touching the pipeline.
The index is current but the numbers are still old. What now?
Then the pipeline is not the problem: the source figure itself was already old when it arrived. A quarterly report ingested the day it is published is a fresh document containing a 3-month-old number, and no ingest cadence changes that. The repair is either a different source that recomputes more often, or an interface that shows the as-at date so readers can judge for themselves.
- freshness
- pipelines
- measurement
- diagnosis
The work behind this page
Builds from our portfolio that this page draws on.
AskVault
An AI internal knowledge-search platform that answers employee questions from your own docs — grounded in citations, with knowledge gaps surfaced and deflection tracked.
Productivity AISupport Pulse
AI ticket triage and drafted replies for SaaS support teams — cut first response time and stop SLA leaks.
SaaS ToolsRead next
- The reindex has been running for three days and is still not donePercent complete cannot tell a slow job from a stuck one. Committed records per minute can, and the curve's shape names the cause before you open a single log file.diagnostic
- The record was deleted and the assistant still quotes itDeletion is the operation pipelines never test, so it is the one that stops halfway in silence. One tracer record, queried against every store in order, names the hop that ate it.diagnostic
- A freshness SLA is a promise about the worst case, not the averageFreshness is the age of the data behind an answer when it is served, held under a stated ceiling. Latency is how fast a run finishes, and the two can disagree by a week.definition
- Someone updated the document and the system still serves the old textThe indexing job is rarely the culprit. A timestamp that never moved, a hash over the wrong bytes, or an edit the extractor discards all leave the pipeline correctly deciding there is nothing to do.diagnostic
- A subset of the PDFs came through as gibberish and nobody lookedA chunk of mojibake embeds happily, indexes happily and retrieves for nothing. No stage errors, so the only defence is a screen that reads the text before it is indexed.diagnostic
- An upstream field changed and the pipeline carried on regardlessA renamed source field does not raise an error. It returns nothing, coalesces to an empty string, and quietly hollows out every record ingested since — until someone plots completeness by day.diagnostic
Working on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation