Skip to content

Commit 1d016f6

Browse files
vkagamlykCopilot
andcommitted
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>
1 parent e70ba39 commit 1d016f6

2 files changed

Lines changed: 9 additions & 10 deletions

File tree

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,9 @@ def hook(
206206
207207
The hook MUST return a row list with the same shape as its input.
208208
Hooks that raise will fail the whole batch — there is no per-row
209-
fallback. The resolved callable is cached on the store instance, so
210-
swapping the config to a different hook also requires a new
211-
``AerospikeOnlineStore`` instance (which Feast already does on
212-
``RepoConfig`` change).
209+
fallback. The resolved callable is cached on the store instance; if the
210+
configured import string changes between calls, it is re-resolved
211+
automatically on the next write.
213212
"""
214213

215214
user: Optional[str] = None
@@ -318,6 +317,7 @@ def _get_client(self, config: RepoConfig) -> aerospike.Client:
318317
}
319318
batch_policy: Dict[str, Any] = {
320319
"total_timeout": store_cfg.batch_total_timeout_ms,
320+
"max_retries": store_cfg.max_retries,
321321
}
322322
if store_cfg.socket_timeout_ms is not None:
323323
# socket_timeout is the per-attempt deadline; without it,
@@ -666,7 +666,7 @@ def online_read(
666666
fv_event_ts_ms = bins.get("event_ts") if bins else None
667667
fv_features = self._normalize_projected_features(raw_features)
668668
docs[user_key] = {
669-
"features": {table.name: fv_features} if fv_features else {},
669+
"features": {table.name: fv_features} if fv_features is not None else {},
670670
"event_timestamps": {table.name: _epoch_ms_to_datetime(fv_event_ts_ms)},
671671
}
672672

@@ -719,7 +719,7 @@ def _normalize_projected_features(
719719
return raw
720720
if isinstance(raw, list):
721721
if not raw:
722-
return None
722+
return {}
723723
return dict(zip(raw[0::2], raw[1::2]))
724724
return None
725725

sdk/python/tests/unit/online_store/test_aerospike_online_retrieval.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def fake_batch_operate(keys, ops):
578578
_fake_batch_record(
579579
key,
580580
{
581-
"features": ["rating", 4.91, "trips_last_7d", 132],
581+
"features": ["rating", 4.91],
582582
"event_ts": _datetime_to_epoch_ms(ts),
583583
},
584584
)
@@ -595,8 +595,7 @@ def fake_batch_operate(keys, ops):
595595
ts_out, feats = results[0]
596596
assert ts_out == ts
597597
assert abs(feats["rating"].double_val - 4.91) < 1e-9
598-
assert feats["trips_last_7d"].int64_val == 132
599-
598+
assert feats["trips_last_7d"] == ValueProto()
600599
features_op = captured_ops[0]
601600
assert features_op["op"] == aerospike.OP_MAP_GET_BY_KEY_LIST
602601
assert features_op["bin"] == "features"
@@ -611,7 +610,7 @@ def test_normalize_projected_features_handles_all_payload_shapes():
611610
"""The shape of the ``features`` payload depends on which op produced it;
612611
the helper must accept all of them."""
613612
assert AerospikeOnlineStore._normalize_projected_features(None) is None
614-
assert AerospikeOnlineStore._normalize_projected_features([]) is None
613+
assert AerospikeOnlineStore._normalize_projected_features([]) == {}
615614
assert AerospikeOnlineStore._normalize_projected_features(["a", 1, "b", 2]) == {
616615
"a": 1,
617616
"b": 2,

0 commit comments

Comments
 (0)