feat: Bring Your Own Spark - SparkApplication - #6550
Conversation
…merged-spark-e2e
Keep feast-dev#6550 batching in materialize loops; skip FV state transition when _force_local so async /materialize-async (already MATERIALIZING) works. Co-authored-by: Cursor <cursoragent@cursor.com>
…merged-spark-e2e
Keep feast-dev#6550 batching; skip FV state transition when _force_local so /materialize-async (already MATERIALIZING) works with SparkApplication. Co-authored-by: Cursor <cursoragent@cursor.com>
…aterialization Adds a new batch compute engine that submits materialization jobs as SparkApplication CRDs via the Kubeflow Spark Operator. One 'feast materialize' call creates one SparkApplication pod that processes all feature views using distributed Spark, rather than running in-process on the Feast server. Key changes: - Refactor materialize()/materialize_incremental() to pass all tasks to the engine in a single batch call instead of looping per feature view. Existing engines are unaffected (base class loops tasks internally via _materialize_one). - Add public get_provider() method on FeatureStore. - New spark_application engine: config, compute, job, driver script, Dockerfile. - 12 unit tests covering config, validation, CR structure, state mapping, timeout, cleanup, and job naming. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
- Pod calls apply_materialization via gRPC after each successful FV, setting state to AVAILABLE_ONLINE. Server reads FV state post-completion to determine per-FV success/failure in batched SparkApplication runs. - registry_address is now mandatory (simplified from complex path heuristic). - Dockerfile rewritten to install feast from source (matches K8s engine pattern). - Unit tests updated: 15/15 pass (3 new tests for _build_per_fv_jobs). - E2E validated: 5 FVs x 9600 rows, 5 executors, all AVAILABLE_ONLINE. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
… stores - Config delivery: Secret → ConfigMap. Operator's ClusterRole already has full ConfigMap CRUD — avoids widening RBAC for Secrets in ODH. Matches KubernetesComputeEngine pattern. - Removed registry_address config field. Pod inherits server's registry config (SQL, Snowflake) directly and writes apply_materialization() to the same database. Eliminates TLS certificate mounting complexity. - Reject file-based offline stores (dask, file, duckdb) and registries (file) at __init__(), same as existing sqlite/faiss online store rejection. SparkApplication pod has ephemeral filesystem. - Dockerfile: added PYTHONPATH/SPARK_HOME for PySpark, added pymysql. - 20/20 unit tests pass. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
9578721 to
7a27217
Compare
Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
|
@aniketpalu lint checks are failing |
jyejare
left a comment
There was a problem hiding this comment.
While the core architecture is solid, there are several critical issues around thread safety, error handling, and resource management that need addressing before merge.
| @@ -424,6 +424,113 @@ def _get_provider(self) -> Provider: | |||
| # TODO: Bake self.repo_path into self.config so that we dont only have one interface to paths | |||
There was a problem hiding this comment.
[Critical] Race condition in state management
The state transition and rollback logic has potential race conditions when multiple materialization jobs run concurrently. The previous_states dictionary and feature view state updates are not atomic, which could lead to inconsistent states or lost rollbacks.
Suggested:
| # TODO: Bake self.repo_path into self.config so that we dont only have one interface to paths | |
| # Use locks or atomic operations for state management | |
| import threading | |
| state_lock = threading.Lock() | |
| with state_lock: | |
| for fv, job in zip(regular_fvs, jobs): | |
| fv_status = job.status() | |
| if fv_status == MaterializationJobStatus.ERROR: | |
| failed_fvs.append(fv) | |
| if first_error is None and job.error(): | |
| first_error = job.error() | |
| else: | |
| succeeded_fvs.append(fv) | |
| if failed_fvs: | |
| self._rollback_fv_states(failed_fvs, previous_states) |
There was a problem hiding this comment.
The threading race condition described isn't present because previous_states is a local variable (line 2551) and all loops (for fv, job in zip(...)) are sequential Python for loops, not threaded. Each materialize() call creates its own dict.
However, reviewing this code path revealed a shared-object bug in our engine's _build_per_fv_jobs. Previously, all succeeded FVs received the same SparkApplicationMaterializationJob reference. During _wait_for_completion, polling sets _error on that object when the SparkApp transitions to FAILED. Later, when feature_store.py calls job.status() for each FV, they all hit the if self._error is not None: return ERROR early-return, even for the FVs that actually succeeded and wrote AVAILABLE_ONLINE to the registry.
| "staging_location is configured but only used for " | ||
| "get_historical_features (not yet supported by this engine). " | ||
| "It will be ignored for materialize operations.", | ||
| stacklevel=2, | ||
| ) | ||
| for i, entry in enumerate(self.env): | ||
| if "name" not in entry: | ||
| raise ValueError( | ||
| f"env[{i}] is missing required 'name' key. " | ||
| f"Each env entry must have at least 'name' and 'value'. Got: {entry}" | ||
| ) |
There was a problem hiding this comment.
[Suggestion] Improve configuration validation
The env validation only checks for 'name' field but doesn't validate that 'value' exists or that the overall structure is correct for Kubernetes environment variables.
Suggested:
| "staging_location is configured but only used for " | |
| "get_historical_features (not yet supported by this engine). " | |
| "It will be ignored for materialize operations.", | |
| stacklevel=2, | |
| ) | |
| for i, entry in enumerate(self.env): | |
| if "name" not in entry: | |
| raise ValueError( | |
| f"env[{i}] is missing required 'name' key. " | |
| f"Each env entry must have at least 'name' and 'value'. Got: {entry}" | |
| ) | |
| for i, entry in enumerate(self.env): | |
| if not isinstance(entry, dict): | |
| raise ValueError( | |
| f"env[{i}] must be a dict, got {type(entry).__name__}: {entry}" | |
| ) | |
| if "name" not in entry: | |
| raise ValueError( | |
| f"env[{i}] is missing required 'name' key. " | |
| f"Each env entry must have at least 'name' and 'value'. Got: {entry}" | |
| ) | |
| if "value" not in entry and "valueFrom" not in entry: | |
| raise ValueError( | |
| f"env[{i}] must have either 'value' or 'valueFrom'. Got: {entry}" | |
| ) |
- Remove redundant get_provider(); callers use .provider property - Replace SparkSession monkey-patch with per-thread session binding - Smart retry: only 5xx/429; fail fast on 401/403 with RBAC hint - Retry ConfigMap + SparkApplication creation (transient K8s errors) - Validate env entries: require name + value or valueFrom (K8s EnvVar) - Independent per-FV job status via CompletedMaterializationJob - Exit 1 on any FV failure so SparkApp CR reflects partial failure - Cleanup logs include kubectl delete command for manual recovery - Lint: pragma allowlist on test fixture URL Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6550 +/- ##
==========================================
- Coverage 46.02% 46.00% -0.03%
==========================================
Files 402 406 +4
Lines 47656 48164 +508
Branches 6741 6814 +73
==========================================
+ Hits 21936 22157 +221
- Misses 24196 24461 +265
- Partials 1524 1546 +22
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
…) into merged-spark-e2e
Keep feast-dev#6550 batching; skip FV state transition when _force_local so /materialize-async (already MATERIALIZING) works with SparkApplication. Co-authored-by: Cursor <cursoragent@cursor.com>
| tqdm_builder=lambda length: tqdm(total=length, ncols=100), | ||
| ) | ||
|
|
||
| thread_store.registry.apply_materialization(fv, thread_store.project, start, end) |
There was a problem hiding this comment.
apply_materialization is called twice for each succeeded FV - once by the driver pod, once by the server after the job completes.
There was a problem hiding this comment.
Agreed, the double write was duplicating materialization intervals. The driver pod remains the owner of apply_materialization (needed for per-FV AVAILABLE_ONLINE / partial success). The server batch path now skips the second call when the engine sets applies_materialization=True (SparkApplication only). Other engines still apply on the server as before.
| job = SparkApplicationMaterializationJob( | ||
| job_id, self.config.namespace, self.custom_api | ||
| ) | ||
| self._wait_for_completion(job) |
There was a problem hiding this comment.
This should be async.
This blocks the Feast server thread for the entire duration of the Spark job (up to job_timeout_seconds, default 1 hour). For a server handling multiple clients, this is a scalability concern.
There was a problem hiding this comment.
Agreed this is a real scalability concern for the sync path: _wait_for_completion holds the caller until the SparkApplication finishes.
Making the engine poll itself async would redesign the ComputeEngine.materialize(). The intended non-blocking path is remote materialization in #6590 (POST /materialize-async + registry polling). We'll address true submit-without-wait server behavior there so the async path does not hold a worker thread for the full Spark job.
Would it be okay to leaving this as-is in #6550 and tracked against #6590?
Restore master's per-FV materialize path for engines that do not support batching; keep SparkApplication on the existing batch path via ComputeEngine.supports_batch. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Take supports_batch isolation; keep _force_local skip for remote async materialize on both batch and per-FV paths. Co-authored-by: Cursor <cursoragent@cursor.com>
… cleanup Replace __end_date__ sentinel with _MaterializationDateRange; fail fast if engine job count mismatches; map UNKNOWN SparkApp state to WAITING; always cleanup ConfigMap/CR after wait. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Defer kubeconfig load until materialize/cleanup so feast apply can construct the engine without a cluster. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
| # Roll back state to previous value on failure. | ||
| # batch_engine is on PassthroughProvider (concrete); same access as | ||
| # _submit_and_process_materialization_jobs via untyped provider. | ||
| if getattr(provider, "batch_engine").supports_batch: |
There was a problem hiding this comment.
fragile
batch_engine = getattr(provider, "batch_engine", None)
if batch_engine and getattr(batch_engine, "supports_batch", False):
Pod already writes watermarks via applies_materialization; server batch path skips the second call to avoid duplicate intervals. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Use null-safe getattr so materialize falls back to the per-FV path when batch_engine or supports_batch is absent. Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Null-safe supports_batch, skip duplicate apply_materialization, lazy K8s client, review hardening.
Bring in feast-dev#6550 SparkApplication (supports_batch / _materialize_fvs_batch). Resolve feature_store.py by keeping master batch structure and threading _force_local through transition/rollback/batch helpers so async remote-mat does not double-set MATERIALIZING or wrongly roll back state. Co-authored-by: Cursor <cursoragent@cursor.com>
Refresh with current feast-dev#6590 tip (includes master merge with feast-dev#6550). Keep spark_application Dockerfile postgres extra for E2E. Co-authored-by: Cursor <cursoragent@cursor.com>
# [0.65.0](v0.64.0...v0.65.0) (2026-07-20) ### Bug Fixes * add debug logging for FIPS mode detection fallback ([6c1b24e](6c1b24e)) * Build embedded UI from local source ([#6525](#6525)) ([3500349](3500349)) * Bump decommissioned Snowflake Python UDF runtime from 3.9 to 3.10 ([#6606](#6606)) ([#6608](#6608)) ([10341e4](10341e4)) * configure FIPS-compliant gRPC cipher suites for offline server ([6bc80a2](6bc80a2)) * Correct Flink PyArrow dependency constraints ([#6604](#6604)) ([70a9751](70a9751)) * Fix ValueError in signal handling for Trino worker threads ([#6428](#6428)) ([506d919](506d919)) * Fixed monitoring page issues ([7946018](7946018)) * Make pytest config compatible with newer pytest ([#5779](#5779)) ([a57ea33](a57ea33)) * Replace comma with space in DynamoDB-incompatible label tag value ([51e3a16](51e3a16)) * Resolve UI build warnings ([#6529](#6529)) ([abe92af](abe92af)) * Unblock nightly UI build ([#6570](#6570)) ([f296d4b](f296d4b)) * Use LONGBLOB for SQL registry proto columns on MySQL ([#6566](#6566)) ([7e4beb2](7e4beb2)) ### Features * Add click-to-zoom lightbox for blog post images ([#6575](#6575)) ([1cb23fd](1cb23fd)) * Add dark mode support to website and blog ([#6589](#6589)) ([7358fb8](7358fb8)) * Add OnlineStore for Aerospike ([#6532](#6532)) ([9cd35e1](9cd35e1)) * Add OpenLineage Consumer to Feast - receive, store, and visualize cross-producer lineage ([#6549](#6549)) ([a834126](a834126)) * Add registry list feature views by updated since ([#6092](#6092)) ([#6093](#6093)) ([006c606](006c606)) * Add ScyllaDB online store with vector search ([#6508](#6508)) ([1669661](1669661)) * Added compute and jobs UI ([ba2c05c](ba2c05c)) * Added Iceberg REST Catalog data source support ([e0a8573](e0a8573)) * Bring Your Own Spark - SparkApplication ([#6550](#6550)) ([dcd496f](dcd496f)) * **cassandra:** Add multi-DC support via per-datacenter execution profiles ([#6434](#6434)) ([0de9196](0de9196)) * Enhanced data source creation as a visual catalog with type-specific forms ([#6557](#6557)) ([d6acbba](d6acbba)) * Enhanced datasets UI functionality ([de11152](de11152)) * Implement RegistryServer.Proto RPC with RBAC-filtered response ([#6558](#6558)) ([#6552](#6552)) ([0d02614](0d02614)) * New zoned timestamp feature type ([#6536](#6536)) ([#6537](#6537)) ([eb042f0](eb042f0)) * **operator:** Auto-create RBAC for spark_application batch engine ([#6597](#6597)) ([f487b37](f487b37)) * **operator:** integrate cluster TLS profile for OCP 5.0 compliance ([43263a6](43263a6)) * Permissions CRUD UI and OIDC auth integration in UI ([6511da1](6511da1)) * Retrieve historical features from BigQuery without entity_df ([#6569](#6569)) ([cd5f6bb](cd5f6bb)), closes [#6558](#6558) [#6552](#6552) * **spark:** SparkSource query+path and pre-computed offline read for BatchFeatureView ([#6440](#6440)) ([4dc8757](4dc8757)) ### BREAKING CHANGES * total_timeout_ms is renamed to batch_total_timeout_ms. Config files using the old name must be updated. No default value change. Docs updated (reference + perf-tuning guide) with a short explainer on the per-attempt vs total deadline distinction. Two new unit tests pin the policy wiring: socket_timeout_ms propagates to all three scopes, and is omitted (not injected as None) when unset. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * refactor(aerospike): use MAP_KEY_ORDERED, KEY_DIGEST, and instance-scoped client Cheap-win cleanups flagged in review, all touching the same small patch of write-path and lifecycle code. * Map CDTs are now created with MAP_KEY_ORDERED. map_get_by_key / map_remove_by_key on an ordered map are O(log N) in the map size instead of O(N); matters on reads of wide feature views and on the update() background scan (which walks every record in the project's set). * Writes drop POLICY_KEY_SEND and rely on the client default (POLICY_KEY_DIGEST). The serialized entity key is no longer stored alongside each record, saving per-record storage the read path never consumes (batch_operate preserves request order; results are paired back by zip in online_read). * _client moves from a class attribute to an instance attribute (set in __init__). Previously two AerospikeOnlineStore instances could share the cached client through class state until one wrote self._client. With the instance attribute the state is always per-instance from construction. * Drop MongoDB references from class docstrings and comments (they referred to how the storage layout was derived rather than documenting current behavior). Also rewrite the _build_batch_writes docstring to describe the policies applied on the write path. Unit test assertions for the write-path record are updated: bw.policy is now None (client default applies) and map ops carry map_policy={'map_order': MAP_KEY_ORDERED}. All three docker-backed integration tests still pass end-to-end (cross-FV upsert, update() background scan, full feature-store round-trip), so the read/write shape survives the ordering and policy changes against a real server. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * feat(aerospike): add per-FV namespace/set overrides and prewriting hook Adds three configuration knobs to AerospikeOnlineStoreConfig: - namespace_overrides: pin individual feature views to a different Aerospike namespace (e.g. RAM-only vs. SSD-backed) without splitting the project across stores. - set_overrides: place a feature view in its own set so admin ops on it (truncate, scan-based deletes during `feast apply`) do not touch records of other views. - prewriting_hook: import-string-resolved callable invoked once per online_write_batch with the rows about to be written, returning the rows that actually go on the wire. Resolved and cached on first use; returning [] short-circuits the wire call. Read, write, update and teardown paths all honour the per-FV ns/set resolution. update() groups dropped feature views by their resolved (ns, set) pair and issues one background scan per group. teardown() truncates every unique (ns, set) pair the project may have written to, including the store-level default. Adds 22 unit tests for the new behaviour and updates 3 existing call sites of _build_batch_writes for the new namespace= parameter. Adds a sample hook module under examples/online_store/aerospike_overrides_and_hooks/ and corresponding sections in docs/reference/online-stores/aerospike.md. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * test: update aerospike image tag Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * chore: sync README template and secrets baseline after master merge Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * chore: fix secrets baseline line number for v1 operator types Adding aerospike to the feast-operator enum shifted the allowlisted SecretRef entry in api/v1/featurestore_types.go by one line. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * docs: update aerospike docs Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * fix(aerospike): wire batch max_retries and fix empty projection handling Copilot review feedback on PR #6532: - Add max_retries to the batch client policy (batch_operate/batch_write path) - Treat empty projected feature maps as present FV slots (is not None) - Return {} from _normalize_projected_features([]) instead of None - Fix projection unit test mock/assertions - Correct prewriting_hook config docstring Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * style(aerospike): format online_read docs assignment for ruff Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * chore: update pixi.lock for aerospike optional extra Regenerate the v6 lockfile with Pixi v0.63.1 after adding the aerospike extra to pyproject.toml. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * fix(aerospike): add client init lock and batch chunking Guard lazy client creation with a lock to avoid connection leaks under concurrent first use, and chunk batch reads/writes by batch_max_records so large materializations stay under Aerospike server batch limits. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com>
* feat: Add SparkApplicationComputeEngine for Kubernetes-native batch materialization Adds a new batch compute engine that submits materialization jobs as SparkApplication CRDs via the Kubeflow Spark Operator. One 'feast materialize' call creates one SparkApplication pod that processes all feature views using distributed Spark, rather than running in-process on the Feast server. Key changes: - Refactor materialize()/materialize_incremental() to pass all tasks to the engine in a single batch call instead of looping per feature view. Existing engines are unaffected (base class loops tasks internally via _materialize_one). - Add public get_provider() method on FeatureStore. - New spark_application engine: config, compute, job, driver script, Dockerfile. - 12 unit tests covering config, validation, CR structure, state mapping, timeout, cleanup, and job naming. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * feat: Add per-FV result reporting and clean up Dockerfile - Pod calls apply_materialization via gRPC after each successful FV, setting state to AVAILABLE_ONLINE. Server reads FV state post-completion to determine per-FV success/failure in batched SparkApplication runs. - registry_address is now mandatory (simplified from complex path heuristic). - Dockerfile rewritten to install feast from source (matches K8s engine pattern). - Unit tests updated: 15/15 pass (3 new tests for _build_per_fv_jobs). - E2E validated: 5 FVs x 9600 rows, 5 executors, all AVAILABLE_ONLINE. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * Minor lint & formatting change Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * feat: Switch to ConfigMap, remove registry_address, reject file-based stores - Config delivery: Secret → ConfigMap. Operator's ClusterRole already has full ConfigMap CRUD — avoids widening RBAC for Secrets in ODH. Matches KubernetesComputeEngine pattern. - Removed registry_address config field. Pod inherits server's registry config (SQL, Snowflake) directly and writes apply_materialization() to the same database. Eliminates TLS certificate mounting complexity. - Reject file-based offline stores (dask, file, duckdb) and registries (file) at __init__(), same as existing sqlite/faiss online store rejection. SparkApplication pod has ephemeral filesystem. - Dockerfile: added PYTHONPATH/SPARK_HOME for PySpark, added pymysql. - 20/20 unit tests pass. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * Minor formatting Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * fix: address PR feast-dev#6550 review — retry, validation, per-FV status - Remove redundant get_provider(); callers use .provider property - Replace SparkSession monkey-patch with per-thread session binding - Smart retry: only 5xx/429; fail fast on 401/403 with RBAC hint - Retry ConfigMap + SparkApplication creation (transient K8s errors) - Validate env entries: require name + value or valueFrom (K8s EnvVar) - Independent per-FV job status via CompletedMaterializationJob - Exit 1 on any FV failure so SparkApp CR reflects partial failure - Cleanup logs include kubectl delete command for manual recovery - Lint: pragma allowlist on test fixture URL Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * fix: isolate batch materialization to supports_batch engines Restore master's per-FV materialize path for engines that do not support batching; keep SparkApplication on the existing batch path via ComputeEngine.supports_batch. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * fix: address ntkathole review — dates dataclass, jobs check, UNKNOWN, cleanup Replace __end_date__ sentinel with _MaterializationDateRange; fail fast if engine job count mismatches; map UNKNOWN SparkApp state to WAITING; always cleanup ConfigMap/CR after wait. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * fix: lazy-init K8s client for spark_application engine Defer kubeconfig load until materialize/cleanup so feast apply can construct the engine without a cluster. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * chore: Refresh pixi.lock after pyproject.toml dependency changes Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * fix: skip duplicate apply_materialization for SparkApplication Pod already writes watermarks via applies_materialization; server batch path skips the second call to avoid duplicate intervals. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> * fix: harden supports_batch check against missing batch_engine Use null-safe getattr so materialize falls back to the per-FV path when batch_engine or supports_batch is absent. Signed-off-by: Aniket Paluskar <apaluska@redhat.com> --------- Signed-off-by: Aniket Paluskar <apaluska@redhat.com>
Bring in feast-dev#6550 SparkApplication (supports_batch / _materialize_fvs_batch). Resolve feature_store.py by keeping master batch structure and threading _force_local through transition/rollback/batch helpers so async remote-mat does not double-set MATERIALIZING or wrongly roll back state. Co-authored-by: Cursor <cursoragent@cursor.com>
# [0.65.0](feast-dev/feast@v0.64.0...v0.65.0) (2026-07-20) ### Bug Fixes * add debug logging for FIPS mode detection fallback ([6c1b24e](feast-dev@6c1b24e)) * Build embedded UI from local source ([feast-dev#6525](feast-dev#6525)) ([3500349](feast-dev@3500349)) * Bump decommissioned Snowflake Python UDF runtime from 3.9 to 3.10 ([feast-dev#6606](feast-dev#6606)) ([feast-dev#6608](feast-dev#6608)) ([10341e4](feast-dev@10341e4)) * configure FIPS-compliant gRPC cipher suites for offline server ([6bc80a2](feast-dev@6bc80a2)) * Correct Flink PyArrow dependency constraints ([feast-dev#6604](feast-dev#6604)) ([70a9751](feast-dev@70a9751)) * Fix ValueError in signal handling for Trino worker threads ([feast-dev#6428](feast-dev#6428)) ([506d919](feast-dev@506d919)) * Fixed monitoring page issues ([7946018](feast-dev@7946018)) * Make pytest config compatible with newer pytest ([feast-dev#5779](feast-dev#5779)) ([a57ea33](feast-dev@a57ea33)) * Replace comma with space in DynamoDB-incompatible label tag value ([51e3a16](feast-dev@51e3a16)) * Resolve UI build warnings ([feast-dev#6529](feast-dev#6529)) ([abe92af](feast-dev@abe92af)) * Unblock nightly UI build ([feast-dev#6570](feast-dev#6570)) ([f296d4b](feast-dev@f296d4b)) * Use LONGBLOB for SQL registry proto columns on MySQL ([feast-dev#6566](feast-dev#6566)) ([7e4beb2](feast-dev@7e4beb2)) ### Features * Add click-to-zoom lightbox for blog post images ([feast-dev#6575](feast-dev#6575)) ([1cb23fd](feast-dev@1cb23fd)) * Add dark mode support to website and blog ([feast-dev#6589](feast-dev#6589)) ([7358fb8](feast-dev@7358fb8)) * Add OnlineStore for Aerospike ([feast-dev#6532](feast-dev#6532)) ([9cd35e1](feast-dev@9cd35e1)) * Add OpenLineage Consumer to Feast - receive, store, and visualize cross-producer lineage ([feast-dev#6549](feast-dev#6549)) ([a834126](feast-dev@a834126)) * Add registry list feature views by updated since ([feast-dev#6092](feast-dev#6092)) ([feast-dev#6093](feast-dev#6093)) ([006c606](feast-dev@006c606)) * Add ScyllaDB online store with vector search ([feast-dev#6508](feast-dev#6508)) ([1669661](feast-dev@1669661)) * Added compute and jobs UI ([ba2c05c](feast-dev@ba2c05c)) * Added Iceberg REST Catalog data source support ([e0a8573](feast-dev@e0a8573)) * Bring Your Own Spark - SparkApplication ([feast-dev#6550](feast-dev#6550)) ([dcd496f](feast-dev@dcd496f)) * **cassandra:** Add multi-DC support via per-datacenter execution profiles ([feast-dev#6434](feast-dev#6434)) ([0de9196](feast-dev@0de9196)) * Enhanced data source creation as a visual catalog with type-specific forms ([feast-dev#6557](feast-dev#6557)) ([d6acbba](feast-dev@d6acbba)) * Enhanced datasets UI functionality ([de11152](feast-dev@de11152)) * Implement RegistryServer.Proto RPC with RBAC-filtered response ([feast-dev#6558](feast-dev#6558)) ([feast-dev#6552](feast-dev#6552)) ([0d02614](feast-dev@0d02614)) * New zoned timestamp feature type ([feast-dev#6536](feast-dev#6536)) ([feast-dev#6537](feast-dev#6537)) ([eb042f0](feast-dev@eb042f0)) * **operator:** Auto-create RBAC for spark_application batch engine ([feast-dev#6597](feast-dev#6597)) ([f487b37](feast-dev@f487b37)) * **operator:** integrate cluster TLS profile for OCP 5.0 compliance ([43263a6](feast-dev@43263a6)) * Permissions CRUD UI and OIDC auth integration in UI ([6511da1](feast-dev@6511da1)) * Retrieve historical features from BigQuery without entity_df ([feast-dev#6569](feast-dev#6569)) ([cd5f6bb](feast-dev@cd5f6bb)), closes [feast-dev#6558](feast-dev#6558) [feast-dev#6552](feast-dev#6552) * **spark:** SparkSource query+path and pre-computed offline read for BatchFeatureView ([feast-dev#6440](feast-dev#6440)) ([4dc8757](feast-dev@4dc8757)) ### BREAKING CHANGES * total_timeout_ms is renamed to batch_total_timeout_ms. Config files using the old name must be updated. No default value change. Docs updated (reference + perf-tuning guide) with a short explainer on the per-attempt vs total deadline distinction. Two new unit tests pin the policy wiring: socket_timeout_ms propagates to all three scopes, and is omitted (not injected as None) when unset. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * refactor(aerospike): use MAP_KEY_ORDERED, KEY_DIGEST, and instance-scoped client Cheap-win cleanups flagged in review, all touching the same small patch of write-path and lifecycle code. * Map CDTs are now created with MAP_KEY_ORDERED. map_get_by_key / map_remove_by_key on an ordered map are O(log N) in the map size instead of O(N); matters on reads of wide feature views and on the update() background scan (which walks every record in the project's set). * Writes drop POLICY_KEY_SEND and rely on the client default (POLICY_KEY_DIGEST). The serialized entity key is no longer stored alongside each record, saving per-record storage the read path never consumes (batch_operate preserves request order; results are paired back by zip in online_read). * _client moves from a class attribute to an instance attribute (set in __init__). Previously two AerospikeOnlineStore instances could share the cached client through class state until one wrote self._client. With the instance attribute the state is always per-instance from construction. * Drop MongoDB references from class docstrings and comments (they referred to how the storage layout was derived rather than documenting current behavior). Also rewrite the _build_batch_writes docstring to describe the policies applied on the write path. Unit test assertions for the write-path record are updated: bw.policy is now None (client default applies) and map ops carry map_policy={'map_order': MAP_KEY_ORDERED}. All three docker-backed integration tests still pass end-to-end (cross-FV upsert, update() background scan, full feature-store round-trip), so the read/write shape survives the ordering and policy changes against a real server. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * feat(aerospike): add per-FV namespace/set overrides and prewriting hook Adds three configuration knobs to AerospikeOnlineStoreConfig: - namespace_overrides: pin individual feature views to a different Aerospike namespace (e.g. RAM-only vs. SSD-backed) without splitting the project across stores. - set_overrides: place a feature view in its own set so admin ops on it (truncate, scan-based deletes during `feast apply`) do not touch records of other views. - prewriting_hook: import-string-resolved callable invoked once per online_write_batch with the rows about to be written, returning the rows that actually go on the wire. Resolved and cached on first use; returning [] short-circuits the wire call. Read, write, update and teardown paths all honour the per-FV ns/set resolution. update() groups dropped feature views by their resolved (ns, set) pair and issues one background scan per group. teardown() truncates every unique (ns, set) pair the project may have written to, including the store-level default. Adds 22 unit tests for the new behaviour and updates 3 existing call sites of _build_batch_writes for the new namespace= parameter. Adds a sample hook module under examples/online_store/aerospike_overrides_and_hooks/ and corresponding sections in docs/reference/online-stores/aerospike.md. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * test: update aerospike image tag Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * chore: sync README template and secrets baseline after master merge Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * chore: fix secrets baseline line number for v1 operator types Adding aerospike to the feast-operator enum shifted the allowlisted SecretRef entry in api/v1/featurestore_types.go by one line. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * docs: update aerospike docs Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * fix(aerospike): wire batch max_retries and fix empty projection handling Copilot review feedback on PR feast-dev#6532: - Add max_retries to the batch client policy (batch_operate/batch_write path) - Treat empty projected feature maps as present FV slots (is not None) - Return {} from _normalize_projected_features([]) instead of None - Fix projection unit test mock/assertions - Correct prewriting_hook config docstring Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * style(aerospike): format online_read docs assignment for ruff Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * chore: update pixi.lock for aerospike optional extra Regenerate the v6 lockfile with Pixi v0.63.1 after adding the aerospike extra to pyproject.toml. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com> * fix(aerospike): add client init lock and batch chunking Guard lazy client creation with a lock to avoid connection leaks under concurrent first use, and chunk batch reads/writes by batch_max_records so large materializations stay under Aerospike server batch limits. Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com>
What this PR does / why we need it:
Core changes
Upstream (feature_store.py):
New engine (spark_application/):
Design decisions
Validated on
Test plan
Which issue(s) this PR fixes:
Checks
git commit -s)Testing Strategy
Misc