티스토리 뷰

개요

  • Spring Boot 기반 프로젝트에서 DynamoDB를 사용하기 위한 @DynamoDbBean 클래스를 작성하는 방법을 정리하였다.

사전 요구 지식

@Repository 빈 설계 예

import org.springframework.stereotype.Repository
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable
import software.amazon.awssdk.enhanced.dynamodb.Key
import software.amazon.awssdk.enhanced.dynamodb.TableSchema
import software.amazon.awssdk.enhanced.dynamodb.model.QueryConditional
import software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest

@Repository
class CurrencyRepository(
    val dynamoDbEnhancedClient: DynamoDbEnhancedClient
) {
    fun findFirstByCurrencyAndContryOrderByTimestampDesc(currency: String, contry: String): Currency {

        // 쿼리 조건 지정
        val queryConditional = QueryConditional
            .keyEqualTo(
                Key.builder()
                    .partitionValue("${currency}#${contry}")
                    .build()
            )

        val queryEnhanceRequest = QueryEnhancedRequest.builder()
            .queryConditional(queryConditional)
            .limit(1)
            // 정렬 방식 지정
            // true: ASC, false: DESC
            .scanIndexForward(false)
            .build()


        // 일치하는 아이템이 없을 경우 ResourceNotFoundException 예외 발생
        return table()
            .query(queryEnhanceRequest)
            .items()
            .stream()
            .findFirst()
            .get()
    }

    private fun table(): DynamoDbTable<Currency> {

        return dynamoDbEnhancedClient
            .table(
                "currency",
                TableSchema.fromBean(Currency::class.java)
            )
    }
}

INSERT OR REPLACE

  • PutItem 명령은 이름 그대로 아래와 같이 실행할 수 있다. 만약, 동일한 Primary Key을 가진 아이템이 이미 존재할 경우, 별도의 예외를 발생시키지 않고 새로운 아이템으로 교체한다.
fun save(foo: FooDynamoDbBean) {
    table.putItem(foo)
}

INSERT OR UPDATE

  • UpdateItemPutItem과 동작이 비슷하지만 차이점은 이미 아이템이 존재할 경우, 아이템 전체를 교체하지 않고 새로운 아이템의 필드 값을 추가하거나 교체한다.
fun update(foo: FooDynamoDbBean): FooDynamoDbBean {
    table.updateItem(foo)
}

BATCH INSERTS OR REPLACE

  • BatchWriteItem 명령은 아래와 같이 실행한다. 2가지 예외를 고려하여 로직을 작성해야 한다.
  • 첫째, 1개 배치 요청 목록의 아이템 개수가 25개를 초과하면 예외가 발생한다.
  • 둘째, 배치 요청시 실패한 아이템에 대해 별도의 예외를 발생시키지 않는다. 대신 배치 응답에 포함된 unprocessedDeleteItemsForTable(), unprocessedPutItemsForTable()을 통해 실패한 아이템 목록에 대해 재시도 로직을 작성하면 된다.
  • 배치는 PutItem 명령에 비하면 로직 작성이 까다롭지만, 대량의 아이템을 다룰수록 처리 속도가 월등히 빠르다. (평균 175.22바이트 크기의 아이템을 온디맨드 모드로 배치 생성할 경우 1억건이 약 25분에 완료된다.)
fun saveAll(foos: List<FooDynamoDbBean>) {

    // 아이템 목록을 최대 25개의 목록으로 분할
    foos.chunked(25).forEach { aChunkOfFoos ->
        val writeBatchBuilder = WriteBatch
            .builder(FooDbBean::class.java)
            .mappedTableResource(table)

        aChunkOfFoos.forEach { foo ->
            writeBatchBuilder.addPutItem(foo)
        }

        val batchWriteItemEnhancedRequest: BatchWriteItemEnhancedRequest = BatchWriteItemEnhancedRequest
            .builder()
            .writeBatches(writeBatchBuilder.build())
            .build()

        val batchWriteResult = dynamoDbEnhancedClient.batchWriteItem(batchWriteItemEnhancedRequest)

        // 삭제 실패한 아이템 목록을 삭제 처리
        batchWriteResult.unprocessedDeleteItemsForTable(table).forEach { key ->
            table.deleteItem(key)
        }

        // 생성 실패한 아이템 목록을 생성 처리
        batchWriteResult.unprocessedPutItemsForTable(table).forEach { item ->
            table.putItem(item)
        }
    }
}

DELETE

  • DeleteItem 명령은 아래와 같이 실행할 수 있다. 파라메터의 Primary Key 필드 값에 해당하는 아이템을 삭제한 후, 삭제된 아이템을 반환한다.
fun delete(foo: FooDynamoDbBean): FooDynamoDbBean {
    table.deleteItem(foo)
}

예외 처리: 자동 재시도되는 예외 목록

  • DynamoDB는 예외 발생시 SDK 레벨에서 자동으로 정해진 최대 횟수까지 재실행을 요청한다. 아래는 재시도 로직이 작동하는 전체 예외 목록이다. (모두 software.amazon.awssdk.services.dynamodb.model 패키지에 속한다.)
# 400
ItemCollectionSizeLimitExceededException
LimitExceededException
ProvisionedThroughputExceededException
RequestLimitExceeded

# 500
InternalServerErrorException

예외 처리: 500 Internal Server Error

  • 리파지터리 빈에서 CRUD 실행시 아래와 같이 500 Internal Server Error을 의미하는 software.amazon.awssdk.services.dynamodb.model.InternalServerErrorException 예외가 발생할 수 있다. AWS의 내부 문제가 원인으로 애플리케이션의 책임은 없다.
software.amazon.awssdk.services.dynamodb.model.InternalServerErrorException: Internal server error (Service: DynamoDb, Status Code: 500, Request ID: 236OI2K4PJSJK206OMTMV9DQF3VV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: null)
  • 코드에서 점검할 부분은 다음과 같다. PutItem, UpdateItem, DeleteItem 실행시 발생했다면 해당 명령은 완료되었을 수도 있고, 아닐 수도 있다. 따라서 예외 발생시 GetItem으로 해당 명령이 정상적으로 실행되었는지 확인한 후, 해당 명령을 재시도하는 보완 로직이 작성되어야 한다. 만약, TransactWriteItem 실행시 발생했다면 해당 명령은 완료되지 않은 것으로 바로 재시도 로직을 작성하면 된다.

참고 글

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함