DynamoDBクライアントでインスタンスが同じならセッション再利用されるのか実験してみた

[AWS SDK for JavaScript](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/)を使ってDynamoDBを利用する場合、`AWS.DynamoDB`インスタンスを生成し、これを通してAPIアクセスを行うことになりますが、その際のセッションがどうなっているのか気になって調べてみました。

# お急ぎの方に結論
オプションもしくは環境変数を設定すればセッションは再利用されます。
[参考](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html)

# 準備
[DynamoDB local](https://www.npmjs.com/package/dynamodb-local)を起動し、これにアクセスするコードを用意します。
（DynamoDB localがポート18005で動いています）
TCPセッションの状態は`lsof -i@localhost`で確認します。

# 実験１
以下のようなコードを用意しました。
`DynamoDB`インスタンスは１つ作成し、APIを２回実行します。

```javascript
import { DynamoDB } from 'aws-sdk';
import { execSync } from 'child_process';

process.env.AWS_REGION = 'ap-northeast-1';

function getDynamoDB() {
  return new DynamoDB({
    endpoint: 'http://localhost:18005',
  });
}

function printSession() {
  try {
    const stdout = execSync('lsof -i@localhost | grep node');
    console.log(stdout.toString());
  } catch (e) {
    console.error('null');
  }
}

async function main() {
  console.log('#0');
  printSession();
  const cl = getDynamoDB();
  console.log('#1');
  printSession();
  await cl.describeTable({ TableName: 'test-table' }).promise();
  console.log('#2');
  printSession();
  await cl.describeTable({ TableName: 'test-table' }).promise();
  console.log('#3');
  printSession();
}

main();
```

以下が出力されました。

```shell
#0
null
#1
null
#2
node      98657 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56434->localhost:18005 (CLOSE_WAIT)
#3
node      98657 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56435->localhost:18005 (CLOSE_WAIT)
```

インスタンス作っても接続はされないんですね。
クライアント側のポートが異なるものが・・・ということは、それぞれでセッションが作られていそうです。

# 実験２
公式がズバリな記事を用意していました。
[Reusing Connections with Keep-Alive in Node.js](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html)

`keepAlive`を設定してみます。

```javascript
import { DynamoDB } from 'aws-sdk';
import { execSync } from 'child_process';
import * as http from 'http';

process.env.AWS_REGION = 'ap-northeast-1';

function getDynamoDB() {
  return new DynamoDB({
    endpoint: 'http://localhost:18005',
    httpOptions: {
      agent: new http.Agent({
        keepAlive: true,
      }),
    },
  });
}

function printSession() {
  try {
    const stdout = execSync('lsof -i@localhost | grep node');
    console.log(stdout.toString());
  } catch (e) {
    console.error('null');
  }
}

async function main() {
  console.log('#0');
  printSession();
  const cl = getDynamoDB();
  console.log('#1');
  printSession();
  await cl.describeTable({ TableName: 'test-table' }).promise();
  console.log('#2');
  printSession();
  await cl.describeTable({ TableName: 'test-table' }).promise();
  console.log('#3');
  printSession();
}

main();
```

実行すると以下が出力されました。

```shell
#0
null
#1
null
#2
node      99003 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56503->localhost:18005 (ESTABLISHED)
#3
node      99003 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56503->localhost:18005 (ESTABLISHED)
```

ちゃんと再利用されていそうです。

# 実験３
ちなみに、別インスタンスを作ると？と思って念のための追試です。

```javascript
import { DynamoDB } from 'aws-sdk';
import { execSync } from 'child_process';
import * as http from 'http';

process.env.AWS_REGION = 'ap-northeast-1';

function getDynamoDB() {
  return new DynamoDB({
    endpoint: 'http://localhost:18005',
    httpOptions: {
      agent: new http.Agent({
        keepAlive: true,
      }),
    },
  });
}

function printSession() {
  try {
    const stdout = execSync('lsof -i@localhost | grep node');
    console.log(stdout.toString());
  } catch (e) {
    console.error('null');
  }
}

async function main() {
  console.log('#0');
  printSession();
  const cl = getDynamoDB();
  console.log('#1');
  printSession();
  await cl.describeTable({ TableName: 'test-table' }).promise();
  console.log('#2');
  printSession();
  await cl.describeTable({ TableName: 'test-table' }).promise();
  console.log('#3');
  printSession();
  const cl2 = getDynamoDB();
  console.log('#4');
  printSession();
  await cl2.describeTable({ TableName: 'test-table' }).promise();
  console.log('#5');
  printSession();
}

main();
```

以下が出力されました。

```shell
#0
null
#1
null
#2
node      99143 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56510->localhost:18005 (ESTABLISHED)
#3
node      99143 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56510->localhost:18005 (ESTABLISHED)
#4
node      99143 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56510->localhost:18005 (ESTABLISHED)
#5
node      99143 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56510->localhost:18005 (ESTABLISHED)
node      99143 user   35u  IPv4 0x108da27b1d53185d      0t0  TCP localhost:56511->localhost:18005 (ESTABLISHED)
```

インスタンスまたいで共有はされませんね。

# 結果
短い時間に複数回の操作を呼び出すユースケースがある場合は、`keepAlive`設定するか、公式に記載の`AWS_NODEJS_CONNECTION_REUSE_ENABLED`を利用するのがよさそうです。

`AWS_NODEJS_CONNECTION_REUSE_ENABLED`を設定すると別インスタンスでもセッションが共有されます。

`keepAlive`を使う場合、この実験ではローカル接続でhttpでしたが、AWS環境で有効にするには`https.Agent`にする必要があります。