官方更新 AWS ML Blog Fri, 28 Aug 2026 正文 ok 2125 words

Amazon SageMaker Feature Store 支持批量写入和记录发现

英文标题:Batch write and discover records in Amazon SageMaker Feature Store

Amazon SageMaker Feature Store 新增 BatchWriteRecord 和 ListRecords 两个 API。BatchWriteRecord 可在一次调用中跨多个特征组写入最多 25 条记录,支持部分成功返回、逐记录 TTL 设置,并保留基于 EventTime 的排序语义。ListRecords 支持通过分页枚举特征组中的记录标识符,覆盖 Standard 和 In-Memory 存储层,解决内存存储层记录无法发现和清理的问题。对开发者而言,这些能力可提升高吞吐特征摄取效率,并改善记录恢复、运维和合规删除流程。
打开原文相关 1.8 · 机会 3
media1.3 MB media101.1 KB media124.1 KB media345.0 KB media141.2 KB media346.5 KB media29.5 KB media84.3 KB media28.5 KB media35.2 KB media157.7 KB

中文

Amazon SageMaker Feature Store 是一个全托管、专为机器学习(ML)模型特征存储、共享和管理而构建的存储库。它为实时推理提供低延迟在线服务,为历史保留和训练特征数据提供离线存储,并支持流式和批量摄取模式。

随着 ML 平台日趋成熟,两个运营缺口反复出现。首先,运行高吞吐特征管道的团队必须循环调用 PutRecord(该 API 每次向在线存储写入一条特征记录)。这意味着每条记录、每个特征组都需要一次 API 调用,从而带来连接开销和较差的吞吐量。一个每秒摄取 10,000 条记录、涉及五个特征组的欺诈检测管道,仅为保持特征最新,就必须持续每秒发起 50,000 次独立 API 调用。第二个挑战是,使用 In-Memory 存储层的团队无法浏览或枚举在线存储中的记录。如果记录标识符因 bug 或管道故障而丢失,这些记录将永久无法恢复。In-Memory 层没有可供回退的离线存储,没有可执行的 Amazon Athena 查询,也没有用于发现现有记录的 API。

今天,我们宣布为 Amazon SageMaker Feature Store 推出两个新 API:

  1. BatchWriteRecord — 在一次 API 调用中跨多个特征组写入最多 25 条记录,支持部分成功语义、逐记录生存时间(TTL)控制,以及与 PutRecord 相同的基于 EventTime 的排序保证。
  2. ListRecords — 通过分页枚举特征组内的记录标识符。适用于 Standard(由 Amazon DynamoDB 支持)和 In-Memory(由 Redis 支持)两种存储层。

本文将通过代码示例介绍每个 API,帮助你快速上手。

前提条件

要跟随本文中的示例操作,你需要:

BatchWriteRecord

BatchWriteRecord API 旨在解决单记录摄取的吞吐量限制。以下章节将说明它解决的问题及其工作方式。

单记录摄取的挑战

Feature Store 现有的 PutRecord API 每次调用向一个特征组写入一条记录。每次调用都会执行条件写入:仅当请求中包含的 EventTime 晚于现有记录时,该记录才会作为“最新”版本持久化。如果条件不满足,该记录仍会作为历史版本写入离线存储。

这一设计提供了强大的排序保证,但在大规模场景下会迫使系统采用 N×M 调用模式(N 条记录 × M 个特征组),造成连接开销和尾延迟,从而限制吞吐量。

BatchWriteRecord 的工作方式

BatchWriteRecord 可在单个请求中接收最多 25 个条目,并可同时面向一个或多个特征组。每条记录独立成功或失败。这是一个部分成功 API,即单条记录失败不会导致整个请求失败。

该 API 保留与 PutRecord 相同的基于 EventTime 的排序语义:

  • 如果传入记录的 EventTime 晚于现有记录,它将成为在线存储中的最新版本。
  • 如果不是,该记录会作为历史版本写入离线存储(适用于启用了离线存储的特征组)。
  • 因其他原因失败的记录(如身份验证/验证错误、服务限流)将在响应中返回错误详情及原始记录。
  • 未处理的请求将在响应中作为 UnprocessedEntries 返回,可以重试。

