쿠케
@kuke
미들 (4~8년)·
백엔드/서버 개발자
수강생
1,994
수강평
216
강의 평점
4.9
멘토링 신청
1
멘토링 리뷰
-
멘토링 평점
-
게시글
질문&답변
Split 전략 강의 중 질문 있어요
SPRING님, 안녕하세요! 어 이거 해결 처리되어 있어서 답변을 놓쳤군요. (제가 잘못 눌렀었는지..?!)이미 해결이 되신걸까요? 답변 드려보면, Split 전략은 자동으로 여러 개의 데이터를 넣도록 확장하기 위한 전략이 아닙니다.Split 전략은 Redis 단일 키 크기의 한계를 해결하기 위함이지,n=1000으로 만들었는데 2000개를 넣으면, 당연히 단일 BloomFilter와 동일하게 오차율이 급격하게 증가하게 됩니다.단일 Bloom Filter에 1억 개의 데이터를 관리하고자 한다면,Redis 단일 키에 대한 저장 크기 한계로 인해 실패하지만,Split 전략으로 인해 여러 개의 키(split)로 분산 되어 거대한 Bloom Filter를 관리할 수 있게 되는 것입니다!물리적으로는 여러 개의 키로 Bloom Filter가 분할되지만, 논리적으로는 1개의 거대한 Bloom Filter를 이루는 것입니다.
- 0
- 2
- 49
질문&답변
23강 5:38 부분 질문 있습니다!
발그레지는개발님, 안녕하세요! 샤딩 자체를 한다고 해서 메모리를 덜 사용한다는 의미가 아니라,n이 커질수록 1에 급격하게 수렴하는 특성으로 인해,더욱 적은 메모리로도 충분히 목표 오차율에 도달할 수 있다는 의미입니다!목표 오차율이 0.01이라면, 0.000001과 0.000000001의 차이를 신경쓸 필요는 없습니다.그래프를 살펴 보시고 이러한 특성을 우선적으로 이해해 보시면 좋을 것 같습니다! 320MB 짜리 1개인 경우와 32MB짜리 10개인 경우의 오차율이 똑같지 않나 하는 생각말씀하신대로 똑같습니다!오차율이 조금 증가하는 대신 메모리를 덜 쓸 수 있는 것 아닌가요?증가한 오차율은 무의미한 수준이고, 비싼 메모리는 덜 쓸 수 있게 됩니다.
- 0
- 1
- 35
질문&답변
23강 17초 부분 질문있습니다~
발그레지는개발님, 안녕하세요! Split 전략은 결국 논리적으로 1개의 블룸 필터를 이루고 있습니다.만약 K개의 해시 함수 결과가 모두 다른 Split 인덱스를 가리킨다면, 각 Split을 모두 조회해야할 수 있습니다!예시로 3개의 Split이 있고, 각 Split의 크기는 1024비트라고 가정해보겠습니다.해시 함수의 결과가 0, 1, 2, 1023, 1025, ... 이런 식으로 나온다면,[0, 1, 2, 1023]에 의해 1번 스플릿 조회, [1025, ...]에 의해 2번 또는 3번 스플릿까지 조회가 필요할 수 있습니다.SplitBloomFilterRedisHandler에서 add, mightContain 메소드를 보시면,해시 함수 결과를 순회하며 findSplitIndex를 반복적으로 수행하게 되는데요,이 경우에 모든 split을 접근할 수도 있다라는 의미로 받아들여주시면 됩니다!
- 0
- 2
- 42
질문&답변
질문이 있습니다!!
희준님, 안녕하세요! 1. RateLimit Boundary Burst 관련말씀하신대로 경계 구간을 연달아 넘어서는 순간 요청 트래픽이 몰릴 수 있습니다.이 부분은 먼저 정책적인 정리가 필요할 것 같은데요,고정된 구간에 대해 N건, 최근 N건, 첫 요청 시점부터의 N건 등 정책에 따라서 구현 방식은 달라지게 됩니다.현재 강의에서 취한 "첫 요청 시점부터의 N건 전략"은 구현이 간단하여 채택한 방식이지만,rate limit의 다양한 구현 방식보단 활용 전략과 구간에 대한 이해를 위해 구성한 것이 의도였던 것이고요.만약 다른 정책으로 가야 한다면 실무 관점에서는 구현 방식을 달리할 수도 있지만,반드시 이러한 정책으로 가야하는지부터 고민해볼 수도 있고,윈도우 구간을 더욱 세분화해서 이중/삼중으로 관리할 수도 있는 것이고(예를 들어, 분단위 정책과 초단위 정책 조합),boundary burst 문제가 우려되지 않을 정도의 가용량을 미리 확보해두는 것도 간단하고 효율적인 방법일 것 같습니다. 2. Request Collapsing 관련Request Collapsing은 전략일 뿐이지, 반드시 모든 요청을 병합할 필요는 없습니다.인스턴스 수가 많지 않거나 또는 많더라도 데이터 소스가 충분히 트래픽을 받쳐줄 수 있다면,별도 리모트 서버에 분산락을 구성하여 인프라, 네트워크 비용 등의 오버헤드를 높이는 것보단 말씀하신 전략으로 충분합니다. 실제로도 그렇게 많이 구성하고 있고요.가벼운 요청의 경우 인스턴스 단위로 처리해도 충분할 수 있으나,데이터 소스가 반드시 1회만 처리해야하는 경우 또는 무거운 요청은,여전히 전체 요청에 대해 1회만 처리하는게 좋을지 고민해볼 수 있을 것 같습니다. 3. Write Through 방식에서의 장애 처리DB에는 쓰기 성공Redis에는 쓰기 실패위 상황을 트랜잭션 실패로 다루어야하는지는 시스템과 데이터에 대한 정책에 따라 다릅니다.어떤 데이터는 정합성이 깨져도 문제가 없을 수도 있고, 지연 처리되어도 문제 없을 수도 있고, 반드시 동시에 처리되어야할 수도 있습니다.구현에 대한 고민보단 정책적인 고민이 먼저 필요할 것으로 보이고,DB와 Redis 간에는 기본적으로 트랜잭션 통합을 지원하지 않기 때문에,반드시 통합시켜야한다면 saga, outbox 등의 분산 트랜잭션을 위한 패턴을 고민하고 구현 방식을 달리해야 합니다.핵심 트랜잭션(DB 쓰기)까지 롤백해야 하는지아니면 캐시는 보조 저장소로 보고 DB 성공을 기준으로 처리해야 하는지두 저장소 간 원자성을 반드시 보장해야 하는 설계인지그래서 위 방법들 모두 필요할 수 있습니다.핵심 트랜잭션(DB 쓰기)까지 롤백해야 하는지 이 부분은 데이터 캐싱에 실패했다고 해서 원본 데이터에 대한 비즈니스까지 실패 처리하는건 대부분의 상황에 애매할 수 있겠네요.(물론, 반드시 데이터가 일치되어야 한다면 필요)추가적으로 아래 전략들도 함께 고민해볼 수 있을 것 같습니다.Redis 복구까지 DB 접근Redis 복구 완료 시에 실패 로그 또는 DB 원본 데이터를 기준으로 정합성 관리정책에 따른 적절한 전략과 해결책은 그저 찾아서 구현하면 된다고 봅니다!
- 0
- 2
- 60
질문&답변
정렬, 필터, 검색 등의 조건이 붙을 경우 최적화할 수 있는 방법이 무엇이 있을까요?
dltkdcksqkqh님, 안녕하세요! 인덱스의 동작 원리를 정확히 이해하고 있으면, 다른 쿼리도 동일한 방식으로 최적화할 수 있습니다.반드시 커버링 인덱스를 사용해야하는 것도 아닙니다.offset 스캔에 대한 우려가 없다면 단일 쿼리로 처리할 수도 있는 것이고요.이 부분은 다른 쿼리 방식도 한번 직접 고민하며 만들어보셔도 좋을 것 같네요!아래 질문들에 대한 답변으로 궁금증들이 어느 정도 해소되길 바랍니다. 정렬되는 컬럼 전부에 대해 인덱스를 걸면 더 문제가 발생할 것 같아요.인덱스를 걸면 추가적인 관리 비용이 생기기 때문에, 반드시 만들어야 하는 것인지부터 고민이 필요합니다.데이터 종류, 규모, 조회 패턴 등을 고려해볼 수 있을 것 같은데요.몇 가지 예시를 보겠습니다. 첫째로, 인덱스 range scan하면서 어느 정도는 조회 시점에 필터링하는 것도 충분할 수도 있는 상황인데요.각 게시글에 해시태그를 설정할 수 있고, 등록 가능한 수가 최대 10개라고만 가정해보겠습니다.해시태그 테이블을 간단히 정의하면, hashtag_id(pk), article_id(fk), hashtag_name 이렇게 구성될 수 있을 것이고요.그리고 게시글에 특정한 해시태그명이 이미 작성되어 있는지 확인하려면,article_id + hashtag_name로 쿼리가 필요할 수 있습니다.그런데 이러한 쿼리를 수행하기 위해서 (article_id, hashtag_name) 인덱스가 반드시 필요할지 고민해볼 수 있을 것 같습니다.어차피 시스템 제약으로 게시글의 해시태그는 최대 10건인 상황입니다.즉, article_id로만 필터링해도 10건 정도는 전체 스캔 해와도 메모리 내에서 빠르게 처리할 수 있으므로, 굳이 hashtag_name까지 인덱스에 포함하지 않을 수 있겠습니다. 또, 인덱스를 만들지 않고 테이블 풀스캔하는 것이 적절한 상황도 있습니다.게시글을 작성할 수 있는 게시판이라는 개념을 생각해보겠습니다.이 게시판은 생성/수정/삭제가 엄청나게 잦지만(쓰기 작업이 엄청 잦다는 예시에 게시판이 적절할지는 모르겠지만 그 부분은 차치하고 대략적인 맥락만 이해해주시면 됩니다!),어차피 사용자가 실제 선택하고 활용할만한 게시판은 최대 수 천개 내에서만 관리된다고 가정해보겠습니다.수 천개 수준에서는 그냥 전체 데이터에 대해 풀스캔으로 처리해도 충분합니다.(중간에 캐시 레이어를 설정할 수도 있는 것이고)괜히 인덱스를 설정하면, 쓰기 작업이 잦은 데이터에 대해 부가적인 인덱스 관리 비용만 커질 수 있습니다. 그래도 쿼리 요구사항이 다양하다면 인덱스를 만드는 것이 불가피할 수 있습니다.하지만 인덱스는 개념적인 것이고, 단일한 RDB에서 간편하게 만들 수 있도록 지원도 해주는 것이지,꼭 RDB의 index 기능을 활용할 필요는 없습니다.별도의 테이블 또는 별도의 DB(아예 다른 DB도 가능)에다가 인덱스를 위한 데이터 구조를 만들고,쿼리 요구사항을 위한 식별자 조회는 별도의 인덱스 데이터 구조(secondary index를 직접 만든다고 생각하면 됩니다)에서 조회 -> 실제 데이터는 원본 DB에서 조회하는 전략을 취할 수도 있습니다.인덱스와 원본 데이터를 분리하고, 애플리케이션에서 조인하게 되는 것이라고 봐주시면 되네요.이러면, 원본 데이터 DB의 인덱스 관리 비용은 별도의 DB로 분산시킬 수 있습니다.관련해서는 제 강의 중 "분산 데이터 모델링"에서도 가볍게 언급되는 부분이라, 참고 차 말씀 드려봅니다!(꼭 들을 필요는 없습니다) 검색 %검색어%의 경우에는 결국엔 full_scan이어서 성능 최적화가 불가능하다.이것도 말씀하신대로 %검색어% 쿼리는 인덱스 최적화가 불가합니다.동작 원리를 생각하면 검색어% 쿼리만 인덱스 최적화가 되는 것이고요.이 부분은 필터링/정렬 등 방식의 인덱싱 방식이 아니라, 검색 엔진의 인덱싱 방식을 참고해보시면 됩니다.역인덱스(inverted index)라는 방식인데요, "[키워드 목록] -> 찾고자하는 식별자"와 같은 형태로 인덱스가 만들어집니다.관련해서는 따로 학습해보셔도 좋을 것 같고,보통 복잡한 검색 기능은 별도의 검색 엔진에다가 데이터를 복제해서 전용으로 검색 DB를 구축하는 것이 일반적이네요!
- 0
- 2
- 71
질문&답변
좋아요 기능 정합성 보장 방법
근것님, 안녕하세요! 일단 1차적으로 사용자 단에서 막는 것부터 시작일 것 같습니다.사용자 클라이언트 애플리케이션에서는, 서버에 요청을 보내고 응답을 받기까지 중복 클릭/터치(따닥)를 막으면, 정상 사용자에 대한 중복 요청은 1차적으로 걸러질 수 있게 됩니다. 또는, 이미 성공한 결과에 대해서 중복 요청을 아예 못보내도록 막을 수도 있겠네요. 하지만 공격자, 별도 스크립트를 통한 요청, 네트워크 문제, 중복 요청에 대해 열려있는 기능 등으로 인해 중복 요청은 당연히 보내질 수 있습니다.서버 애플리케이션에서 이를 막을 방법은 필요하다고 생각할 수 있고 방법들도 너무나도 다양하겠지만,사실 그렇게까지 필요한지는 잘 모르겠습니다.질문 주신 의도는 중복 요청을 다루기 위한 정합성 관점보단, DB까지 트래픽 전파를 막고 싶다는 관점에서 주신 것으로 이해했습니다.그리고 적절한 방법이 무엇인지 질문을 주셨지만, 오히려 아무런 조치도 취하지 않는 것이 적절할 수도 있다는 생각이 있네요.물론, 공격자라면 당연히 막아야합니다. 하지만 이건 중복 요청에 대한 방지책이라기 보단, 공격자 자체를 차단해야하는 문제라서 사실 다른 문제로 보입니다.이미 열려있는 기능 또는 정상적인 사용자 또는 일시적으로 발생하는 네트워크 순단 등으로 인해서 말씀주신 상황이 얼마나 많이 발생할까요?DB까지 중복 요청에 대한 트래픽이 전파된다고 하더라도, 이러한 트래픽이 크게 문제가 될 수준이 된다면, 오히려 시스템 정책과 DB 스펙에 대해서 다시 검토가 필요할 것 같습니다.굳이 만들고자 한다면 무상태 서버 애플리케이션에 어떠한 상태를 관리해야 할텐데, 서버 애플리케이션에서 이를 위한 시스템을 구축하는 것은 오히려 메모리 관리도 어렵고 낭비일 수도 있을 것 같네요. 그렇다고 별도 리모트 시스템을 구축하자니 이것도 리모트 시스템에 관리 비용은 늘어나게 됩니다.로컬 메모리에 중복을 관리하든, 리모트 저장소에 중복을 관리하든, 중복에 대한 상태가 저장되기까지 중복 요청은 여전히 있을 수 있으니, 결국에 원점으로 돌아가는 셈입니다.exists -> insert 의 동시성 문제는 찰나일 수 있고 대부분의 정상 요청에서는 exists에서 막히게 될 것으로 기대하므로,이것 자체가 애플리케이션에서 취할 수 있는 가장 간단하고 적절한 방법으로 생각됩니다. 제가 질문에 대한 의도와 상황을 모두 제대로 이해하고 답변드린지는 모르겠습니다.혹시 더 궁금한 점 있으시면 편히 남겨주세요!모든 상황을 가정하다보면 끝도 없어질 수 있어서, 좋아요라는 도메인과 지금 시스템 동작 방식 내에서 어느 정도 선을 그은 부분도 있는데요, 왜 문제로 생각했는지 구체적인 예시를 고민해 주셔도 좋을 것 같네요!
- 0
- 2
- 68
질문&답변
좋아요 동시성처리 최적의 선택?
쵸잉님, 안녕하세요! 1.강의에서는 비관적 락과 낙관적 락을 다루셨는데, 일반적으로 대규모 서비스가 아닌이상 좋아요 자체가 순식간에 많은 트래픽이 몰릴것같지않아 낙관락으로 처리하는 것이 더 효율적일것같다고 생각이듭니다.말씀하신대로 트래픽이 엄청 몰리지 않는다면 낙관적 락으로도 충분할 수 있습니다. 다만, 낙관적 락 특성 상 정상 사용자도 동시 요청 시에 실패 응답을 받을 수 있으므로, 사용성과 시스템 신뢰에 문제가 생길 수 있습니다. 이를 위해 간단하게 재처리 1~2회 정도만 넣어줘도 실패할 확률은 대폭 줄일 수 있게 되네요. 근데 또 다시 생각해보면, 어차피 트래픽이 적다면 비관적 락으로도 충분할 수 있는 것이고요. 굳이 낙관적 락으로 사용자에게 실패 상황을 만들어줄 필요는 없을 것 같습니다!그래도 극단적인 상황을 대비해서, 뒤에서 나오는 조회수 처리처럼 레디스로 좋아요 수를 증가시키고 스케줄링같은걸로 RDB에 백업하는 방식은 어떤가요?조회수처럼 레디스/RDB 백업 전략을 취할 수도 있겠지만, 조회수에서 제시한 전략은 유실에 대해 열려 있었습니다. 그리고 좋아요를 위한 RDB와 좋아요 수를 위한 Redis는 트랜잭션 통합을 지원하지 않습니다.또, 레디스와 스케줄링 시스템을 위해서 시스템 및 구현 복잡도는 더욱 올라갈 수 있는 것이고요. RDB만으로 충분하다면, 인프라 복잡도를 올리는 것보다 현재 가용한 자원을 활용하는 것이 좋다고 생각됩니다! 2.동시성처리에서 비관적 락으로만 처리해야 하는 상황이 있을까요? 레디스의 분산 락을 사용하는 것이 성능 측면에서 비관락보다 유리할 때도 있을 것 같은데, 실제로 비관락을 반드시 써야 하는 예시나 사례가 궁금합니다.락을 위해 레디스 등을 활용할 수도 있고 RDB에서 락에 대한 부하를 감소시킬 수 있다는 장점은 있겠지만, 인프라 복잡도와 서버 및 운영 비용 증가에 대한 부분도 고려해야할 것 같습니다.레디스도 결국에 네트워크 통신을 거쳐서 락을 획득해야 합니다. 이러한 경우에 RDB로 처리하는게 오히려 더 빠른 선택지가 될 수 있습니다.또, 레디스에 장애가 발생 시에 락이 유실될 수 있는 것이고요. 이 경우에 데이터에 대한 정합성을 반드시 보장할 수 없을 수 있고, 레디스 복구까지 작업 처리에 지연이 생길 수 있습니다. 이러한 경우 RDB로만 처리하는 것이 유리할 것으로 생각되네요!예를 들어, 레디스 락 획득 -> 결제 -> 레디스 장애로 락 유실(장애 시에 복구까지 아예 지연시킬 수 있으나, 레플리카가 마스터로 승격되어서 장애는 복구되었지만 비동기 복제 지연으로 인해 유실되는 상황을 가정할 수도 있겠음) -> 중복된 요청이 다시 레디스 락 획득 -> 결제.. 이러한 시나리오를 생각해볼 수 있을 것 같습니다. 3.RDB와 레디스는 트랜잭션이 통합되지 않기 때문에(다른 시스템이고, 레디스는 준비 과정 없고 트랜잭션 지원 안하기 때문에 2PC 지원 안함) @Transactional로 묶을 필요도 없고 묶지 않는 것이 좋습니다. 레디스에서 응답 지연이 생긴다면, 트랜잭션을 계속 점유하며 레디스 응답을 기다리게 되고, 여러 비슷한 요청이 중첩된다면 커넥션 풀도 고갈나고, 결국 DB 또는 애플리케이션까지 장애가 전파될 수 있는 위험이 있습니다.이러한 문제를 방지하기 위해서 레디스 요청은 트랜잭션 범위 바깥으로 꺼내는 것이 일반적이고요.유실에 대해 별다른 위험성이 없는 경우를 가정한다면, 꼭 트랜잭션을 통합으로 관리할 필요는 없습니다.대부분의 상황에는 유실이 발생하지 않고, 드물게 발생하는 문제 시에 로그나 원본 데이터 기반으로 복구하면 충분할 수 있습니다.정합성을 반드시 맞춰야 한다면, 강의에서 제시한 Outbox 패턴 등을 활용해서 이벤트를 발행하고 레디스에 동기화해줄 수 있습니다. 다만, 이 경우는 비동기에 대한 고려가 필요하기 때문에 시스템 정책에 대한 검토가 필요할 수는 있습니다!
- 1
- 2
- 85
질문&답변
프론트엔드 msa 환경 api 주소 통합? 과 서버끼리 통신 방식에대해
쵸잉님, 안녕하세요! 요즘 본업으로 정신이 없어서 질문이 올라온지도 모르고 있었네요;; 일단 답변이 늦어서 죄송합니다. 포트 번호로 분리되었다는 것이, 단일 서버에서 여러개의 애플리케이션을 띄우는 상황을 말씀하신 것으로 이해했습니다. 말씀하신대로 path 기반으로 라우팅하는 것이 무난하고 일반적인 방법일 것 같습니다! (꼭 nginx가 아니어도 게이트웨이 역할을 하는 서버를 의미합니다.) 중간 게이트웨이에서 별도 도메인을 발급해서 관리하고 있다면, 도메인 단위로 분리할 수도 있는 것이고요.CQRS는 서버 통신 방식에 대한 관점 보단, Command와 Query의 분리 관점에서 봐주시면 좋을 것 같습니다. 말씀하신 http 통신과 카프카 방식의 차이에 대해서는, 동기와 비동기의 관점으로 바라보는 것이 좋을 것 같고요! 현재 article-read에서는 http와 kafka 두 방식을 모두 취하고 있습니다. 조회 트래픽 전파를 막기 위해 비동기로 데이터를 받아서 쿼리 모델을 만들어주고 있지만, 네트워크 또는 장비 이슈 등 항상 장애 없이 이상적으로 동작하진 않기 때문에, 쿼리 모델의 동기화 지연에 대한 대비책으로 http 통신 전략도 겸할 수 있는 것입니다. 두 방식은 목적이 다르기 때문에 각기 적용될 수 있는 것입니다. 또, 클라이언트와 서버는 상대적인 개념인데요(물론, 클라이언트가 사용자 기기를 말씀하신 부분은 이해하고 있습니다). 서버 간 통신이더라도 요청을 보내는 쪽은 클라이언트, 요청을 받는 쪽은 서버가 됩니다. 그리고 클라이언트와 서버 통신을 위한 프로토콜은 다양하지만, 강의에서는 http 1.1을 활용할 뿐이고요. 동기식, 범용적, 자주 사용되기 때문에 이해가 쉽고 구현이 간단하다는 장점은 있지만, 꼭 http 1.1을 활용할 필요는 없습니다. 성능이나 비용 측면에서 grpc(http 2.0 위에서 동작)도 자주 사용되는 선택지입니다!
- 0
- 2
- 73
질문&답변
안녕하세요 무한스크롤 강의듣다가 질문이 있습니다.
동훈님, 안녕하세요! 쿼리 플랜에서 나오는 rows는 통계치 기반이고 실제로 그렇게 스캔하는게 아니므로 문제될 부분은 없습니다.줄이는 방법에 대해서 고민해본 적은 없고, 줄여야하는 이유에 대해서도 크게 신경 쓸 필요는 없을 것 같습니다.플랜은 실제로 스캔하는게 아니라 말 그대로 예상치를 기반으로 만들어진 계획으로 인지하시면 됩니다.반드시 플랜과 동일한 방식으로 수행되는 것도 아니고요.rows 수가 강의와 다르게 나오는 것에 대해서도, 각 환경의 데이터 분포나 통계치 갱신 시점 등에 따라서 차이가 있다고 봐주시면 될 것 같습니다.실제 쿼리는 LIMIT만큼 스캔하고 종료됩니다!
- 0
- 1
- 46
질문&답변
조회수 조회 로직 질문
쵸잉님, 안녕하세요! 말씀하신대로 레디스에 값이 없을 경우, 백업용 rdb를 추가로 확인해볼 수도 있습니다.해당 부분은 구현하기 나름이고 전략적인 부분으로 봐주시면 될 것 같네요.실시간으로 백업용 rdb를 조회해서 자동으로 복구 가능하도록 하거나,사람이 수동 개입해서 자동으로 복구 가능하도록 하거나 등 어떤 정책을 삼을지는 개발자의 선택이 될 수 있을 것 같습니다.일단 백업용 rdb는 이름 그대로 백업을 위해 설계된 것이고, 현 설계 특성 상 항상 데이터가 동일하진 않습니다.레디스 자체에서 복구 가능한 문제일 수 있는데, rdb로 모든 트래픽을 유입시키거나 레디스의 데이터를 아예 날려버리는게 문제가 되는 선택일 수 있습니다.그래서 정책을 정하기 나름인 것이고, 비슷한 개념으로 standby와 replica 차이에 대해서도 한번 고민해보시면 좋을 것 같네요!
- 1
- 2
- 52




