ML data engineering
Clocks: Event, Ingestion, Availability, and Label Time
Separate the clocks that govern event history, feature eligibility, corrections, and outcome maturity.
By the end you can
- Distinguish event, commit, ingestion, availability, correction, and label times
- Explain why an earlier event timestamp can still represent future information
- Audit feature latency and point-in-time eligibility using production evidence
- Write temporal contracts for features and labels
Visual
One event can carry five legitimate clocks
A record becomes safe for different uses at different moments. One card transaction carries five defensible timestamps. Each answers a different operational question: when the action occurred, when the producing system durably recorded it, when the data platform accepted it, when a decision path could actually retrieve it, and when its outcome became final enough to train or evaluate on.
None of these is a modelling convention a team is free to adopt or ignore. Each is set somewhere concrete, by someone who wrote the number down. In an Apache Kafka topic configuration. In Google's published measurement of how wrong its own clocks can be. In a Public Health England data-load failure. In a Visa dispute rule. In an EU delegated regulation with articles and annex tables. The rest of this lesson works through those cases, because a clock nobody can point at is a clock nobody owns.
Event time
When the real-world action or measurement occurred.
Source commit time
When the producing system durably recorded it.
Ingestion time
When the data platform accepted the record.
Feature availability time
When the value could actually be retrieved for a decision.
Label maturity time
When an outcome became sufficiently final for training or evaluation.
Historical correctness depends on the clock relevant to the decision, not the timestamp easiest to query.
Example
A chargeback exists at several times
Consider a card transaction scored at 10:00 on Monday and disputed later. How much later is not a matter of taste. Two published rulebooks bound it.
Regulation Z gives the consumer a window and the creditor a deadline. A billing-error notice is timely if the creditor receives it no later than 60 days after transmitting the first periodic statement reflecting the error. The creditor must then complete resolution within two complete billing cycles, and in no event later than 90 days after receiving that notice.
Visa's own windows run far longer. Its client bulletin of 14 January 2021 states: “Currently, issuers can process a dispute for a transaction where merchandise or services were not rendered due to merchant insolvency or bankruptcy no later than 120 calendar days from the last date the cardholder expected to receive the merchandise or services, not to exceed 540 calendar days from the transaction processing date.”
A training set that treats a transaction as non-fraudulent because no dispute has arrived is asserting that the 60-day, 90-day, 120-day and 540-day clocks have all run out. Usually they have not.
- Transaction time: Monday at 09:58, when the purchase occurred — and the anchor for Visa's outer limit, which is measured from the transaction processing date.
- Authorization commit: Monday at 09:59, when the payment system accepted it. Nothing about a dispute is knowable at this point, by construction.
- Warehouse ingestion: Monday at 10:07, seven minutes after the score. The row describes 09:58 but was not retrievable when the decision was made.
- Assertion window: under Regulation Z the consumer's billing-error notice is timely if received no later than 60 days after the creditor transmitted the first periodic statement reflecting the error, so the outcome cannot even be asserted before that statement goes out.
- Maturity and finality: the creditor must resolve within two complete billing cycles and no later than 90 days after receiving the notice, while a Visa non-delivery dispute can still arrive up to 120 calendar days from expected delivery and up to 540 calendar days from the transaction processing date. A label read at 30 days is not a final label.
Availability time is the boundary of legitimate evidence
Event time answers when something happened. A prediction can only use a value once the serving system could have accessed it under the production path.
This gap matters when databases are backfilled, corrected, or replicated with delay. A row may describe an earlier event and still become available after the decision.
Point-in-time datasets therefore need more than a business timestamp. They need a defensible availability model for every feature source.
The two clocks were named and separated in 2015, in the paper on the Dataflow model that Akidau and ten colleagues published. Event time is “the time at which the event itself actually occurred”. Processing time is “the time at which an event is observed at any given point during processing within the pipeline”.
What the paper refuses to do is treat the distance between them as a defect awaiting a fix: “During processing, the realities of the systems in use (communication delays, scheduling algorithms, time spent processing, pipeline serialization, etc.) result in an inherent and dynamically changing amount of skew between the two domains.” Inherent, and dynamically changing. A single subtracted constant contradicts both words at once.
This is not one research group's vocabulary. Apache Flink's engine documentation states the same split independently, and draws the consequence for anyone building on it: in event time “the progress of time depends on the data, not on any wall clocks”. An availability model is nothing more than an honest account of that moving gap, source by source.
A fact can describe the past and still be future information for a historical prediction.
Comparison
Timestamp substitutions that create false history
The tempting timestamp is often not the one that governed the production decision. Partition date is convenient for storage pruning, but it is usually derived from ingestion or the batch schedule. It may lag the event, may change after a replay, and is generally too coarse for scoring. A `last_updated` column shows the most recent mutation of a row rather than when each value was known. It overwrites earlier states, may record correction time, and destroys the intermediate versions a historical join needs. Only an availability timestamp states when the production decision path could retrieve the value — defined per source, tested against logs, supporting as-of retrieval.
There is a third substitution, and it never appears in a modelling document, because it happens at the broker. Apache Kafka has a topic-level setting called message.timestamp.type. The 4.0 configuration reference gives its purpose in one line: “Define whether the timestamp in the message is message create time or log append time.” Its valid values are CreateTime and LogAppendTime. Its default is CreateTime. Flip that one word to LogAppendTime and every record in the topic carries ingestion time in the field a downstream job will read as event time. No schema change, no migration, no line in a feature definition.
The default is not passive either. Under CreateTime the broker rejects any record whose timestamp is further ahead of the broker clock than message.timestamp.after.max.ms, default 3600000 ms — one hour. Under LogAppendTime that rule is ignored entirely. So a producer whose clock runs fast produces a rejected record in one configuration and, in the other, a record silently stamped by the broker instead. Redpanda, an independent implementation of the Kafka API, exposes the same switch as log_message_timestamp_type, with the same CreateTime default. Confluent Platform documents the setting downstream. The choice is a property of the interface, not one vendor's quirk, and it is one config key away from rewriting your entire event-time column.
Partition date
Convenient for storage pruning but often derived from ingestion or batch schedule.
- May lag the event
- May change after replay
- Usually too coarse for scoring
- Good for layout, weak for evidence
Last updated
Shows the most recent mutation of a row, not necessarily when each value was known.
- Can overwrite earlier states
- May reflect correction time
- Loses intermediate versions
- Dangerous for historical joins
Availability timestamp
Represents when the production decision path could retrieve the value.
- Defined per source
- Tested against logs
- Supports as-of retrieval
- Best boundary for feature eligibility
Key idea
A fixed delay is rarely an availability model
Subtracting a constant lag, such as fifteen minutes, from every feature is a common shortcut. It fails when latency varies by region, source state, retry path, or outage. Estimate the latency distribution and its tail.
A regulator has already settled which point of that distribution governs. Under MiFID II, trading venues classify themselves not by a typical delay but by an extreme one. ESMA's guidelines put it in a sentence: “For the purposes of clock synchronisation, ESMA considers that Trading Venues should use the gateway-to-gateway latency time at the 99th percentile.” If a safe boundary requires that percentile, document the resulting staleness and test exceptional paths separately.
The tail is where the cost is, and it has been priced. Between 25 September and 2 October 2020 the measurement in England worked and the transfer did not. Public Health England's statement of 4 October 2020 says what broke: “A technical issue was identified overnight on Friday 2 October in the data load process that transfers COVID-19 positive lab results into reporting dashboards. After rapid investigation, we have identified that 15,841 cases between 25 September and 2 October were not included in the reported daily COVID-19 cases.” Over 75% of them — 11,968 — should have been reported between 30 September and 2 October. All outstanding cases were transferred into the tracing system only by 1am on 3 October.
Fetzer and Graeber treated the episode as a natural experiment in 2021. They estimate that one additional case referred late to contact tracing was associated with 18.6 additional infections over the following six weeks. Every one of those 15,841 records had a correct event time. Only the ingestion clock moved, and it moved by up to a week. For critical features, replay production access logs or materialization histories rather than guessing from an average delay.
Average latency describes the middle; leakage often enters through the tail.
Case
Spanner publishes its clock uncertainty instead of assuming it away
Google's Spanner reports how wrong its clocks might be. Its TrueTime API “directly exposes clock uncertainty” rather than returning a single instant. A call returns an interval, and the caller is made to reckon with its width.
Google published that width. The Spanner paper reports: “In our production environment, ε is typically a sawtooth function of time, varying from about 1 to 7 ms over each poll interval.” The shape is arithmetic rather than folklore. A 30-second daemon poll interval at an applied drift rate of 200 microseconds per second accounts for 0 to 6 ms of that sawtooth. Communication delay to the time masters accounts for the remaining 1 ms.
Nor is 7 ms Google's private opinion of itself. Cockroach Labs, building a competing database without atomic clocks, describes the same 7 ms bound, and sets it against what an ordinary deployment gets from NTP synchronisation: roughly 100-250 ms. That is the real range a pipeline is operating inside when it picks a number.
A pipeline that subtracts one fixed lag has not avoided this problem. It has silently chosen a single point on that sawtooth, and asserted the bound Spanner spent an atomic-clock fleet refusing to assume.
Figure
Steps
Audit the clocks behind one feature
Choose a high-impact feature and reconstruct exactly when it became available.
First, name the source event: the real-world action and its native timestamp. Second, trace the durable writes — commit, replication, ingestion, correction — and record each as a separate time rather than collapsing them into one. Third, inspect the serving path and establish when the online or batch consumer could first retrieve the value. For anything arriving over a log, that includes reading the topic configuration, because CreateTime and LogAppendTime put different clocks into the same field. Fourth, compare historical builds against production, and check whether backfills expose earlier values than the serving path ever did. Fifth, encode the policy: store or derive the chosen availability boundary, and add temporal tests that fail when it is violated.
RTS 25 demands one step from regulated firms that most feature audits skip entirely. It requires them to identify the exact point at which a timestamp is applied. Most feature definitions cannot answer that question about their own sources. The audit is finished when yours can.
1. Name the source event
Identify the real-world action and its native timestamp.
2. Trace durable writes
Record commit, replication, ingestion, and correction times.
3. Inspect the serving path
Determine when the online or batch consumer could first retrieve the value.
4. Compare historical builds
Check whether backfills expose earlier values than production did.
5. Encode the policy
Store or derive the chosen availability boundary and add temporal tests.
Analogy
Four clocks in a newsroom
Something happens in the world. A reporter files notes, the newsroom receives them, and publication puts the story in front of readers — event time, source commit, ingestion, and feature availability. A later correction may improve the archive without changing what readers knew at noon. Historical evaluation must use the edition that was available at the original decision time. Production records may be machine-generated, duplicated, or partially reordered, and availability is a mechanical outcome rather than the editorial judgment a newsroom would apply.
One profession stopped debating this and started shipping editions. The U.S. Bureau of Economic Analysis publishes the size of its own rewrites. Over estimates from 1996 through 2024, quarterly real GDP growth was revised on average, without regard to sign, by 0.5 percentage point from the advance to the second estimate, 0.6 from advance to third, and 0.3 from second to third.
The Federal Reserve Bank of Philadelphia went further and kept the vintages themselves. Its real-time data set for macroeconomists, built by Croushore and Stark in 1999, reconstructs what one committee could actually see: “At the time of the FOMC meeting, the statistics available showed GDP growth of 2.9 percent in the first quarter (versus 4.7 percent in today’s statistics), and 1.5 percent in the second quarter (vs. 2.5 percent); the third quarter number hadn’t been released yet.”
That October 1992 meeting is the newsroom analogy with numbers attached. 2.9 against 4.7 is the gap between the archive and the edition on the table, and the third quarter was not merely wrong, it did not exist yet. Backtesting a policy rule on today's series would credit the committee with information no member had. The Bank's answer was not a better current series. It was to keep every edition.
The best archive is not automatically the right replay of what was knowable earlier.
Make temporal semantics part of the dataset interface
Every feature definition should name its event time, availability rule, maximum tolerated staleness, correction behavior, and behavior when the time is missing. Dataset manifests should preserve the build cutoff and source snapshot. Monitoring should report latency distributions and the fraction of records assigned by fallback logic.
This is not an exotic standard invented for machine learning. The EU delegated regulation known as RTS 25 has been in force since 3 January 2018, and it writes a temporal contract by number rather than by convention. Divergence from UTC is capped at 1 millisecond for trading venues whose gateway-to-gateway latency exceeds 1 ms. It is capped at 100 microseconds, timestamped to a granularity of 1 microsecond or better, where that latency is 1 ms or less and for firms using high-frequency algorithmic trading techniques. The same regulation requires firms to identify the exact point at which a timestamp is applied. ESMA's guidelines say which latency figure decides which cap a venue falls under: the 99th percentile, not the typical one.
So the contract already exists somewhere, with an accuracy bound, a granularity, a stamping point, and a percentile that fixes the classification. A team that cannot say, for one feature, which clock it uses, how far that clock may drift, and where in the path the value is stamped is holding a weaker temporal contract than European trading venues have been legally obliged to hold since 3 January 2018.
These choices let reviewers distinguish legitimate historical evidence from reconstructed knowledge. They also make outages and backfills explainable.
Time becomes governable only when each clock has an owner and an operational meaning.
Key takeaways
- Event time, commit time, ingestion time, feature availability time, and label maturity time answer different operational questions. The Dataflow model paper named the first two in 2015, and called the gap between them an inherent and dynamically changing skew — not a defect to be cleaned up.
- Historical features must respect when the production path could retrieve a value, not merely when the underlying event occurred. One Kafka topic setting erases that distinction: message.timestamp.type defaults to CreateTime, LogAppendTime writes ingestion time into the same field, and the one-hour message.timestamp.after.max.ms rejection rule is then ignored entirely.
- Backfills and corrections improve the archive while creating an impossible view of what was knowable earlier. At the October 1992 FOMC meeting first-quarter growth read 2.9 percent against 4.7 percent in later data, and BEA's own table puts average revisions from the advance to the third estimate at 0.6 percentage point.
- Availability policies should model latency distributions and exceptional paths rather than a universal fixed delay. ESMA directs trading venues to classify themselves at the 99th percentile of gateway-to-gateway latency, and Spanner reports an error bound that sawtooths from about 1 to 7 ms over each poll interval instead of a single number.
- Point-in-time audits should trace source writes, replication, materialization, and serving access, because the failure is mechanical: a data load lost 15,841 COVID-19 positives recorded in England between 25 September and 2 October 2020, and Fetzer and Graeber estimate 18.6 additional infections for each case referred late.
- Temporal semantics belong in contracts, manifests, monitoring, and tests. RTS 25 caps divergence from UTC at 1 millisecond, or 100 microseconds timestamped to 1 microsecond or better for the fastest venues and for firms using high-frequency algorithmic trading techniques, and requires firms to identify the exact point at which a timestamp is applied.