Java에서 DynamoDB 테이블을 S3로 내보내자


환경

  • Java
  • Gradle
  • AWS(DynamoDB, S3, IAM)
  • AWS Java SDK(1.x 또는 2.x)
  • IntelliJ


시작하기 전에

  • 해당 내용은 AWS IAM에서 사용자의 생성, 사용자의 역할 및 권한 부여, 사용자의 키(aws_access_key_id, aws_secret_access_key) 발급에 대해서 알고 있다고 가정한다.
  • 해당 내용은 S3에서 버킷의 개념에 대해 알고 있다고 가정한다.
  • 해당 내용은 DynamoDB에서 테이블이 이미 생성되어 있고 해당 테이블의 PITR이 활성화 되어있다고 가정한다. 이 부분에 관해서는 Amazon S3 로 DynamoDB 데이터 내보내기: 작동 방식을 참고한다.


사전 준비

AWS 설정

사용자 및 Credential 설정

  • AWS IAM에서 사용자는 생성되어 있다고 가정한다.
  • 사용자에 해당하는 credential은 이 코드가 실행되는곳에 이미 설정되어 있다고 가정한다. 관련 내용은 Step 1: Set up for this tutorial를 참고한다.

(옵션) AWS IAM 사용자가 아니라 AWS IAM 역할을 사용하는 경우

  • EC2 인스턴스에 IAM 역할을 부여해서 사용하는것처럼 AWS IAM의 역할을 사용하는 경우 해당 역할의 ARN만 사용해주면 되며 정책의 적용 방법은 아래와 모두 동일하다.
  • 사용자 권한 설정의 IAM 정책을 역할에 연결해주고 해당 역할의 ARN을 대상 S3 버킷 정책 설정에 적용하면 된다.

