Skip to content

Commit 534b935

Browse files
committed
feat: implement Aerospike update and teardown
Signed-off-by: Valentyn Kahamlyk <valentin.kagamlyk@gmail.com>
1 parent 4173e0a commit 534b935

1 file changed

Lines changed: 63 additions & 4 deletions

File tree

  • sdk/python/feast/infra/online_stores/aerospike_online_store

sdk/python/feast/infra/online_stores/aerospike_online_store/aerospike.py

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ def _convert_raw_docs_to_proto(
448448
results.append((ts, row_features))
449449
return results
450450

451+
# ------------------------------------------------------------------
452+
# Admin paths (update / teardown)
453+
# ------------------------------------------------------------------
451454
def update(
452455
self,
453456
config: RepoConfig,
@@ -457,14 +460,70 @@ def update(
457460
entities_to_keep: Sequence[Entity],
458461
partial: bool,
459462
) -> None:
460-
raise NotImplementedError("AerospikeOnlineStore.update is not implemented yet.")
463+
"""Reconcile per-feature-view data when a schema change is applied.
464+
465+
Aerospike has no explicit schema, and records/sets are created lazily
466+
on first write, so there is nothing to do for ``tables_to_keep`` or
467+
either of the entity lists. For ``tables_to_delete`` we strip each
468+
feature-view's slot out of the ``features`` and ``event_ts`` Map
469+
CDTs on every record in the project's set.
470+
471+
This is issued as a single **background scan** with a combined op
472+
list covering all feature views being removed, so the cost is a
473+
single server-side pass regardless of how many feature views are
474+
dropped. The scan runs asynchronously server-side and returns
475+
immediately; this matches the intent of ``feast apply``, after
476+
which the caller stops reading the dropped feature views anyway.
477+
"""
478+
if not isinstance(config.online_store, AerospikeOnlineStoreConfig):
479+
raise RuntimeError(f"{config.online_store.type = }. It must be aerospike.")
480+
if not tables_to_delete:
481+
return
482+
483+
client = self._get_client(config)
484+
ns = config.online_store.namespace
485+
set_name = self._set_name(config)
486+
487+
remove_ops: List[Dict[str, Any]] = []
488+
for fv in tables_to_delete:
489+
remove_ops.append(
490+
map_ops.map_remove_by_key(
491+
"features", fv.name, aerospike.MAP_RETURN_NONE
492+
)
493+
)
494+
remove_ops.append(
495+
map_ops.map_remove_by_key(
496+
"event_ts", fv.name, aerospike.MAP_RETURN_NONE
497+
)
498+
)
499+
500+
scan = client.scan(ns, set_name)
501+
scan.add_ops(remove_ops)
502+
scan.execute_background()
461503

462504
def teardown(
463505
self,
464506
config: RepoConfig,
465507
tables: Sequence[FeatureView],
466508
entities: Sequence[Entity],
467509
) -> None:
468-
raise NotImplementedError(
469-
"AerospikeOnlineStore.teardown is not implemented yet."
470-
)
510+
"""Truncate the project's set and close the cached client.
511+
512+
Uses Aerospike's ``truncate(namespace, set, 0)`` — a set-scoped
513+
metadata operation that clears every record in O(1) client time,
514+
cheaper than Mongo's ``collection.drop()``. Passing ``0`` as the
515+
cutoff means "drop everything regardless of last-update time".
516+
517+
Truncate on a non-existent set is a no-op, so calling ``teardown``
518+
on a project that never wrote data is safe.
519+
"""
520+
if not isinstance(config.online_store, AerospikeOnlineStoreConfig):
521+
raise RuntimeError(f"{config.online_store.type = }. It must be aerospike.")
522+
523+
client = self._get_client(config)
524+
ns = config.online_store.namespace
525+
set_name = self._set_name(config)
526+
client.truncate(ns, set_name, 0)
527+
if self._client is not None:
528+
self._client.close()
529+
self._client = None

0 commit comments

Comments
 (0)