重试后AWS DynamoDB仍报ConditionalCheckFailedException问题求助
问题描述
通过AWS Lambda更新AWS DynamoDB表时,使用ConditionExpression校验updatedTimeStamp与表中数据一致。更新失败后,以ConsistentRead=true重新获取最新文档数据,再用最新的updatedTimeStamp尝试更新,但仍抛出ConditionalCheckFailedException。
现有代码
获取数据代码
async function getItem(Id) { return new Promise(async (resolve, reject) => { let params = { ConsistentRead: true, TableName: process.env.TABLE, Key: { Id: Id, }, }; documentClient.get(params, async function (err, data) { if (err) { reject(err); } else resolve(data); }); }); }
更新数据代码
async updateDataIntoDB(data, formdata, updatedTimestamp, index) { try { const params = { TableName: process.env.TABLE, ReturnValues: "ALL_NEW", Key: { Id: Id, // 注意:此处Id未定义,疑似bug }, UpdateExpression: `set #updatedTimestamp = :currentTimestamp, #brandProfile.#additionalAttributes.#forms.#index = :formdata, #updatedForm = :updatedForm, #updatedFormStatus = :updatedFormStatus, #updatedBy = :updatedBy, #callStepFunction = :callStepFunctionVal`, ExpressionAttributeNames: { "#brandProfile": "brandProfile", "#additionalAttributes": "additionalAttributes", "#forms": "forms", "#index": index, "#updatedForm": "updatedForm", "#updatedFormStatus": "updatedFormStatus", "#updatedBy": "updatedBy", "#callStepFunction": "callStepFunction", "#updatedTimestamp": "updatedTimestamp", }, ConditionExpression: "updatedTimestamp = :updatedTimestamp", ExpressionAttributeValues: { ":formdata": formdata, ":updatedFormStatus": updatedFormStatus, ":updatedForm": updatedForm, ":callStepFunctionVal": callStepFunction, ":updatedBy": UpdatedBy, ":updatedTimestamp": updatedTimestamp, // 用于条件校验 ":currentTimestamp": new Date().toISOString(), }, }; return await documentClient.update(params).promise(); } catch (error) { Log.warn(`Failed In Updating DB- ${error}`); } }
解决方案
1. 修复变量引用错误
更新函数中Key字段的Id未定义,需从传入的data参数中获取,修改为:
Key: { Id: data.Id, },
该错误会导致更新错误条目,直接触发条件校验失败。
2. 保证时间戳格式完全一致
- 获取数据后,直接使用返回
Item中的updatedTimestamp原始值,不做任何格式转换或修改,直接传入更新函数。 - 确认
new Date().toISOString()生成的时间戳与DynamoDB存储格式完全匹配(如毫秒位数、时区),DynamoDB存储的ISO字符串通常精确到毫秒,需确保格式统一。
3. 实现指数退避的重试逻辑
单次重试仍可能遭遇并发更新,需循环尝试获取最新数据并更新,直到成功或达到最大重试次数:
async retryUpdate(data, formdata, index, maxRetries = 3) { let retries = 0; while (retries < maxRetries) { try { // 获取最新数据 const latestItem = await getItem(data.Id); if (!latestItem.Item) { throw new Error("Item not found"); } // 使用最新timestamp更新 const result = await updateDataIntoDB(data, formdata, latestItem.Item.updatedTimestamp, index); return result; } catch (error) { if (error.name === "ConditionalCheckFailedException" && retries < maxRetries - 1) { retries++; // 指数退避等待 await new Promise(resolve => setTimeout(resolve, Math.pow(2, retries) * 100)); continue; } throw error; } } throw new Error("Max retries exceeded"); }
4. 修正ConditionExpression字段引用
在ConditionExpression中使用表达式属性名#updatedTimestamp替代直接写字段名,避免与DynamoDB保留字冲突:
ConditionExpression: "#updatedTimestamp = :updatedTimestamp",
5. 应对并发更新场景
若存在多服务同时更新同一条数据,即使使用ConsistentRead,获取数据与执行更新的时间窗口仍可能被其他请求抢占。指数退避重试可有效应对这类并发冲突。
内容的提问来源于stack exchange,提问作者PrasathBV
相关产品推荐
相关产品推荐

