Aws lambda Lambda函数在DynamoDB事件中触发两次
我有Lambda函数,它连接到DynamoDB变更事件。 当我更改/修改DynamoDB中测试机器表中的项目时,Lambda会触发两次 我正在将Aws lambda Lambda函数在DynamoDB事件中触发两次,aws-lambda,amazon-dynamodb,Aws Lambda,Amazon Dynamodb,我有Lambda函数,它连接到DynamoDB变更事件。 当我更改/修改DynamoDB中测试机器表中的项目时,Lambda会触发两次 我正在将IsMachineOn值从True修改为False,这是触发测试机器处于警报状态的两次Lambda功能 我不明白为什么两次lambda是一个触发器 我在Lambda的事件参数中观察到记录中的一个小变化 对于第一个触发器 NewImage[“ismachineeon”][“BOOL”]的值为False OldImage[“IsMachine”][“BOOL
IsMachineOn
值从True
修改为False
,这是触发测试机器处于警报状态的两次
Lambda功能
我不明白为什么两次lambda是一个触发器
我在Lambda的事件
参数中观察到记录
中的一个小变化
对于第一个触发器
NewImage[“ismachineeon”][“BOOL”]
的值为False
OldImage[“IsMachine”][“BOOL”]
的值为True
对于第二个触发器
NewImage[“ismachineeon”][“BOOL”]
的值为False
OldImage[“IsMachineOn”][“BOOL”]
的值为False
我在NewImage[“ismachineeon”][“BOOL”]==False
上有业务逻辑,因此我的业务逻辑运行了两次
有两件事:
我们在使用全局表在多个区域的dynamodb表之间同步数据时发现了这个问题。我们的假设是,第二次推送是由全局表在区域间同步数据后进行的。我编写了一个简单的代码来检查新旧图像是否真的不同,并且仅当它们不同时才处理事件
def check_if_dynamo_entities_are_same(dyanmoStreamEvent):
'''copying so that we dont change the incoming event'''
dyanmoStreamEventCopy = copy.deepcopy(dyanmoStreamEvent)
if( not 'NewImage' in dyanmoStreamEventCopy['dynamodb'] or not 'OldImage' in dyanmoStreamEventCopy['dynamodb']):
logger.info("one of newimage or oldimage is not present returning true")
return False
remove_aws_keys(dyanmoStreamEventCopy['dynamodb']['NewImage'])
remove_aws_keys(dyanmoStreamEventCopy['dynamodb']['OldImage'])
return compare_two_json(dyanmoStreamEventCopy['dynamodb']['NewImage'], dyanmoStreamEventCopy['dynamodb']['OldImage'])
def remove_aws_keys(dic):
for k in dic.copy():
if k.startswith('aws:'):
logger.info("poping key=%s", k)
dic.pop(k)
def ordered(obj):
if isinstance(obj, dict):
return sorted((k, ordered(v)) for k, v in obj.items())
if isinstance(obj, list):
return sorted(ordered(x) for x in obj)
else:
return obj
def compare_two_json(json1, json2):
"""This method return true or false if the given jsons are equal or not.
This has been taken from https://stackoverflow.com/a/25851972/3892213"""
return ordered(json1) == ordered(json2)
我也会检查,如果记录有变化! 因此,我用Python3.6编写了以下代码
old_sites=set()
新建站点=集合()
#计算新旧映射的不相交数量
对于['OldImage','NewImage']中的图像名称:
如果记录['dynamodb']不是None,并且记录['dynamodb']中的图像名称为:
ddb_entry=记录['dynamodb'][图像名称]
mappings=ddb_条目[值_键]['L']
打印(f“映射:{mappings}”)
对于映射中的映射:
旧站点。如果图像名称=='OldImage',则添加(映射['S']),否则添加新站点。添加(映射['S'])
更改的\u映射=旧的\u站点。对称的\u差异(新的\u站点)
我们的DynamoDB全局表也有同样的问题。我们观察到的是,双事件只发生在您进行更新的区域,其他区域仍然接收到1个事件,这非常好
得到2个事件的原因是DynamoDB需要维护一些内置字段,以防止区域之间无限更新的循环
第一个事件是更新存储的real对象/字段的属性,在您的例子中是IsMachineOn
,它从true
更改为false
第二个事件是更新特殊属性,如aws:rep:deleting
,aws:rep:updatetime
,aws:rep:updateregion
。这就是为什么您会看到新旧图像都将ismachineeon
设置为false
,这是new的值
希望这有助于澄清一些事情。这使我困惑了好几个小时
TL;博士
通常,您只需比较新旧图像的aws:rep:updatetime
,如果它们相同,则是更新aws内部字段的事件,因此您可以忽略
在我们的用例中,我们依靠aws:rep:updateregion
来确保某些逻辑只运行一次(而不是在多个区域中),因此我们必须比较旧/新的aws:rep:updatetime
,以忽略具有先前区域信息的第一个事件。好消息是两个事件的新图像都具有我们存储的对象的正确值
更新日期:2020年7月23日
- 我们注意到的另一个重要点是,如果您使用
put()
更新/创建记录,aws内置字段将在第一个事件的新映像以及第二个事件的旧映像中丢失。如果依赖这些字段,最好对现有记录使用update()
,以确保它们始终存在
为了保证至少交付一次,这种多次调用的方式将被使用。问题是,我们应该认为Lambda函数不是幂等函数吗?如果是这样的话,把它变成一个就可以解决问题了。@vahdet:我的Lambda函数不是幂等函数。每次请求id都是不同的。这种行为不一定使代码幂等,但无论如何;如果您严格要求只触发一次,我现在想不出解决方案。您的逻辑当然应该是测试NewImage[“IsMachineOn”][“BOOL”]==False&&NewImage[“IsMachineOn”][“BOOL”!=OldImage[“IsMachineOn”][“BOOL”]
(现在处于关闭状态,这也是一个状态更改事件)。。。但听起来好像是第二个不同的更新触发了第二个事件,所以您可能应该查看其他属性,以确定第二个事件触发的性质。根据定义,如果新的和旧的在一个事件中不同,而在另一个事件中相同,那么这不可能是同一事件上的第二个Lambda触发器。关于这个主题,有一篇很好的博客文章:从中学习的主要内容:确保Lambda函数是幂等函数,并考虑可能的多次执行。