Library// diagnostic

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.

  1. 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.
  2. 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.
  3. 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.
  4. Plot the distribution rather than the mean. Report the median, the 90th percentile and the maximum, because the complaint is always about the tail.
  5. 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.
  6. 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.

StageWhat sets its cadenceHow to measure itThe value that surprises people
Source exportA schedule inside somebody else's system, or a person running a reportCompare the newest record in the export against the export's own timestampA nightly export that runs at 22:00 misses everything entered after 22:00 by a full day
Arrival to ingestYour scheduler, plus whatever waits for a file to appearFile modified time against the ingest run startWeekend and holiday gaps, which double the ceiling without changing the average
Parse and chunkQueue depth, and the slowest document format in the batchPer-document queue wait, split by formatA backlog of scanned documents blocking a queue everything else shares
EmbeddingThroughput against a rate limit, and whether unchanged content is re-embeddedRecords committed per minute over the life of the runA run that has not finished since the last full rebuild started
Index visibilityThe engine's refresh behaviour, which is not the same as write acknowledgementWrite a record, then poll a search for it and time the gapWrites acknowledged instantly and not searchable for seconds, or longer under bulk load
Answer cacheAn expiry someone set for cost or latency reasonsAsk a cached question, then repeat it with caching bypassedA long expiry that quietly overrides every freshness improvement upstream
Each stage's contribution to end-to-end age, and how to measure it directly

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
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

Read next

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