質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.04%

S3のファイル作成時にSNSからメールが2通飛んでしまう

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,064

Otazoman

score 43

 前提・実現したいこと

S3のファイルがアップロードされたタイミングでJSONのファイル名に該当する
DynamoDBのテーブルにJSONデータを書き込むLambdaFunctionを作成しています。
DynamoDBのテーブルにデータの書き込みが完了したタイミングでSNS経由でメールを
送信するようにしたいと考えております。

 発生している問題

何とかSNS経由でメール送信するところまではできたのですが、S3にファイルをアップロードした
際に1通しか送信されないはずのメールが2通送信されてきてしまいます。
どの様に処理すれば処理が完了したタイミングで1通のメールが送信されるというように
できるでしょうか。
ご教示のほどお願いいたします。

 該当のソースコード

import logging
import boto3
import json
import os
import urllib
import datetime

LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
dynamodb = boto3.resource('dynamodb')
s3_res = boto3.resource('s3')
s3_cl = boto3.client('s3')
# Write DynamoDB
def put_item(writeitems,writetable):
    try:
        table = dynamodb.Table(writetable)
        with table.batch_writer() as batch:
            for i in range(len(writeitems)):
                batch.put_item(
                    Item=writeitems[i]
                )
        LOGGER.info("Completed registration")
        return "end"
    except Exception as e:
        LOGGER.error(e)
        raise e
def translateJson(res):
    try:
        s=json.loads(res.decode('utf-8-sig'))
        return s

    except Exception as e:
        LOGGER.error(e)
        raise e
# send message
def sendmessage(subject,msg):
    try:
        sqs_arn = os.getenv('SQS_ARN')
        client = boto3.client('sns')
        request = {
                     'TopicArn': sqs_arn,
                     'Message': msg,
                     'Subject': subject
                  }
        r = client.publish(**request)
        return r
    except Exception as e:
        LOGGER.error(e)
        raise e
# main
def lambda_handler(event, context):
    try:
        bucket_name = os.getenv('BUCKET_NAME')
        rep = s3_res.Bucket(bucket_name).objects.all()
        mg = ''
        for all_object in rep:
            file_name = all_object.key
            table_name=('.').join(file_name.split('.')[:-1])
            response = s3_cl.get_object(Bucket=bucket_name, Key=file_name)
            tabledata = response['Body'].read()
            objItem = translateJson(tabledata)
            stat = put_item(objItem,table_name)
            LOGGER.info("Completed registration")
            mg= mg + table_name + '\n'
        else:
            sb=u'Complete'
            cd = datetime.datetime.now()
            mg= mg + '\n' + u'EndTime:' + str(cd)
            sendmessage(sb,mg)
            return "end"
    except Exception as e:
        LOGGER.error(e)
        sb=u'ERROR'
        sendmessage(sb,e)
        raise e

 試したこと

Lambdaのエディタで「テスト」とした場合は1通だけメールが送信されます。
S3にファイルをアップした際にメールが2通飛んできます。

  • Lambdaでテスト送信されるメール
item-content
item

EndTime:2018-11-27 02:24:14.801696
  • S3にファイルをアップロードした際に送信されるメール
    -- 1通目
item-content

EndTime:2018-11-27 02:27:26.662573


--2通目

item-content
item

EndTime:2018-11-27 02:27:27.544248

 補足情報(FW/ツールのバージョンなど)

該当コードはLambdaのエディタで作成
python3.6

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

具体的な解決策の提示ではありませんが
Lambdaは処理失敗時に実行条件に応じて、再試行を行います。
その際、デッドレターキュー(DLQ)を設定していれば
再試行したか否か解ると思いますので、試してみてはいかがでしょうか?

https://dev.classmethod.jp/cloud/aws/lambda-supports-dead-letter-queue/

以前、AWS IoT -> Lambda -> DynamoDBという処理フローで、ログの重複が発生し
調査したのですが、Lambda処理時、データ上は、1度目の処理は上手くいっているようなのですが
実際Lambdaは処理失敗判定となっており、再試行が走り、ログの重複が発生したという経験があります。

もし、参考になるのであればと思い投稿させて頂きました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/28 09:57

    ありがとうございます。記事詳細確認させていただいてやってみます。

    キャンセル

  • 2018/12/06 15:55

    すいません。エラーではなかったみたいです。正常な動きで本当に初歩的な箇所で躓いていました。
    ふっと冷静に考えてみると、そうだよなぁとなるんですが離れてみないと気付かないものです。
    本当にありがとうございました。m(__)m

    キャンセル

  • 2018/12/08 13:07

    自己解決されたようで、良かったです。
    一度、距離を置いてから見直すと、結構自分で気づいてしまう事ってあるあるですよね。
    開発頑張ってください👍

    キャンセル

check解決した方法

0

別件でしばらく別の作業をしていて解決が少し遅くなりましたが
自己解決しました。

そもそもの話ですが、複数ファイルのアップロードが完了してからLambdaが
起動されると考えていたのですが、そうではなくてS3にファイルがアップロード
された都度、トリガーが動いてLambdaが起動するみたいです。
なので以下の動きなので、自分の想定ミスです。初歩的なことですいません。

(1)1個目のitem.jsonのアップロード完了→Lambda起動
(2)2個目のitem-content.jsonのアップロード完了→Lambda起動
そのため、SNSでの通知が2回行われる結果となってしまいました。
※メールが2通届いて当然です。

そこでバッチ処理とはなりますが10分に1回程度処理ができれば問題ないので
トリガーをCloudWatchEventに変更して対処いたしました。

スケジュール式: cron(0/10 * ? * MON-FRI *)

CloudWatchEvent設定

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る