AWS Lambdaで「Function.ResponseSizeTooLarge」エラーが出たので対処した話

# 環境

[AWS LambdaとPythonでスクレイピング処理をマイクロサービス化する](https://qiita.com/r-wakatsuki/items/f5eefb79dd03f5c6791c)

上記記事で紹介している「headlessms」を利用する、下記のような「IDとパスワードでログインをして取得したクッキーを使って指定のURLから画像データをダウンロードし、そのバイナリを配列で取得して処理する」システムをAWS Lambdaで運用している。

```lambda_main.py
import re,json,boto3,os
from base64 import b64decode

def lambda_handler(event, context):

    kms = boto3.client('kms')
    login_id = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_id']))['Plaintext'].decode('utf-8')
    login_password = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_password']))['Plaintext'].decode('utf-8')

    img_urls = []    
    img_urls = re.findall('https://bmimg.sample3.jp/image/chXXXXXXX/.+\.jpg',event['body'])

    input_event = {
        "body": open('func.py').read() \
                    .replace('__imgUrls__',str(img_urls)) \
                    .replace('__loginId__',login_id) \
                    .replace('__loginPassword__',login_password)
    }

    res = boto3.client('lambda').invoke(
        FunctionName = 'headlessms-aws-function-00',
        InvocationType = 'RequestResponse',
        Payload = json.dumps(input_event)
    )
    img_ary = json.loads(json.loads(res['Payload'].read())['body'])
```

```func.py
import requests,json
from base64 import b64encode
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
def scrape_process(driver):

    driver.get('https://account.sample3.jp/login')
    WebDriverWait(driver, 5).until(ec.presence_of_all_elements_located)

    driver.find_element_by_id('input__mailtel').send_keys('__loginId__')
    driver.find_element_by_id('input__password').send_keys('__loginPassword__')

    submitButton = driver.find_element_by_id('login__submit')
    submitButton.click()

    session = requests.session()
    for cookie in driver.get_cookies():
        session.cookies.set(cookie['name'], cookie['value'])

    img_urls = __imgUrls__
    img_contents = []

    for url in img_urls:
        img_contents.append(b64encode(session.get(url).content).decode('utf-8'))

    return(json.dumps(img_contents))
```

# 事象

あるとき、いつもなら問題ないく動作している上記システムの自動実行が`boto3.client('lambda').invoke`の箇所で以下のようなエラーを出して不正終了していた。

```sh
{'errorType': 'Function.ResponseSizeTooLarge', 'errorMessage': 'Response payload size (9471685 bytes) exceeded maximum allowed payload size (6291556 bytes).'}
```

調査してみると、Lambda関数を`RequestResponse`タイプで呼び出したときに送受信されるデータのサイズ上限が`6291556 bytes`（約6MB）であり、超過した場合に発生するエラーとのこと。

そこで「headlessms」に送信されたURL数を確認してみると、いつもなら一度に10個程度の画像URL(`img_urls`)しか送信されないところ、今回のエラーが発生したときの処理では32個もURLが送信されていた。
いつもより送信するURL数が多かったため、「headlessms」側で6MBを超える画像が取得され、返送時にエラーとなっていたらしい。

# 解決

`lambda_main.py`を以下のように修正し、画像URLの「headlessms」への送信は最大10個ずつとなるようにした。

```modified_lambda_main.py
import re,json,boto3,os
from base64 import b64decode

def lambda_handler(event, context):

    kms = boto3.client('kms')
    login_id = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_id']))['Plaintext'].decode('utf-8')
    login_password = kms.decrypt(CiphertextBlob=b64decode(os.environ['login_password']))['Plaintext'].decode('utf-8')

    img_urls = []    
    img_urls = re.findall('https://bmimg.sample3.jp/image/chXXXXXXX/.+\.jpg',event['body'])

    def split_list(l):
        for idx in range(0, len(l), 10):
            yield l[idx:idx + 10]
    img_urls_list = list(split_list(img_urls))

    img_ary = []
    for img_urls_within10 in img_urls_list:
        #画像URLから画像データ取得
        input_event = {
            "body": open('func.py').read() \
                        .replace('__imgUrls__',str(img_urls_within10)) \
                        .replace('__loginId__',login_id) \
                        .replace('__loginPassword__',login_password)
        }
    
        res = boto3.client('lambda').invoke(
            FunctionName = 'headlessms-aws-function-00',
            InvocationType = 'RequestResponse',
            Payload = json.dumps(input_event)
        )
    
        img_ary.extend(json.loads(json.loads(res['Payload'].read())['body']))
```

# 参考

https://forums.aws.amazon.com/thread.jspa?threadID=230229
https://www.stackery.io/blog/RequestEntityTooLargeException-aws-lambda-message-invocation-limits/
https://www.python.ambitious-engineer.com/archives/1843

以上