请求结构

{
    "Entries": [
        {
            "FeatureGroupName": "click-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "click_count", "ValueAsString": "42"}
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
            "TtlDuration": {"Unit": "Days", "Value": 7}
        },
        {
            "FeatureGroupName": "login-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-456"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:01Z"},
                {"FeatureName": "login_count", "ValueAsString": "18"}
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"]
        }
    ]
}

响应仅返回写入失败的记录:

{
    "Errors": [
        {
            "Entry": {
                "FeatureGroupName": "string",
                "Record": [
                    {
                        "FeatureName": "string",
                        "ValueAsString": "string",
                        "ValueAsStringList": ["string"]
                    }
                ],
                "TargetStores": ["string"],
                "TtlDuration": {
                    "Unit": "string",
                    "Value": number
                }
            },
            "ErrorCode": "string",
            "ErrorMessage": "string"
        }
    ],
    "UnprocessedEntries": [
        {
            "FeatureGroupName": "string",
            "Record": [
                {
                    "FeatureName": "string",
                    "ValueAsString": "string",
                    "ValueAsStringList": ["string"]
                }
            ],
            "TargetStores": ["string"],
            "TtlDuration": {
                "Unit": "string",
                "Value": number
            }
        }
    ]
}

未列在 Errors 或 UnprocessedEntries 中的记录均已成功写入。对于可重试错误,你的应用程序应仅重试失败记录,并使用指数退避策略。

代码示例:使用 Boto3 进行批量摄取

import boto3

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

