Aws lambda Lambda函数在DynamoDB事件中触发两次

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

我有Lambda函数,它连接到DynamoDB变更事件。 当我更改/修改DynamoDB中测试机器表中的项目时,Lambda会触发两次

我正在将
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
上有业务逻辑,因此我的业务逻辑运行了两次

有两件事:

  • 为什么Lambda跑了两次
  • 解决此问题的变通方法是什么

  • 我们在使用全局表在多个区域的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函数是幂等函数,并考虑可能的多次执行。