

# Python 및 DAX
<a name="DAX.client.run-application-python"></a>

다음 절차를 따라 Amazon EC2 인스턴스에서 Python 샘플 애플리케이션을 실행합니다.

**DAX에 대한 Python 샘플을 실행하려면**

1. `pip` 유틸리티를 사용하여 DAX Python 클라이언트를 설치합니다.

   ```
   pip install amazon-dax-client
   ```

1. 샘플 프로그램 소스 코드(`.zip` 파일)를 다운로드합니다.

   ```
   wget http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/samples/TryDax.zip
   ```

   다운로드가 완료되면 소스 파일의 압축을 풉니다.

   ```
   unzip TryDax.zip
   ```

1. 다음 Python 프로그램을 실행합니다. 첫 프로그램은 `TryDaxTable`이라는 Amazon DynamoDB 테이블을 생성합니다. 두 번째 프로그램은 해당 테이블에 데이터를 씁니다.

   ```
   python 01-create-table.py
   python 02-write-data.py
   ```

1. 다음 Python 프로그램을 실행합니다.

   ```
   python 03-getitem-test.py
   python 04-query-test.py
   python 05-scan-test.py
   ```

    `GetItem`, `Query` 및 `Scan` 테스트에 필요한 시간 정보(밀리초)를 기록해 둡니다.

1. 전 단계에서 DynamoDB 엔드포인트에 대해 프로그램을 실행했습니다. 이제 프로그램을 다시 실행하되, 이번에는 `GetItem`, `Query` 및 `Scan` 작업을 DAX 클러스터에서 처리합니다.

   DAX 클러스터의 엔드포인트를 정의하려면 다음 중 하나를 선택합니다.
   + **Using the DynamoDB console(DynamoDB 콘솔 사용)** - DAX 클러스터를 선택합니다. 다음 예제와 같이 클러스터 엔드포인트가 콘솔에 표시됩니다.

     ```
     dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
     ```
   + **AWS CLI 사용** - 다음 명령을 입력합니다.

     ```
     aws dax describe-clusters --query "Clusters[*].ClusterDiscoveryEndpoint"
     ```

     다음 예제와 같이 클러스터 엔드포인트가 출력에 표시됩니다.

     ```
     {
         "Address": "my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com",
         "Port": 8111,
         "URL": "dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com"
     }
     ```

   프로그램을 다시 실행합니다. 이번에는 클러스터 엔드포인트를 명령줄 파라미터로 지정합니다.

   ```
   python 03-getitem-test.py dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
   python 04-query-test.py dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
   python 05-scan-test.py dax://my-cluster.l6fzcv.dax-clusters.us-east-1.amazonaws.com
   ```

   나머지 출력을 확인하여 시간 정보를 기록해 둡니다. `GetItem`, `Query`, `Scan`에 대한 경과 시간은 DAX가 DynamoDB보다 현저히 적어야 합니다.

1. 다음 Python 프로그램을 실행하여 `TryDaxTable`을 삭제합니다.

   ```
   python 06-delete-table.py
   ```

이러한 프로그램에 대한 자세한 내용은 다음 단원을 참조하세요.
+ [01-create-table.py](DAX.client.run-application-python.01-create-table.md)
+ [02-write-data.py](DAX.client.run-application-python.02-write-data.md)
+ [03-getitem-test.py](DAX.client.run-application-python.03-getitem-test.md)
+ [04-query-test.py](DAX.client.run-application-python.04-query-test.md)
+ [05-scan-test.py](DAX.client.run-application-python.05-scan-test.md)
+ [06-delete-table.py](DAX.client.run-application-python.06-delete-table.md)

# 01-create-table.py
<a name="DAX.client.run-application-python.01-create-table"></a>

`01-create-table.py` 프로그램은 테이블(`TryDaxTable`)을 만듭니다. 이 단원의 나머지 Python 프로그램은 이 테이블을 사용합니다.

```
import boto3


def create_dax_table(dyn_resource=None):
    """
    Creates a DynamoDB table.

    :param dyn_resource: Either a Boto3 or DAX resource.
    :return: The newly created table.
    """
    if dyn_resource is None:
        dyn_resource = boto3.resource("dynamodb")

    table_name = "TryDaxTable"
    params = {
        "TableName": table_name,
        "KeySchema": [
            {"AttributeName": "partition_key", "KeyType": "HASH"},
            {"AttributeName": "sort_key", "KeyType": "RANGE"},
        ],
        "AttributeDefinitions": [
            {"AttributeName": "partition_key", "AttributeType": "N"},
            {"AttributeName": "sort_key", "AttributeType": "N"},
        ],
        "BillingMode": "PAY_PER_REQUEST",
    }
    table = dyn_resource.create_table(**params)
    print(f"Creating {table_name}...")
    table.wait_until_exists()
    return table


if __name__ == "__main__":
    dax_table = create_dax_table()
    print(f"Created table.")
```

