Amazon Timestreamのマルチメジャーのレコード長制限がよくわからないので検証してみた


# 本記事で書いていること

Amazon Timestreamは時系列DBを格納するのに特化したマネージドサービスです。
Amazon Timestreamはテーブルに格納できるレコードに制限があります。
制限を守って運用したいのですが、この制限がマルチメジャーだと特にわかりづらく公式の記事で正しいと思われる情報が読み取りきれなかったので自分で検証してみました。

# Amazon Timestreamの制限の確認

https://docs.aws.amazon.com/ja_jp/timestream/latest/developerguide/ts-limits.html

レコード長の制限と思われる記述が`Maximum record size`と`Measure value size per multi-measure record`で2つあります。

箇条書きにするとこうですね。

- Maximum record size
    - The maximum size of a record.
    - 2 Kilobytes
- Measure value size per multi-measure record
    - The maximum size of measure values per multi-measure record.
    - 2048 bytes

こう書かれていると、レコード全体で2KByteまでなのか、マルチメジャーの部分だけで2KByteなのかよくわからなくなります。
これを検証してみます。

# 検証における仮説内容、検証内容、検証コード

Python＋Lambdaで簡単な検証コードを書いて検証してみました。
検証における仮説内容、検証内容、検証コードは以下の通り。

## 検証における仮説

こちらが仮説です。

- マルチメジャーの全項目の値のサイズ合計が2048byteを超えるとエラーになる
- タイムスタンプ、ディメンション、measure_name、マルチメジャーの項目名はレコード長の制限とは無関係

以下はAWS公式から読みとれた確定の情報です。
こちらの確定情報も踏まえつつ仮説を検証していきます。

- マルチメジャーのVARCHAR型ならUTF-8の１文字が1バイトになる
- マルチメジャーのDOUBLE型は桁数に関わらず8バイト固定

https://docs.aws.amazon.com/ja_jp/timestream/latest/developerguide/metering-and-pricing.writes.html

> The write size of each time series event is calculated as the sum of the size of the timestamp and one or more dimension names, dimension values, measure names, and measure values. The size of the timestamp is 8 bytes. The size of dimension names, dimension values, and measure names are the length of the UTF-8 encoded bytes of the string representing each dimension name, dimension value, and measure name. The size of the measure value depends on the data type. It is 1 byte for the boolean data type, 8 bytes for bigint and double, and the length of the UTF-8 encoded bytes for strings. Each write is counted in units of 1 KiB.

## 検証内容

後述の検証コードを用いて、以下の組み合わせのレコードの登録を試みます。

| 検証内容 |
|:---|
| マルチメジャーのVARCHAR項目の合計サイズが2048byte |
| マルチメジャーのVARCHAR項目の合計サイズが2049byte |
| マルチメジャーのVARCHAR項目の合計サイズが2040byte＋マルチメジャーのDOUBLE型が1つ |
| マルチメジャーのVARCHAR項目の合計サイズが2041byte＋マルチメジャーのDOUBLE型が1つ |

## 検証コード

検証時には、一部改変しながら検証します。

```python
import boto3
import os
import time
from typing import List, Dict, Any, Optional


def lambda_handler(event: Dict[str, Any], context: Optional[Any]) -> Dict[str, Any]:

    timestream_write_client = boto3.client(
        'timestream-write', region_name=os.environ['REGION'])

    # 今の時刻を取得
    current_time_in_millis = int(time.time() * 1000)

    measure_values: List[Dict[str, Any]] = []

    # VARCHARで16文字の値持つ列を127個作って、合計2032文字＝2032byteにする
    for i in range(127):
        measure_values.append(
            {
                'Name': f"column_{i:03}",  # 10文字
                'Value': f"val{i:013}",  # 16文字
                'Type': 'VARCHAR'
            }
        )

    measure_values.append(
        {
            'Name': "col_y",
            'Value': "1", # DOUBLE方はおそらく数値の桁数にかかわらず8byte固定
            'Type': 'DOUBLE'
        }
    )
    
    measure_values.append(
        {
            'Name': "col_x",
            'Value': "valxxxxx", # ここの値を8文字にすると合計2048byte=2Kbyteで通る、9文字にするとこれを超えるので確かにエラーになる 
            'Type': 'VARCHAR'
        }
    )

    
    # 1レコードの制限は2Kbyte=2048byte
    # timeで8byte
    # ディメンションが項目名で9文字、値で1文字なので、10byte
    # measure_nameが項目名で12文字、値で8文字なので、20byte
    # ここまでで合計38byte
    timestream_write_client.write_records(
        DatabaseName=event.get('database_name'),
        TableName=event.get('table_name'),
        Records=[
            {
                'Dimensions': [
                    {'Name': 'dimension',
                        'Value': '1'},
                ],
                'MeasureName': 'measure1',
                'MeasureValueType': 'MULTI',
                'MeasureValues': measure_values,
                'Time': str(current_time_in_millis),
                'TimeUnit': 'MILLISECONDS'
            }
        ]
    )

    return {
        'statusCode': 200
    }
```

# 検証結果

|検証内容　|　検証結果　|
|:---|:---:|
| マルチメジャーのVARCHAR項目の合計サイズが2048byte |　登録可能　|
| マルチメジャーのVARCHAR項目の合計サイズが2049byte |　登録不可　|
| マルチメジャーのVARCHAR項目の合計サイズが2040byte＋マルチメジャーのDOUBLE型が1つ |　登録可能　|
| マルチメジャーのVARCHAR項目の合計サイズが2041byte＋マルチメジャーのDOUBLE型が1つ |　登録不可　|

検証結果からやはりこういうことのようです。

- マルチメジャーの全項目の値のサイズ合計が2048byteを超えるとエラーになる
- タイムスタンプ、ディメンション、measure_name、マルチメジャーの項目名はレコード長の制限とは無関係

取り急ぎ自分で動かして検証してみたらこのような結果となりました。
もっと明確な公式の情報が見つかったら本記事に追記しておこうと思います。