Commit 79b33ce
feat: Add opt-in filter_by_created_timestamp cutoff to get_historical_features (#6617)
* feat: Add opt-in at_event_time created_timestamp cutoff to get_historical_features
When a feature view has a created_timestamp_column, it is currently used
only as a dedup tiebreaker in point-in-time joins, so retrieval can serve
feature values whose created_timestamp is after the entity row's event
timestamp (backfills, late corrections). This leaks future information
into training data and makes training sets non-reproducible.
Add an opt-in at_event_time flag (default False) to get_historical_features
that adds a created_timestamp <= entity_timestamp predicate to the
point-in-time join, so retrieval reflects what was known as of each entity
row's timestamp. Implemented for the SQL template stores (BigQuery,
Redshift, Spark, Trino, Athena, Postgres, ClickHouse, Couchbase), the
ibis-based stores (DuckDB, MSSQL, Oracle) and the dask store. Snowflake,
Remote and Ray raise NotImplementedError when the flag is set.
Fixes #6615
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* test: Add universal integration test and docs for at_event_time
Adds a universal offline store integration test covering at_event_time
(default returns backfilled values, at_event_time=True only returns values
created at or before the entity timestamp, stores without support skip via
NotImplementedError) and documents the flag on the point-in-time joins
concept page.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* refactor: Rename at_event_time to filter_by_created_timestamp and address review feedback
Rename the flag to filter_by_created_timestamp to match the repo's
mechanical flag naming and sit alongside created_timestamp_column.
Review fixes:
- HybridOfflineStore now forwards optional kwargs to the delegated store
instead of raising TypeError for supported underlying stores.
- The dask filter now excludes feature rows with a null created timestamp
(consistent with the SQL predicate) while still keeping unmatched entity
rows from the left join.
- The integration test only treats NotImplementedError from
get_historical_features itself as an unsupported-store skip.
- Add template-render tests for all SQL dialects and a dask null
created-timestamp test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* refactor: Centralize filter_by_created_timestamp support behind a store capability flag
Simplification pass over the feature, reviewed again by Codex:
- Declare support via OfflineStore.supports_filter_by_created_timestamp
(default False) and check it once in the passthrough provider via
ensure_filter_by_created_timestamp_supported, replacing the three
per-store NotImplementedError guards. Unsupported stores can no longer
silently ignore the flag.
- The hybrid store re-checks the resolved child store before delegating.
- Standardize every supporting store on an explicit
filter_by_created_timestamp parameter instead of kwargs.get lookups.
- Document the flag in the OfflineStore.get_historical_features docstring
alongside start_date/end_date.
- Return the dask created-timestamp filter lazily so the mask fuses into
the following _drop_duplicates persist instead of forcing an extra
materialization.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* chore: Trim comments to the non-obvious constraints
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* docs: Tighten filter_by_created_timestamp docstring
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* docs: Condense filter_by_created_timestamp caveats into a hint
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* fix: Keep entity rows whose candidate versions are all future-created
The Dask cutoff filtered rows after the left join, so an entity whose every
candidate version was created after its timestamp lost all of its rows and
disappeared from the result. get_historical_features must preserve
entity-dataframe cardinality and return null features instead.
Blank the feature-view columns rather than dropping the row, which leaves it
indistinguishable from an unmatched left join. _drop_duplicates already sorts
nulls first and keeps the last row, so a valid version still wins where one
exists and a blanked row survives only when nothing else does.
The unit tests now set fv.entity_columns. Without it the derived join keys are
empty and _merge silently cross joins, which hid the per-entity behaviour
because the existing cases all used a single entity row.
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* refactor: Normalize created timestamp on read, not in the join predicate
The cutoff predicate cast created_timestamp to UTC while the event-timestamp
comparison beside it did not. read_fv normalizes the event timestamp when it
reads the source and left the created timestamp alone, so the predicate was
compensating for a missing normalization at the one comparison site.
Normalize both on read instead. The predicate then needs no cast and matches
its neighbour. deduplicate() orders by created_timestamp_column regardless of
the cutoff flag, so the normalization is unconditional rather than gated on it;
casting a column that is already tz-aware compiles away, so this leaves the
emitted query unchanged for tz-aware sources and retires the "mutate only if
tz-naive" TODO.
Signed-off-by: David <david-adeniji@hotmail.co.uk>
* Shorten comments in the created timestamp cutoff
Signed-off-by: David <david-adeniji@hotmail.co.uk>
---------
Signed-off-by: David <david-adeniji@hotmail.co.uk>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent faf85e0 commit 79b33ce
22 files changed
Lines changed: 618 additions & 8 deletions
File tree
- docs/getting-started/concepts
- sdk/python
- feast
- infra
- offline_stores
- contrib
- athena_offline_store
- clickhouse_offline_store
- couchbase_offline_store
- mssql_offline_store
- oracle_offline_store
- postgres_offline_store
- spark_offline_store
- trino_offline_store
- tests
- integration/offline_store
- unit/infra/offline_stores
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1940 | 1940 | | |
1941 | 1941 | | |
1942 | 1942 | | |
| 1943 | + | |
1943 | 1944 | | |
1944 | 1945 | | |
1945 | 1946 | | |
| |||
1971 | 1972 | | |
1972 | 1973 | | |
1973 | 1974 | | |
| 1975 | + | |
| 1976 | + | |
| 1977 | + | |
| 1978 | + | |
| 1979 | + | |
1974 | 1980 | | |
1975 | 1981 | | |
1976 | 1982 | | |
| |||
2072 | 2078 | | |
2073 | 2079 | | |
2074 | 2080 | | |
2075 | | - | |
| 2081 | + | |
2076 | 2082 | | |
2077 | 2083 | | |
2078 | 2084 | | |
2079 | 2085 | | |
| 2086 | + | |
| 2087 | + | |
2080 | 2088 | | |
2081 | 2089 | | |
2082 | 2090 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
| 140 | + | |
| 141 | + | |
140 | 142 | | |
141 | 143 | | |
142 | 144 | | |
| |||
273 | 275 | | |
274 | 276 | | |
275 | 277 | | |
| 278 | + | |
276 | 279 | | |
277 | 280 | | |
278 | 281 | | |
| |||
388 | 391 | | |
389 | 392 | | |
390 | 393 | | |
| 394 | + | |
391 | 395 | | |
392 | 396 | | |
393 | 397 | | |
| |||
1734 | 1738 | | |
1735 | 1739 | | |
1736 | 1740 | | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
1737 | 1745 | | |
1738 | 1746 | | |
1739 | 1747 | | |
| |||
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
| 70 | + | |
69 | 71 | | |
70 | 72 | | |
71 | 73 | | |
| |||
195 | 197 | | |
196 | 198 | | |
197 | 199 | | |
| 200 | + | |
198 | 201 | | |
199 | 202 | | |
200 | 203 | | |
| |||
252 | 255 | | |
253 | 256 | | |
254 | 257 | | |
| 258 | + | |
255 | 259 | | |
256 | 260 | | |
257 | 261 | | |
| |||
651 | 655 | | |
652 | 656 | | |
653 | 657 | | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
654 | 662 | | |
655 | 663 | | |
656 | 664 | | |
| |||
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
| |||
48 | 50 | | |
49 | 51 | | |
50 | 52 | | |
| 53 | + | |
51 | 54 | | |
52 | 55 | | |
53 | 56 | | |
| |||
123 | 126 | | |
124 | 127 | | |
125 | 128 | | |
| 129 | + | |
126 | 130 | | |
127 | 131 | | |
128 | 132 | | |
| |||
539 | 543 | | |
540 | 544 | | |
541 | 545 | | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
542 | 550 | | |
543 | 551 | | |
544 | 552 | | |
| |||
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
67 | 69 | | |
68 | 70 | | |
69 | 71 | | |
| |||
136 | 138 | | |
137 | 139 | | |
138 | 140 | | |
| 141 | + | |
139 | 142 | | |
140 | 143 | | |
141 | 144 | | |
| |||
197 | 200 | | |
198 | 201 | | |
199 | 202 | | |
| 203 | + | |
200 | 204 | | |
201 | 205 | | |
202 | 206 | | |
| |||
481 | 485 | | |
482 | 486 | | |
483 | 487 | | |
| 488 | + | |
484 | 489 | | |
485 | 490 | | |
486 | 491 | | |
| |||
507 | 512 | | |
508 | 513 | | |
509 | 514 | | |
| 515 | + | |
510 | 516 | | |
511 | 517 | | |
512 | 518 | | |
| |||
620 | 626 | | |
621 | 627 | | |
622 | 628 | | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
623 | 632 | | |
624 | 633 | | |
625 | 634 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
| 121 | + | |
| 122 | + | |
121 | 123 | | |
122 | 124 | | |
123 | 125 | | |
| |||
151 | 153 | | |
152 | 154 | | |
153 | 155 | | |
| 156 | + | |
154 | 157 | | |
155 | 158 | | |
156 | 159 | | |
| |||
168 | 171 | | |
169 | 172 | | |
170 | 173 | | |
| 174 | + | |
171 | 175 | | |
172 | 176 | | |
173 | 177 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
486 | 486 | | |
487 | 487 | | |
488 | 488 | | |
| 489 | + | |
| 490 | + | |
489 | 491 | | |
490 | 492 | | |
491 | 493 | | |
| |||
521 | 523 | | |
522 | 524 | | |
523 | 525 | | |
| 526 | + | |
524 | 527 | | |
525 | 528 | | |
526 | 529 | | |
| |||
554 | 557 | | |
555 | 558 | | |
556 | 559 | | |
| 560 | + | |
557 | 561 | | |
558 | 562 | | |
559 | 563 | | |
| |||
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
| |||
135 | 137 | | |
136 | 138 | | |
137 | 139 | | |
| 140 | + | |
138 | 141 | | |
139 | 142 | | |
140 | 143 | | |
| |||
222 | 225 | | |
223 | 226 | | |
224 | 227 | | |
| 228 | + | |
225 | 229 | | |
226 | 230 | | |
227 | 231 | | |
| |||
693 | 697 | | |
694 | 698 | | |
695 | 699 | | |
| 700 | + | |
696 | 701 | | |
697 | 702 | | |
698 | 703 | | |
| |||
723 | 728 | | |
724 | 729 | | |
725 | 730 | | |
| 731 | + | |
726 | 732 | | |
727 | 733 | | |
728 | 734 | | |
| |||
965 | 971 | | |
966 | 972 | | |
967 | 973 | | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
968 | 978 | | |
969 | 979 | | |
970 | 980 | | |
| |||
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| 96 | + | |
| 97 | + | |
96 | 98 | | |
97 | 99 | | |
98 | 100 | | |
| |||
172 | 174 | | |
173 | 175 | | |
174 | 176 | | |
| 177 | + | |
175 | 178 | | |
176 | 179 | | |
177 | 180 | | |
| |||
332 | 335 | | |
333 | 336 | | |
334 | 337 | | |
| 338 | + | |
335 | 339 | | |
336 | 340 | | |
337 | 341 | | |
| |||
1792 | 1796 | | |
1793 | 1797 | | |
1794 | 1798 | | |
| 1799 | + | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
1795 | 1803 | | |
1796 | 1804 | | |
1797 | 1805 | | |
| |||
0 commit comments