사용자 권한 설정

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowDynamoDBExportAction",
            "Effect": "Allow",
            "Action": "dynamodb:ExportTableToPointInTime",
            "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/my-table"
        },
        {
            "Sid": "AllowWriteToDestinationBucket",
            "Effect": "Allow",
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::your-bucket/*"
        }
    ]
}

대상 S3 버킷 정책 설정

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ExampleStatement",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:user/Dave"
            },
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::awsexamplebucket1/*"
        }
    ]
}

IntelliJ 설정

  • build.gradle을 이용해서 SDK를 가져왔으며 각 버전에 해당하는 정보는 아래 코드를 참고한다.


코드

Java SDK 1.x

build.gradle

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-dynamodb', version: '1.12.42'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

ExportDynamoDbTableToS3.java

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.ExportTableToPointInTimeRequest;

public class ExportDynamoDbTableToS3 {

    public static void main(String[] args) {

        // Please fill out below values
        String tableArn = "";
        String s3Bucket = "";
        String s3Prefix = "";
        String exportFormat = "";
        String s3BucketOwner = "";

        AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.defaultClient();
        ExportTableToPointInTimeRequest exportTableToPointInTimeRequest = new ExportTableToPointInTimeRequest();

        exportTableToPointInTimeRequest.setTableArn(tableArn);
        exportTableToPointInTimeRequest.setS3Bucket(s3Bucket);
        exportTableToPointInTimeRequest.setS3Prefix(s3Prefix);
        exportTableToPointInTimeRequest.setExportFormat(exportFormat);
        exportTableToPointInTimeRequest.setS3BucketOwner(s3BucketOwner);

        amazonDynamoDB.exportTableToPointInTime(exportTableToPointInTimeRequest);

    }
}

Java SDK 2.x

build.gradle

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation platform('software.amazon.awssdk:bom:2.17.14')
    implementation 'software.amazon.awssdk:dynamodb'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

ExportDynamoDbTableToS3.java

import software.amazon.awssdk.services.dynamodb.model.ExportTableToPointInTimeRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

public class ExportDynamoDbTableToS3 {

    public static void main(String[] args) {

        // Please fill out below values
        String tableArn = "";
        String s3Bucket = "";
        String s3Prefix = "";
        String exportFormat = "";
        String s3BucketOwner = "";

        DynamoDbClient dynamoDbClient = DynamoDbClient.builder().build();
        ExportTableToPointInTimeRequest exportTableToPointInTimeRequest = ExportTableToPointInTimeRequest
                .builder()
                .tableArn(tableArn)
                .s3Bucket(s3Bucket)
                .s3Prefix(s3Prefix)
                .exportFormat(exportFormat)
                .s3BucketOwner(s3BucketOwner)
                .build();

        dynamoDbClient.exportTableToPointInTime(exportTableToPointInTimeRequest);
    }
}


결과

DynamoDB

  • AWS ID와 버킷명은 가렸습니다.

S3


GitHub


참고자료

Export DynamoDB table to S3 in Java


Environment and Prerequisite

  • Java
  • Gradle
  • AWS(DynamoDB, S3, IAM)
  • AWS Java SDK(1.x or 2.x)
  • IntelliJ


Before Start

  • This post assumes reader knows about user creation, assigning role and policy to user, making user’s key(aws_access_key_id, aws_secret_access_key) in AWS IAM.
  • This post assumes reader knows about S3 bucket concept.
  • This post assumes that DynamoDB table already exists and PITR is enabled in that table. See also DynamoDB data export to Amazon S3: how it works.


Preparation

AWS Setting

User and Credential Setting

  • Let’s consider a user is already created in AWS IAM.
  • Consider credential of that user is already set in running environment. Related content is on Step 1: Set up for this tutorial.

(Option) In case of using AWS IAM role not AWS IAM user

  • When using an AWS IAM role, just like granting an IAM role to an EC2 instance and using it, you only need to use the ARN of the role and the method of applying the policy is the same as below
  • Attach user’s IAM policy to role and apply that role’s ARN in Target S3 Bucket Policy Setting.

User Permission Setting

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowDynamoDBExportAction",
            "Effect": "Allow",
            "Action": "dynamodb:ExportTableToPointInTime",
            "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/my-table"
        },
        {
            "Sid": "AllowWriteToDestinationBucket",
            "Effect": "Allow",
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::your-bucket/*"
        }
    ]
}

Target S3 Bucket Policy Setting

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ExampleStatement",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:user/Dave"
            },
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::awsexamplebucket1/*"
        }
    ]
}

IntelliJ Setting

  • SDK is imported using build.gradle. Check each version in below codes.


Code

Java SDK 1.x

build.gradle

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-dynamodb', version: '1.12.42'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

ExportDynamoDbTableToS3.java

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.model.ExportTableToPointInTimeRequest;

public class ExportDynamoDbTableToS3 {

    public static void main(String[] args) {

        // Please fill out below values
        String tableArn = "";
        String s3Bucket = "";
        String s3Prefix = "";
        String exportFormat = "";
        String s3BucketOwner = "";

        AmazonDynamoDB amazonDynamoDB = AmazonDynamoDBClientBuilder.defaultClient();
        ExportTableToPointInTimeRequest exportTableToPointInTimeRequest = new ExportTableToPointInTimeRequest();

        exportTableToPointInTimeRequest.setTableArn(tableArn);
        exportTableToPointInTimeRequest.setS3Bucket(s3Bucket);
        exportTableToPointInTimeRequest.setS3Prefix(s3Prefix);
        exportTableToPointInTimeRequest.setExportFormat(exportFormat);
        exportTableToPointInTimeRequest.setS3BucketOwner(s3BucketOwner);

        amazonDynamoDB.exportTableToPointInTime(exportTableToPointInTimeRequest);

    }
}

Java SDK 2.x

build.gradle

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation platform('software.amazon.awssdk:bom:2.17.14')
    implementation 'software.amazon.awssdk:dynamodb'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

ExportDynamoDbTableToS3.java

import software.amazon.awssdk.services.dynamodb.model.ExportTableToPointInTimeRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

public class ExportDynamoDbTableToS3 {

    public static void main(String[] args) {

        // Please fill out below values
        String tableArn = "";
        String s3Bucket = "";
        String s3Prefix = "";
        String exportFormat = "";
        String s3BucketOwner = "";

        DynamoDbClient dynamoDbClient = DynamoDbClient.builder().build();
        ExportTableToPointInTimeRequest exportTableToPointInTimeRequest = ExportTableToPointInTimeRequest
                .builder()
                .tableArn(tableArn)
                .s3Bucket(s3Bucket)
                .s3Prefix(s3Prefix)
                .exportFormat(exportFormat)
                .s3BucketOwner(s3BucketOwner)
                .build();

        dynamoDbClient.exportTableToPointInTime(exportTableToPointInTimeRequest);
    }
}


Result

DynamoDB

  • I concealed AWS ID and bucket name.

S3


GitHub


Reference

Iterable과 Iterator에 대한 정리


환경

  • Python


Iterable과 Iterator의 정의

Iterable

  • 멤버들을 한번에 하나씩 돌려줄 수 있는 객체
  • 문서에는 An object capable of returning its members one at a time.으로 나와있다.
  • “이터러블의 예로는 모든 (list, str, tuple 같은) 시퀀스 형들, dict 같은 몇몇 비 시퀀스 형들, 파일 객체들, __iter__()나 시퀀스 개념을 구현하는 __getitem__()메서드를 써서 정의한 모든 클래스의 객체들이 있습니다.”
  • 이터러블 객체가 내장 함수 iter() 에 인자로 전달되면, 그 객체의 이터레이터를 돌려줍니다.
>>> a = [1,2,3]
>>> type(a)
<class 'list'>
>>> a_iterator = iter(a)
>>> type(a_iterator)
<class 'list_iterator'>

Iterator

  • 데이터의 스트림을 표현하는 객체
  • 문서에는 An object representing a stream of data.라고 나와있다.
  • 이터레이터의 __next__() 메서드를 통해 반복적으로 호출하면 스트림의 데이터를 차례대로 돌려준다. 더 이상의 데이터가 없으면 StopIteration 예외를 발생시킨다.
  • 이터레이터는 이터레이터 객체 자신을 돌려주는 __iter__() 메서드를 가지기 때문에, 이터레이터는 이터러블이기도 하다.
>>> a = [1,2,3]
>>> type(a)
<class 'list'>
>>> a_iterator = iter(a)
>>> type(a_iterator)
<class 'list_iterator'>
>>> dir(a_iterator)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']


이터레이터 형(Iterator Types)

이터레이터 프로토콜

  • 파이썬은 이터레이션 개념을 지원한다. 이것은 아래 두 개의 메서드(iterator.__iter__()iterator.__next__())를 사용해서 구현되며 이것들은 사용자 정의 클래스가 이터레이션을 지원할 수 있도록 하는데 사용된다.
  • 문서에서는 컨테이너에 대한 예제만 나와있는데 글을 읽어보면 임의의 클래스나 객체에 대해서도 구현이 가능해보인다.
  • 이터레이터 객체 자체는 다음과 같은 두 가지 메서드를 지원해야 하는데, 둘이 함께 이터레이터 프로토콜 (iterator protocol) 를 이룬다.

iterator.__iter__()

  • 이터레이터 객체 자신을 돌려준다.

iterator.__next__()

  • 객체의 다음 항목을 돌려준다. 더 항목이 없으면 StopIteration 예외를 일으킨다.


컨테이너에서의 이터레이션

  • 컨테이너 객체가 이터레이션 지원을 제공하려면 아래에 있는 한 가지 메서드를 정의해야한다.
  • 위에 이터레이터 프로토콜에 따라서 바로 구현해도 되는거 같다. (글쓴이의 추측)

container.__iter__()


추가 궁금한 사항

Iterable과 Iterator의 관계

  • 상단에 정리를 해두었지만 반복을 통해 멤버들을 한번에 하나씩 돌려줄 수 있는 객체들을 모두 통틀어서 Iterable이라고 하고 그 Iterable를 구현하는 방법중 하나가 Iterator를 사용한거다.
  • Iterable로 만들고자하는 클래스에 __iter()__ 메서드를 구현해서 Iterator를 반환하도록 만들고 그 Iterator는 상단 이터레이터 형(Iterator Types)에 나온 이터레이터 프로토콜에 따라 자신을 반환하는 __iter()__와 다음 항목을 돌려주는 __next()__를 구현하면 된다.


__iter__()와 __getitem__()의 차이


정리 및 마무리

처음에는 IterableIterator가 뭔지가 궁금했는데 알면 알수록 조금 복잡했었다.

정리하면 Iterable은 멤버들을 한번에 하나씩 돌려줄 수 있는 객체들을 말하며 list, str, dict, 파일 객체 그리고 __iter__()나 시퀀스 개념을 구현하는 __getitem__()를 통해 구현한 객체들이 그 예시이다. 반복을 통해 속한 멤버들을 하나씩 반환할 수 있는 특징을 가진 객체들을 통칭한다고 보면 될거 같다.

이제 Iterator는 데이터의 스트림을 표현하는 객체로 이터레이터 프로토콜에 따라 두 가지 메서드(iterator.__iter__()iterator.__next__())를 구현한 객체이다. Iterator__iter__() 메서드가 구현되어 있기 때문에 Iterable하기도 하다. 이 Iterator를 이용해 Iterable한 객체를 만들 수 있다.

실제 리스트를 보면 __iter__() 메서드를 직접 호출하거나 리스트를 내장 함수 iter()에 넘겨주면 Iterator를 반환하며 이 Iterator 내부에는 두 가지 메서드(iterator.__iter__()iterator.__next__())가 정의되어 있다.

a = [1, 2, 3]

print(type(a))
print(type(a.__iter__()))
print(type(iter(a)))

print()

print(dir(a))
print(dir(a.__iter__()))
print(dir(iter(a)))
<class 'list'>
<class 'list_iterator'>
<class 'list_iterator'>

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']


참고자료

Post about Iterable and Iterator


Environment and Prerequisite

  • Python


Definition of Iterable and Iterator

Iterable

  • An object capable of returning its members one at a time.
  • In document, it is written An object capable of returning its members one at a time..
  • “Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an __iter__() method or with a __getitem__() method that implements Sequence semantics.”
  • If iterable object is passed to built-in function iter(), it returns its object’s iterator.
>>> a = [1,2,3]
>>> type(a)
<class 'list'>
>>> a_iterator = iter(a)
>>> type(a_iterator)
<class 'list_iterator'>

Iterator

  • An object representing a stream of data.
  • In document, it is written An object representing a stream of data..
  • Repeated calls to the iterator’s __next__() method return successive items in the stream. It raise StopIteration exception if there is no more data.
  • Iterator has __iter__() method which returns iterator itself so iterator is also iterable.
>>> a = [1,2,3]
>>> type(a)
<class 'list'>
>>> a_iterator = iter(a)
>>> type(a_iterator)
<class 'list_iterator'>
>>> dir(a_iterator)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']


Iterator Types

Iterator Protocol

  • Python supports iteration concept. This is implemented by below two methods(iterator.__iter__() and iterator.__next__()) and these are used to allow user-defined classes to support iteration.
  • Document only tells about container but it looks it supports user-defined classes.
  • The iterator objects themselves are required to support the following two methods, which together form the iterator protocol.

iterator.__iter__()

  • Return the iterator object itself.

iterator.__next__()

  • Return the next item from the object. If there are no further items, raise the StopIteration exception.


Iteration in container

  • One method needs to be defined for container objects to provide iteration support.
  • It could be implemented by above Iterator Protocol. (Writer’s opinion)

container.__iter__()

  • Return an iterator object. This object is required to support the Iterator Protocol described above.


Additional Questions

Relation between Iterable and Iterator

  • As wrote above, we call all objects to Iterable which is capable of returning its members one at a time and one of its implmentation methods is using Iterator.
  • Implement __iter()__ method to class which you want to make Iterable to return Iterator. That Iterator should be implemented with __iter()__ method which returns itself and __next()__ method which returns its next member following to above Iterator Protocol in Iterator Types.


Difference between __iter__() and __getitem__()


Summary and Conclusion

At first I was just curious about Iterable and Iterator but those are little bit difficult when I searched.

In short, Iterable is an object capable of returning its members one at a time such as list, str, dict, file object and objects of any classes you define with an __iter__() method or with a __getitem__() method that implements Sequence semantics. We can commonly call object which can return its members one at a time.

Now Iterator is an object representing a stream of data. It can be implemented with two methods(iterator.__iter__() and iterator.__next__()) according to Iterator Protocol. Iterator is also Iterable because it has __iter__() method. We can make Iterable object via using this Iterator.

List returns Iterator when use its __iter__() method or call built-in function iter(). This Iterator internally implements two methods(iterator.__iter__() and iterator.__next__()).

a = [1, 2, 3]

print(type(a))
print(type(a.__iter__()))
print(type(iter(a)))

print()

print(dir(a))
print(dir(a.__iter__()))
print(dir(iter(a)))
<class 'list'>
<class 'list_iterator'>
<class 'list_iterator'>

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']


Reference

환경변수를 가져오는 방법 정리


환경

  • Python 3.X 이상


환경변수 가져오기

os 모듈 사용

기본 형태

  • key에 해당하는 환경변수가 있다면 반환한다.
  • key에 해당하는 환경변수가 없다면 default를 반환한다.
  • default의 값이 없고 key에 해당하는 환경변수가 없다면 None을 반환한다.
  • 환경변수 반환값의 타입은 str이다.
os.getenv(key, default=None)

예시

  • 환경변수 설정
export TEST="TEST env value"
export NUM=123
  • 환경변수 가져오기
>>> import os
>>> os.getenv("TEST", "TEST env default value")
'TEST env value'
  • 반환형 확인
>>> import os
>>> type(os.getenv("NUM", "NUM env default value"))
<class 'str'>
  • key 값이 없을때 default를 가져오기
>>> import os
>>> os.getenv("default", "TEST env default value")
'TEST env default value'
>>> type(os.getenv("default", "TEST env default value"))
<class 'str'>
>>> import os
>>> os.getenv("default", 12345)
12345
>>> type(os.getenv("default", 12345))
<class 'int'>
  • keydefault도 없다면 None을 반환
>>> import os
>>> type(os.getenv("NONE TEST"))
<class 'NoneType'>


참고자료