response = featurestore_runtime.batch_write_record(
    Entries=[
        {
            "FeatureGroupName": "click-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "click_count", "ValueAsString": "42"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        },
        {
            "FeatureGroupName": "login-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-456"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:01Z"},
                {"FeatureName": "login_count", "ValueAsString": "18"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        },
    ]
)

if response["Errors"]:
    for error in response["Errors"]:
        print(f"Record {error['Entry']}, ErrorCode: {error['ErrorCode']} Failed: {error['ErrorMessage']}")

if response["UnprocessedEntries"]:
    for unprocessed in response["UnprocessedEntries"]:
        print(f"Unprocessed: {unprocessed['FeatureGroupName']}")

if not response["Errors"] and not response["UnprocessedEntries"]:
    print("All records written successfully.")

代码示例:跨多个特征组写入

你可以在单个请求中指定多个特征组。记录按特征组分组并独立处理:

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

response = featurestore_runtime.batch_write_record(
    Entries=[
        {
            "FeatureGroupName": "user-profile-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "age", "ValueAsString": "34"},
                {"FeatureName": "region", "ValueAsString": "us-west-2"},
            ],
            "TargetStores": ["OnlineStore"],
        },
        {
            "FeatureGroupName": "click-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "click_count", "ValueAsString": "42"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        },
    ]
)

一个特征组中的写入失败不会影响发送到其他特征组的记录。

TTL(生存时间)支持

BatchWriteRecord 支持 TTL,其优先级分为以下三个层级,按优先级由高到低排列:

  1. 记录级 TTL — 在单个条目中通过 TtlDuration 设置。优先级最高。
  2. 请求级 TTL — 在请求顶层设置默认 TtlDuration,应用于未设置记录级 TTL 的条目。
  3. 特征组级 TTL — 配置在特征组本身的 TTL;当未设置记录级 TTL 和请求级 TTL 时应用。

关键注意事项

每个请求最多 25 个条目。该限制适用于单个请求中所有特征组的条目总数。

部分成功语义:与事务型 API 不同,如果某些记录失败,BatchWriteRecord 不会回滚已成功的写入。你的重试逻辑应仅重新提交 Errors 中返回的记录。

与 PutRecord 类似的 IAM 模型:调用方必须拥有每个目标特征组 Amazon Resource Name(ARN)上的 sagemaker:BatchWriteRecord 和 sagemaker:PutRecord 权限。系统会在处理前检查每个特征组的授权。

保留 EventTime 排序:BatchWriteRecord 使用条件写入来维持与 PutRecord 相同的“最新记录胜出”语义。过期记录无法覆盖在线存储中更新的记录。

TargetStores 灵活性:每个条目可独立指定 OnlineStore、OfflineStore 或两者(默认使用特征组已启用的存储),让你能够细粒度控制每条记录的写入位置。

ListRecords

ListRecords API 弥补了两种存储层中记录发现能力的缺口。以下章节将说明它解决的问题及其工作方式。

记录发现的挑战

Feature Store 支持 PutRecord、GetRecord 和 DeleteRecord,但这些 API 都要求调用方准确知道记录标识符。此前没有可浏览或枚举特征组内记录的 API。

对于 Standard 层,变通方法是使用 Amazon Athena 查询离线存储。这要求配置离线存储,会增加成本,并且无法提供实时结果。

对于 In-Memory 层,情况则更为严重。默认情况下它没有相应的离线存储。如果记录标识符丢失,这些记录将完全无法恢复。你无法发现它们,也无法删除它们。这会导致幽灵数据、存储成本浪费,并会在数据主体提出删除请求时带来潜在合规风险。

ListRecords 的工作方式

ListRecords 通过分页枚举特征组内的记录标识符。它仅返回活跃、未删除、未过期,且可供 GetRecord 或 DeleteRecord 使用的记录。

该 API 适用于两种存储层:

  • Standard 层([Amazon DynamoDB](/dy

English / 原文

Amazon SageMaker Feature Store is a fully managed, purpose-built repository to store, share, and manage features for machine learning (ML) models. It provides low-latency online serving for real-time inference, an offline store for historical retention and training feature data, and supports both streaming and batch ingestion patterns.

As ML platforms mature, two operational gaps surface repeatedly. First, teams running high-throughput feature pipelines must call PutRecord (which writes a single feature record to the online store) in a loop. This means one API call per record, per feature group, which creates connection overhead and poor throughput. A fraud-detection pipeline ingesting 10,000 records per second across five feature groups must sustain 50,000 individual API calls per second only to keep features current. A second challenge is that teams using the In-Memory storage tier have no way to browse or enumerate records stored in the online store. If record identifiers are lost through a bug or pipeline failure, those records become permanently unrecoverable. There is no offline store for the In-Memory tier to fall back on, no Amazon Athena query to run, and no API to discover what exists.

Today, we are announcing two new APIs for Amazon SageMaker Feature Store:

  1. BatchWriteRecord — Write up to 25 records across multiple feature groups in a single API call, with partial-success semantics, per-record time-to-live (TTL) control, and the same EventTime-based ordering guarantees as PutRecord.
  2. ListRecords — Enumerate record identifiers within a feature group using pagination. Works with both Standard (Amazon DynamoDB-backed) and In-Memory (Redis-backed) storage tiers.

In this post, we walk through each API with code examples you can use to get started.

Prerequisites

To follow along with the examples in this post, you need:

BatchWriteRecord

The BatchWriteRecord API tackles the throughput limits of single-record ingestion. The following sections explain the problem it solves and how it works.

The challenge with single-record ingestion

The existing PutRecord API in Feature Store writes one record to one feature group per call. Each call performs a conditional write: the record is persisted as the “latest” version only if its EventTime, included in the request, is newer than the existing record. If the condition fails, the record is still written as a historical version for the offline store.

This design provides strong ordering guarantees, but at scale it forces an N×M calling pattern (N records × M feature groups), creating connection overhead and tail latency that limit throughput.

How BatchWriteRecord works

BatchWriteRecord accepts up to 25 entries in a single request, targeting one or more feature groups simultaneously. Each record succeeds or fails independently. This is a partial-success API, meaning individual record failures do not fail the entire request.

The API preserves the same EventTime-based ordering as PutRecord:

  • If the incoming record’s EventTime is newer than the existing record, it becomes the latest version in the online store.
  • If not, the record is written as a historical version to the offline store (for feature groups with offline storage).
  • Records that fail for other reasons (authentication/validation errors, service throttling) are returned in the response with error details and the original record.
  • The requests that are unprocessed will be returned in response as UnprocessedEntries which can be retried.

Request structure

{
    "Entries": [
        {
            "FeatureGroupName": "click-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "click_count", "ValueAsString": "42"}
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
            "TtlDuration": {"Unit": "Days", "Value": 7}
        },
        {
            "FeatureGroupName": "login-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-456"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:01Z"},
                {"FeatureName": "login_count", "ValueAsString": "18"}
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"]
        }
    ]
}

The response returns only the records that failed:

{
    "Errors": [
        {
            "Entry": {
                "FeatureGroupName": "string",
                "Record": [
                    {
                        "FeatureName": "string",
                        "ValueAsString": "string",
                        "ValueAsStringList": ["string"]
                    }
                ],
                "TargetStores": ["string"],
                "TtlDuration": {
                    "Unit": "string",
                    "Value": number
                }
            },
            "ErrorCode": "string",
            "ErrorMessage": "string"
        }
    ],
    "UnprocessedEntries": [
        {
            "FeatureGroupName": "string",
            "Record": [
                {
                    "FeatureName": "string",
                    "ValueAsString": "string",
                    "ValueAsStringList": ["string"]
                }
            ],
            "TargetStores": ["string"],
            "TtlDuration": {
                "Unit": "string",
                "Value": number
            }
        }
    ]
}

Records not listed in Errors or UnprocessedEntries succeeded. Your application should retry only the failed records using exponential backoff for retriable errors.

Code example: Batch ingestion with Boto3

import boto3

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

response = featurestore_runtime.batch_write_record(
    Entries=[
        {
            "FeatureGroupName": "click-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "click_count", "ValueAsString": "42"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        },
        {
            "FeatureGroupName": "login-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-456"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:01Z"},
                {"FeatureName": "login_count", "ValueAsString": "18"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        },
    ]
)

if response["Errors"]:
    for error in response["Errors"]:
        print(f"Record {error['Entry']}, ErrorCode: {error['ErrorCode']} Failed: {error['ErrorMessage']}")

if response["UnprocessedEntries"]:
    for unprocessed in response["UnprocessedEntries"]:
        print(f"Unprocessed: {unprocessed['FeatureGroupName']}")

if not response["Errors"] and not response["UnprocessedEntries"]:
    print("All records written successfully.")

Code example: Writing across multiple feature groups

You can target multiple feature groups in a single request. Records are grouped by feature group and processed independently:

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

response = featurestore_runtime.batch_write_record(
    Entries=[
        {
            "FeatureGroupName": "user-profile-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "age", "ValueAsString": "34"},
                {"FeatureName": "region", "ValueAsString": "us-west-2"},
            ],
            "TargetStores": ["OnlineStore"],
        },
        {
            "FeatureGroupName": "click-features",
            "Record": [
                {"FeatureName": "user_id", "ValueAsString": "user-123"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T12:00:00Z"},
                {"FeatureName": "click_count", "ValueAsString": "42"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        },
    ]
)

A failure in one feature group does not affect records destined for other feature groups.

TTL (Time-to-Live) support

BatchWriteRecord supports TTL at three levels of precedence, shown in the following priority order:

  1. Record-level TTL — Set with TtlDuration on individual entries. Takes highest priority.
  2. Request-level TTL — A default TtlDuration at the top level of the request, applied to entries without a record-level TTL.
  3. Feature-group-level TTL — The TTL configured on the feature group itself, applied when neither record-level nor request-level TTL is set.

Key considerations

Maximum 25 entries per request. This limit applies to the total number of entries across all feature groups in a single request.

Partial-success semantics: Unlike transactional APIs, BatchWriteRecord does not roll back successful writes if some records fail. Design your retry logic to re-submit only the records returned in Errors.

Similar IAM model as PutRecord: The caller must have sagemaker:BatchWriteRecord and sagemaker:PutRecord permission on the Amazon Resource Name (ARN) of each target feature group. Per-feature-group authorization is checked before processing.

EventTime ordering is preserved: BatchWriteRecord uses conditional writes to maintain the same latest-record-wins semantics as PutRecord. A stale record cannot overwrite a newer one in the online store.

TargetStores flexibility: Each entry can independently target OnlineStore, OfflineStore, or both (defaults to the feature group’s enabled stores), giving you fine-grained control over where each record lands.

ListRecords

The ListRecords API closes the gap in record discovery for both storage tiers. The following sections explain the problem it solves and how it works.

The challenge with record discovery

Feature Store supports PutRecord, GetRecord, and DeleteRecord, but all require the caller to know the exact record identifier. There is no API to browse or enumerate records within a feature group.

For the Standard tier, the workaround is querying the offline store by using Amazon Athena. This requires offline store configuration, adds cost, and is not real-time.

For the In-Memory tier, the situation is critical. There is no corresponding offline store by default. If record identifiers are lost, those records are completely unrecoverable. You cannot discover them, and you cannot delete them. This leads to phantom data, wasted storage costs, and potential compliance risks when data subjects request deletion.

How ListRecords works

ListRecords enumerates record identifiers within a feature group using pagination. It returns only active, non-deleted, non-expired records that are ready to be used with GetRecord or DeleteRecord.

The API works with both storage tiers:

  • Standard tier (Amazon DynamoDB): Scans the online store, returning identifier of the latest version of each record. Soft-deleted and expired records are automatically excluded.
  • In-Memory tier (Redis): Scans keys and filters out soft-deleted records and internal system keys. Returns record identifiers extracted from key names.

Request and response structure

POST /FeatureGroup/{FeatureGroupName}/ListRecords

Request body:

Initial call

Or

{
    "MaxResults": 50,
    "NextToken": "eyJjdXJzb3IiOi4uLn0="
}

Response:

{
    "RecordIdentifiers": [
        "user-001",
        "user-002",
        "user-003"
    ],
    "NextToken": "eyJuZXh0IjoiLi4ufQ=="
}

When NextToken is absent in the response, pagination is complete.

Code example: Enumerate all records in a feature group

import boto3

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

all_identifiers = []
next_token = None

while True:
    params = {
        "FeatureGroupName": "user-profile-features",
        "MaxResults": 100,
    }
    if next_token:
        params["NextToken"] = next_token

    response = featurestore_runtime.list_records(**params)
    all_identifiers.extend(response["RecordIdentifiers"])

    next_token = response.get("NextToken")
    if not next_token:
        break

print(f"Found {len(all_identifiers)} active records.")

Code example: Clean up orphaned records

A common use case is identifying and deleting records that are no longer needed. This is critical for In-Memory tier feature groups, where orphaned records persist indefinitely:

import boto3

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

# Step 1: Enumerate all record identifiers
all_ids = []
next_token = None
while True:
    params = {"FeatureGroupName": "session-features", "MaxResults": 100}
    if next_token:
        params["NextToken"] = next_token
    response = featurestore_runtime.list_records(**params)
    all_ids.extend(response["RecordIdentifiers"])
    next_token = response.get("NextToken")
    if not next_token:
        break

# Step 2: Compare against your application's active session list
active_sessions = get_active_sessions()  # Your application logic
orphaned = [rid for rid in all_ids if rid not in active_sessions]

# Step 3: Delete orphaned records
for record_id in orphaned:
    featurestore_runtime.delete_record(
        FeatureGroupName="session-features",
        RecordIdentifierValueAsString=record_id,
        EventTime="2026-06-05T12:00:00Z",
    )

print(f"Deleted {len(orphaned)} orphaned records.")
  • Page size: Configurable through MaxResults (default 10, maximum 100).
  • Token format: Opaque, encrypted string. Do not parse or construct tokens. Pass them through unchanged.
  • Ordering: Results are not guaranteed to be in any particular order.
  • Concurrent writes: If records are written or deleted during pagination, you may observe duplicates or gaps. This is documented behavior.
  • Token scope: Tokens are tied to a specific feature group and account and cannot be reused across either.

Key considerations

Record identifiers only. The current release returns record identifiers without feature values. Use GetRecord or BatchGetRecord to retrieve full records for the identifiers you need.

Automatic filtering. The API excludes soft-deleted records, expired records (Standard tier TTL), and internal system keys (In-Memory tier). You see only active, retrievable records.

IAM permission. The caller must have sagemaker:ListRecords permission on the feature group ARN.

Both tiers supported. ListRecords works identically from the caller’s perspective regardless of whether the feature group uses Standard or In-Memory storage.

Putting it together

These two APIs complement each other naturally. Consider a compliance workflow that verifies complete data deletion for a user across multiple feature groups:

import boto3

featurestore_runtime = boto3.client("sagemaker-featurestore-runtime")

feature_groups = ["user-profiles", "click-history", "purchase-signals"]
user_to_delete = "user-789"

# Step 1: Find and delete the user across all feature groups
for fg_name in feature_groups:
    all_ids = []
    next_token = None
    while True:
        params = {"FeatureGroupName": fg_name, "MaxResults": 100}
        if next_token:
            params["NextToken"] = next_token
        response = featurestore_runtime.list_records(**params)
        all_ids.extend(response["RecordIdentifiers"])
        next_token = response.get("NextToken")
        if not next_token:
            break

    if user_to_delete in all_ids:
        featurestore_runtime.delete_record(
            FeatureGroupName=fg_name,
            RecordIdentifierValueAsString=user_to_delete,
            EventTime="2026-06-05T23:59:59Z",
        )
        print(f"Deleted '{user_to_delete}' from {fg_name}")

# Step 2: Log the deletion event using BatchWriteRecord
featurestore_runtime.batch_write_record(
    Entries=[
        {
            "FeatureGroupName": "deletion-audit-log",
            "Record": [
                {"FeatureName": "request_id", "ValueAsString": "del-001"},
                {"FeatureName": "event_time", "ValueAsString": "2026-06-05T23:59:59Z"},
                {"FeatureName": "user_id", "ValueAsString": user_to_delete},
                {"FeatureName": "status", "ValueAsString": "completed"},
                {"FeatureName": "feature_groups_cleaned", "ValueAsString": "3"},
            ],
            "TargetStores": ["OnlineStore", "OfflineStore"],
        }
    ]
)

Cleanup

To avoid ongoing charges, delete feature groups you created while following this walkthrough. For In-Memory tier feature groups, use ListRecords to enumerate records and DeleteRecord to remove them before deleting the feature group.

Conclusion

BatchWriteRecord and ListRecords provide key enhancements in the data plane of Amazon SageMaker Feature Store. BatchWriteRecord reduces the API call volume for high-throughput ingestion by up to 25x while preserving the EventTime-based ordering guarantees that keep your online store correct. ListRecords unlocks record discovery and lifecycle management. This is critical for In-Memory tier customers who previously had no way to enumerate or clean up their data.

Together, these APIs support patterns that were previously difficult or impossible: bulk ingestion pipelines with fewer connections and lower latency, compliance workflows that can verify complete data deletion, and operational tooling that can browse feature group contents in real time.

For more information, see the Feature Store documentation, the Feature Store API reference, the offline store configuration documentation, and the What’s New announcement.

For background on Feature Store capabilities, explore these related posts:


About the authors

Harshil Shah

Harshil is a Senior Solutions Architect at AWS with a deep passion for modernizing customer applications. He works with media and entertainment customers to help them build and integrate AI into their existing tech stacks.

Dhaval Shah

Dhaval is a Senior Solutions Architect at AWS. He works with customers to design and build production ML systems, with a focus on feature engineering, generative AI, and scalable data architectures.

Chirag Pandey

Chirag is a software engineer at AWS interested in building reliable and scalable infrastructure for AI/ML workloads.

Siamak Nariman

Siamak is a Senior Product Manager at AWS. He is focused on AI/ML technology, ML model management, and ML governance to improve overall organizational efficiency and productivity. He has extensive experience automating processes and deploying various technologies.