# 02-write-data.py
<a name="DAX.client.run-application-python.02-write-data"></a>

`02-write-data.py` 프로그램은 테스트 데이터를 `TryDaxTable`에 씁니다.

```
import boto3


def write_data_to_dax_table(key_count, item_size, dyn_resource=None):
    """
    Writes test data to the demonstration table.

    :param key_count: The number of partition and sort keys to use to populate the
                      table. The total number of items is key_count * key_count.
    :param item_size: The size of non-key data for each test item.
    :param dyn_resource: Either a Boto3 or DAX resource.
    """
    if dyn_resource is None:
        dyn_resource = boto3.resource("dynamodb")

    table = dyn_resource.Table("TryDaxTable")
    some_data = "X" * item_size

    for partition_key in range(1, key_count + 1):
        for sort_key in range(1, key_count + 1):
            table.put_item(
                Item={
                    "partition_key": partition_key,
                    "sort_key": sort_key,
                    "some_data": some_data,
                }
            )
            print(f"Put item ({partition_key}, {sort_key}) succeeded.")


if __name__ == "__main__":
    write_key_count = 10
    write_item_size = 1000
    print(
        f"Writing {write_key_count*write_key_count} items to the table. "
        f"Each item is {write_item_size} characters."
    )
    write_data_to_dax_table(write_key_count, write_item_size)
```

# 03-getitem-test.py
<a name="DAX.client.run-application-python.03-getitem-test"></a>

`03-getitem-test.py` 프로그램은 `GetItem`에서 `TryDaxTable` 작업을 수행합니다. 이 예제는 eu-west-1 리전용입니다.

```
import argparse
import sys
import time
import amazondax
import boto3


def get_item_test(key_count, iterations, dyn_resource=None):
    """
    Gets items from the table a specified number of times. The time before the
    first iteration and the time after the last iteration are both captured
    and reported.

    :param key_count: The number of items to get from the table in each iteration.
    :param iterations: The number of iterations to run.
    :param dyn_resource: Either a Boto3 or DAX resource.
    :return: The start and end times of the test.
    """
    if dyn_resource is None:
        dyn_resource = boto3.resource('dynamodb')

    table = dyn_resource.Table('TryDaxTable')
    start = time.perf_counter()
    for _ in range(iterations):
        for partition_key in range(1, key_count + 1):
            for sort_key in range(1, key_count + 1):
                table.get_item(Key={
                    'partition_key': partition_key,
                    'sort_key': sort_key
                })
                print('.', end='')
                sys.stdout.flush()
    print()
    end = time.perf_counter()
    return start, end


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'endpoint_url', nargs='?',
        help="When specified, the DAX cluster endpoint. Otherwise, DAX is not used.")
    args = parser.parse_args()

    test_key_count = 10
    test_iterations = 50
    if args.endpoint_url:
        print(f"Getting each item from the table {test_iterations} times, "
              f"using the DAX client.")
        # Use a with statement so the DAX client closes the cluster after completion.
        with amazondax.AmazonDaxClient.resource(endpoint_url=args.endpoint_url, region_name='eu-west-1') as dax:
            test_start, test_end = get_item_test(
                test_key_count, test_iterations, dyn_resource=dax)
    else:
        print(f"Getting each item from the table {test_iterations} times, "
              f"using the Boto3 client.")
        test_start, test_end = get_item_test(
            test_key_count, test_iterations)
    print(f"Total time: {test_end - test_start:.4f} sec. Average time: "
          f"{(test_end - test_start)/ test_iterations}.")
```

# 04-query-test.py
<a name="DAX.client.run-application-python.04-query-test"></a>

`04-query-test.py` 프로그램은 `Query`에서 `TryDaxTable` 작업을 수행합니다.

