본문 바로가기
DevOps/AWS

[AWS] S3 객체 전체 조회(나열), S3 고아 파일 삭제

by 계범 2023. 7. 21.

문제

S3 내에 필요 없는 파일들이 많은 것 같아서 원인 파악 및 해결, 파일 삭제를 진행하게 되었다.

원인 파악 및 로직 수정은 완료했고,

더보기

원인 및 해결방법은 실제 삭제 로직이 없는 경우는 삭제 로직을 추가했고,

스케줄러를 통해 해당 파일들의 생명주기가 다했을 때, 체크하여 삭제하게 했다.

 

DB랑 S3랑 같은 트랜잭션으로 묶었는데,

S3를 뒤로 나둬서 S3작업 중 터질 시 DB롤백하는 구조로 일부 안되어있던걸 변경했다.

실제 필요없는 파일들은 삭제하는 작업을 진행하는데,

해당 prefix(가상 폴더) 내 파일들은 전체 조회하는데 정상으로 조회되지 않았다..

 

15분이 지나도 조회가 되지 않길래 먼저 s3 브라우저를 통해 파일이 얼마나 있는지 확인했고,

해당 prefix에만 33만개 파일이 존재하는 것을 확인했다.

1000개씩 조회하고 오래걸려서 1초 한번 조회한다고 해도 330초인데 너무 오래걸린다고 생각했구,

실제 조회해오는 객체 경로를 보니, 동일한 1000개를 계속 조회하는 중으로 보였다.

 

원본 코드

public static List<String> getAllKeys(Bucket bucket, String prefix) {

        try (S3Client s3Client = getS3Client(bucket)) {

            ListObjectsRequest listObjectsRequest = ListObjectsRequest.builder()
                    .bucket(bucket.getBucketName()).prefix(prefix).build();
            ListObjectsResponse listObjectsResponse;
            List<String> keys = new ArrayList<>();
            do {

                listObjectsResponse = s3Client.listObjects(listObjectsRequest);
                keys.addAll(listObjectsResponse.contents().stream().map(S3Object::key).collect(Collectors.toList()));
                listObjectsRequest = ListObjectsRequest.builder()
                        .bucket(bucket.getBucketName()).prefix(prefix).marker(listObjectsResponse.nextMarker()).build();

            } while (listObjectsResponse.isTruncated());

            return keys;

        } catch (S3Exception e) {

            throw new MessageException(ERROR.FILE_COPY_FAIL);

        }

    }

기존 코드이구, 해당 반환 객체인 listObjectsResponse의 nextMarker를 보니 null로 반환되고 있었다.

찾아보니 그냥 버그인거 같구, 그래서 AWS로 가서 찾아보니 개선된 버전이 존재하였다.

 

개선 코드

public static List<String> getAllKeys(Bucket bucket, String prefix) {

        try (S3Client s3Client = getS3Client(bucket)) {

            List<String> keys = new ArrayList<>();

            ListObjectsV2Request listReq = ListObjectsV2Request.builder()
                    .bucket(bucket.getBucketName())
                    .maxKeys(1000)
                    .prefix(prefix)
                    .build();

            ListObjectsV2Iterable listRes = s3Client.listObjectsV2Paginator(listReq);
            listRes.stream()
                    .forEach(r ->
                            keys.addAll(
                                    r.contents()
                                            .stream()
                                            .map(S3Object::key)
                                            .collect(Collectors.toList())
                            )
                    );

            return keys;

        } catch (S3Exception e) {

            throw new MessageException(ERROR.FILE_COPY_FAIL);

        }

    }

기존 while 문을 통한 반복 코드에서,
ListObjectsV2Request 객체를 통해 반복 조회를 다해서 가져오는 것을 볼 수 있었다.

 

기존 코드에 비해 조회속도도 개선되었다.

참고로 1번 조회할땐 최대 수는 1000개이구, 해당 지정이 maxKeys셋팅이다.

 

참조

https://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/java_s3_code_examples.html

 

https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/ListObjectsPaginated.java

 

https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#listObjects(software.amazon.awssdk.services.s3.model.ListObjectsRequest)

 

댓글