DynamoDB Streams: ExpiredIteratorException
TL;DR — 来自 GetShardIterator 的分片迭代器只有 15 分钟有效。如果你的消费者暂停了、缓慢地处理了一批数据,或者跨重启持有了一个迭代器,下一次 GetRecords 调用就会抛出它。恢复方法是请求一个新迭代器——用 AFTER_SEQUENCE_NUMBER 配上你处理过的最后一个序列号,这样你就能从中断处精确地继续。
含义
ExpiredIteratorException: The provided iterator exceeds the maximum age allowed.直接读取一个 DynamoDB 流是一个两步循环:GetShardIterator 给你一个指向某个分片的位置句柄,而每次 GetRecords 调用会消耗它并返回下一个(NextShardIterator)。这些句柄中的每一个都会在发出后 15 分钟过期。这个异常并不意味着数据没了——流记录会保留 24 小时——它只意味着这个_位置句柄_陈旧了,你需要一个新的。
为什么会发生
- 两次轮询之间处理缓慢——每批所做的工作(写入、外部调用)在下一次
GetRecords之前花了超过 15 分钟。 - 一个暂停或挂起的消费者——一个停止的 worker、一次漫长的部署,或一个持有循环的调试器会话。
- 存储迭代器——把迭代器字符串(在某个数据库或检查点中)持久化并稍后重用;迭代器是短暂的,序列号则不是。
- 退避拖得太久——一个错误重试循环睡过了那个时间窗口。
如何修复
捕获它并从你上一个检查点重新播种迭代器——跟踪你处理过的最后一条记录的
SequenceNumber,然后:const {ShardIterator} = await streams.send( new GetShardIteratorCommand({ StreamArn: streamArn, ShardId: shardId, ShardIteratorType: 'AFTER_SEQUENCE_NUMBER', SequenceNumber: lastProcessedSequenceNumber }) );还没有检查点?
TRIM_HORIZON会从最旧的可用记录开始重新处理该分片;LATEST会跳到新活动。检查点记录序列号,绝不记录迭代器——序列号才是持久的恢复点。
保持轮询循环紧凑——异步地做繁重的逐记录工作(把它排入队列),让
GetRecords的节奏稳稳地保持在 15 分钟以内。优先使用托管消费者——Lambda 事件源映射和 DynamoDB Streams Kinesis 适配器(KCL)会为你管理迭代器和检查点;手工编写的
GetRecords循环正是这个异常出没之处。
确保你的处理是幂等的:从一个检查点恢复可能会重新投递一条你处理了一半的记录。流本身对每条记录恰好包含一次,但一个从上一个检查点重新播种的消费者实际上是至少一次地处理。
当你能看到项目在变化时,调试一个流实际发出了什么会容易得多——DynoTable 桌面应用显示一个项目的实时状态,让你能把流记录与数据对上。在估算一次追赶重放的读取成本?DynamoDB 定价计算器帮你搞定。
相关错误
- TrimmedDataAccessException——你落后于 24 小时的保留窗口了(数据真的没了)。
- Stream not enabled
- Invalid StreamArn
- 学习:DynamoDB Streams
References
- GetShardIterator — Amazon DynamoDB Streams API Reference
- GetRecords — Amazon DynamoDB Streams API Reference
- Change data capture for DynamoDB Streams — Amazon DynamoDB Developer Guide
最后核实于 2026-07-13,依据上方链接的 AWS 官方文档。