```
import argparse
import time
import sys
import amazondax
import boto3
from boto3.dynamodb.conditions import Key


def query_test(partition_key, sort_keys, iterations, dyn_resource=None):
    """
    Queries the table a specified number of times. The time before the
    first iteration and the time after the last iteration are both captured
    and reported.

    :param partition_key: The partition key value to use in the query. The query
                          returns items that have partition keys equal to this value.
    :param sort_keys: The range of sort key values for the query. The query returns
                      items that have sort key values between these two values.
    :param iterations: The number of iterations to run.
    :param dyn_resource: Either a Boto3 or DAX resource.
    :return: The start and end times of the test.
    """
    if dyn_resource is None:
        dyn_resource = boto3.resource("dynamodb")

    table = dyn_resource.Table("TryDaxTable")
    key_condition_expression = Key("partition_key").eq(partition_key) & Key(
        "sort_key"
    ).between(*sort_keys)

    start = time.perf_counter()
    for _ in range(iterations):
        table.query(KeyConditionExpression=key_condition_expression)
        print(".", end="")
        sys.stdout.flush()
    print()
    end = time.perf_counter()
    return start, end


if __name__ == "__main__":
    # pylint: disable=not-context-manager
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "endpoint_url",
        nargs="?",
        help="When specified, the DAX cluster endpoint. Otherwise, DAX is not used.",
    )
    args = parser.parse_args()

    test_partition_key = 5
    test_sort_keys = (2, 9)
    test_iterations = 100
    if args.endpoint_url:
        print(f"Querying the table {test_iterations} times, using the DAX client.")
        # Use a with statement so the DAX client closes the cluster after completion.
        with amazondax.AmazonDaxClient.resource(endpoint_url=args.endpoint_url) as dax:
            test_start, test_end = query_test(
                test_partition_key, test_sort_keys, test_iterations, dyn_resource=dax
            )
    else:
        print(f"Querying the table {test_iterations} times, using the Boto3 client.")
        test_start, test_end = query_test(
            test_partition_key, test_sort_keys, test_iterations
        )

    print(
        f"Total time: {test_end - test_start:.4f} sec. Average time: "
        f"{(test_end - test_start)/test_iterations}."
    )
```

# 05-scan-test.py
<a name="DAX.client.run-application-python.05-scan-test"></a>

`05-scan-test.py` 프로그램은 `Scan`에서 `TryDaxTable` 작업을 수행합니다.

```
import argparse
import time
import sys
import amazondax
import boto3


def scan_test(iterations, dyn_resource=None):
    """
    Scans the table a specified number of times. The time before the
    first iteration and the time after the last iteration are both captured
    and reported.

    :param iterations: The number of iterations to run.
    :param dyn_resource: Either a Boto3 or DAX resource.
    :return: The start and end times of the test.
    """
    if dyn_resource is None:
        dyn_resource = boto3.resource("dynamodb")

    table = dyn_resource.Table("TryDaxTable")
    start = time.perf_counter()
    for _ in range(iterations):
        table.scan()
        print(".", end="")
        sys.stdout.flush()
    print()
    end = time.perf_counter()
    return start, end


if __name__ == "__main__":
    # pylint: disable=not-context-manager
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "endpoint_url",
        nargs="?",
        help="When specified, the DAX cluster endpoint. Otherwise, DAX is not used.",
    )
    args = parser.parse_args()

    test_iterations = 100
    if args.endpoint_url:
        print(f"Scanning the table {test_iterations} times, using the DAX client.")
        # Use a with statement so the DAX client closes the cluster after completion.
        with amazondax.AmazonDaxClient.resource(endpoint_url=args.endpoint_url) as dax:
            test_start, test_end = scan_test(test_iterations, dyn_resource=dax)
    else:
        print(f"Scanning the table {test_iterations} times, using the Boto3 client.")
        test_start, test_end = scan_test(test_iterations)
    print(
        f"Total time: {test_end - test_start:.4f} sec. Average time: "
        f"{(test_end - test_start)/test_iterations}."
    )
```

# 06-delete-table.py
<a name="DAX.client.run-application-python.06-delete-table"></a>

`06-delete-table.py` 프로그램은 `TryDaxTable`을 삭제합니다. Amazon DynamoDB Accelerator(DAX) 기능 테스트를 완료한 후 이 프로그램을 실행합니다.

```
import boto3


def delete_dax_table(dyn_resource=None):
    """
    Deletes the demonstration table.

    :param dyn_resource: Either a Boto3 or DAX resource.
    """
    if dyn_resource is None:
        dyn_resource = boto3.resource("dynamodb")

    table = dyn_resource.Table("TryDaxTable")
    table.delete()

    print(f"Deleting {table.name}...")
    table.wait_until_not_exists()


if __name__ == "__main__":
    delete_dax_table()
    print("Table deleted!")
```