---
title: "Entity and feature view record layout"
description: "How the Aerospike online store maps Feast entities and feature views to Aerospike records."
---

# Entity and feature view record layout

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

This page explains how the Aerospike online store maps Feast entities and feature views to records, and what that layout means for writes, reads, and TTL.

On the overview page, you saw that Feast copies feature values from an offline store into an online store through _materialization_, and reads them back with `get_online_features`. The Aerospike online store maps every feature view for one entity to a single Aerospike record.

In the logistics example, `driver_stats` holds recent delivery statistics and `pricing` holds the demand adjustment. The two teams update their views independently, while the assignment request reads both for the same driver.

You do not configure this layout directly. Your feature view definitions and `feast materialize` calls stay the same no matter how many feature views share an entity.

## Benefits and boundaries

Collocating feature views on one record per entity reduces the number of record keys and primary index (PI) entries you maintain as feature views grow. That is the main storage benefit of this layout.

The tradeoff is that independently updated feature views share one write target. A write that replaced the entire `features` map could replace another feature view’s values. The integration avoids that by using Map CDT operations that update only the targeted feature view’s map entries.

Collocated feature views also share a record-level time to live (TTL). Every write applies `ttl_seconds` in `feature_store.yaml` to the entire Aerospike record, not to an individual feature view. In this implementation, feature views on the same record do not expire independently at the storage layer.

`FeatureView.ttl` does not provide a separate expiry for standard Aerospike online reads. It controls Feast materialization lookback and historical point-in-time retrieval.

Feast still issues one `online_read` call per feature view. Each call reads from the shared entity record key, but collocation does not reduce the number of read calls.

## What the record holds

Each feature view gets its own key inside a shared map in the `features` and `event_ts` bins. A write to `driver_stats` touches only the `driver_stats` key and leaves `pricing` untouched. Understanding this structure explains what you see when you inspect a record directly in the verify step.

In Aerospike, data is organized as:

-   _Namespaces_ contain sets. A namespace is the top-level data container with its own storage policy (memory, flash, or hybrid).
-   _Sets_ contain records, similar to a table. Sets are optional, and records can exist without one.
-   _Records_ contain bins. Each record is identified by a key.
-   _Bins_ are named fields on a record, similar to columns. A bin can hold a scalar value or a collection type like a [Map CDT](https://aerospike.com/docs/server/guide/data-types/cdt-map).

Feast stores feature data in key-ordered Map CDT bins (`MAP_KEY_ORDERED`).

| Bin name | Contents |
| :-- | :-- |
| `features` | A Map CDT keyed by feature view name. Each value is a map of feature name to its stored value. |
| `event_ts` | A Map CDT keyed by feature view name. Each value is the event timestamp, in milliseconds since the Unix epoch, for that feature view’s most recently materialized row. |
| `created_ts` | A single write timestamp, present only when the offline source row includes a `created_timestamp` column. Not written on every record. |

All records for one Feast project live in the same Aerospike set by default, named `<project>_latest`. The Aerospike user key for each record is the entity’s join key serialized as a byte array by Feast (controlled by `entity_key_serialization_version`). If you inspect the key directly with `aql` or the Python client you see binary bytes, not the raw `driver_id` value. For a given `driver_id`, every feature view serializes the same join key to the same byte array, so the views write to the same record.

For example, a record holding features from `driver_stats` and `pricing` for one driver looks like this:

```text
bins:

  features:

    driver_stats:

      rating:        4.91

      trips_last_7d: 132

    pricing:

      surge_multiplier: 1.2

  event_ts:

    driver_stats: 1737374400000

    pricing:      1737447000000

  created_ts:      1737460805000    # only when offline rows include created_timestamp
```

For the full Aerospike-to-Feast mapping (namespace, set, key, and bins), see [Data model](https://github.com/feast-dev/feast/blob/master/docs/reference/online-stores/aerospike.md#data-model) in the Aerospike online store reference.

## How materialization writes work

Each `feast materialize` or `feast materialize-incremental` run writes only the feature view, or views, you specify. Because feature views share a record, the online store uses a _partial upsert_: an Aerospike Map CDT operation that updates only that feature view’s key inside the `features` and `event_ts` maps. Two materialization jobs writing different feature views for the same entity each mutate only their own map key.

## How reads work with a shared record

Feast calls `online_read` once per feature view when handling a `get_online_features` request. For requests containing at most `batch_max_records` entities (1,000 by default), each `online_read` issues one `batch_operate` that reads the requested feature view’s map key. Larger requests issue one `batch_operate` per entity chunk. Collocation does not combine the feature-view calls.

If you isolate a feature view onto its own Aerospike set using `set_overrides`, that view’s reads go to a different set. Reads spanning the isolated set and others then target multiple sets instead of one shared record.

## Namespace overrides and collocation

Collocation applies only within one namespace and set.

Feast can route feature views to different namespaces through `namespace_overrides` in `feature_store.yaml`. Once two feature views land on different namespaces, their records live in separate Aerospike namespaces. Feast still issues one `online_read` per feature view, with one or more `batch_operate` calls depending on entity count. Each call goes to the namespace where that view is stored. A `FeatureService` that spans namespaces is more resource-intensive than one limited to a single namespace.

See [Tutorial: Route hot and cold Feast features to memory and flash](https://aerospike.com/docs/develop/feast-aerospike-tiering) for how to configure and reason about namespace routing.

On the next page, you define a `driver` entity with two feature views and materialize them into Aerospike.

::: undefined
-   I understand the benefits and tradeoffs of storing multiple feature views on one Aerospike record.
-   I know how Map CDT partial upserts target one feature view at a time on a shared record.
:::

[Previous  
Install and connect](https://aerospike.com/docs/develop/feast-aerospike-online-store/step/1/part/1/install-and-connect) [Next  
Define and materialize feature views](https://aerospike.com/docs/develop/feast-aerospike-online-store/step/3/part/0/define-and-materialize)