우리를 거의 파산 한 5 초의 데이터 지연 (그리고 우리가 밀리 초로 어떻게 고정했는지)

우리를 거의 파산 한 5 초의 데이터 지연 (그리고 우리가 밀리 초로 어떻게 고정했는지)

한 달도 채되지 않아, 우리 팀은 기만적으로 간단하게 들리는 도전에 직면했습니다. 당면한 작업은 우리가 유지 한 특정 재무 플랫폼에 대해 실시간으로 트랜잭션을 모니터링하는 대시 보드를 만드는 것이 었습니다. 원칙적으로 간단했습니다. 이벤트 데이터는 DynamODB에 넣을 것이며 OpenSearch는 실시간 분석에 사용될 것입니다.

간단한 기대가있었습니다. DynamoDB에 기록 된 모든 단일 이벤트마다 OpenSearch는 즉시 분석 할 준비가되어 있어야합니다. 지연이 없습니다. 불필요한 대기가 없습니다.

그리고 이것은 우리가 완전히 잘못 계산 한 곳입니다.

“실시간”이 아니었을 때 정말 실시간

첫 데모 중에 CTO의 반응을 생생하게 기억합니다. “왜 그런 지연이 있습니까?” 그는 대시 보드를 가리키면서 거의 5 초 전에 지표를 보여주고 있었다.

우리는 1 초 미만의 대기 시간을 선택했습니다. 우리가 제공 한 것은 때때로 트래픽이 급증하는 동안 3-5 초 뒤에있는 시스템이었습니다. 때로는 더 나빴습니다. 금융 모니터링에서 이러한 종류의 지연은 몇 시간만큼 좋습니다. 사전 또는 반응성? 시스템 실패는 즉시 감지되는 것과 고객 불만 이후 발견 되었습니까? 그것이 진정한 차이입니다.

일부 과감한 변화가 필요했고 빠르게 필요했습니다.

우리의 첫 번째 시도 : 전통적인 배치 접근

우리 앞에 온 다른 팀과 마찬가지로, 우리도 친숙한 영토에 의지했습니다.

  1. 5 초마다 실행을 위해 AWS Lambda 작업을 예약하십시오.
  2. DynamoDB에 저장된 새 레코드를 검색하게하십시오.
  3. 이 업데이트를 수집하여 배치로 그룹화하십시오.
  4. 배치를 푸시하여 인덱싱을 OpenSearch하십시오.

그것은 효과가있었습니다. 거의. “일”과“성취”는 서로 완전히 이혼하는 두 가지 용어라는 것이 문제입니다.

그런 인상적인 속도로 물건이 추락하고 불에 타 버렸습니다.

  • 본질적으로 유도 된 지연: 5 초 동안 새로운 데이터는 단순히 DynamoDB에 전송되기를 기다렸으며 이동할 수 없었습니다.

  • 인덱싱의 대기 시간 : OpenSearch에서 배치하면 쿼리가 크게 지연되어 점점 느려집니다.

  • 신뢰성 문제 : 중간 배치가 충돌하면 배치의 모든 업데이트는 불가능하다는 것을 의미했습니다.

특히 성가신 한 사례 동안, 우리 시스템은 배치 고장에 포함 되었기 때문에 유도 적으로 특정 기본 오류 이벤트를 놓쳤다. 문제가 진단 될 때까지 수천 건의 거래가 이미 시스템을 통과했습니다.

“하나님의 사랑을 위해 이것은 지속 가능하지 않습니다.” 그는 또 다른 보고서를 회상 한 후에도 계속되었습니다. “시스템을 근본적으로 변경하고 배치 대신 라이브로 스트리밍 할 수 있도록해야합니다.”

그는 매우 정확했다.

솔루션 : 실시간 스트리밍 업데이트가 발생합니다

나는 어느 날 밤 오랫동안 AWS 문서 작업을하고 있었고 솔루션은 Dynamodb Streams를 강타했습니다.

DynamODB 테이블로의 모든 단일 변경을 캡처하고 처리 할 수 ​​있다면 두 번째로 일정에 따라 배치로 업데이트를 가져 오는 대신 발생했습니다.

이것은 우리가 더 나은 운영 방식을 완전히 변화 시켰습니다.

  • 레코드의 삽입, 수정 및 제거를 캡처하기 위해 DynamODB 스트림 설정

  • 이러한 변경 사항을 즉시 처리 할 AWS Lambda 기능 추가

  • 업데이트를 OpenSearch로 푸시하고 데이터에 대해 약간의 가벼운 처리를 수행하십시오.

초기 테스트에서 결과는 믿어지지 않았습니다. 대기 시간은 3-5 초에서 500ms 미만으로 떨어졌습니다. 오전 3시에 팀 슬랙에 보낸 메시지를 잊지 못할 것입니다.

실제로 작동하게합니다

이것은 우리가 개념 증명 파이프 라인을 작동시켜야하는 소프트웨어 엔지니어링 할당 또는 설계 프로젝트가 아닙니다. 커피 산을 통한 잠들지 않는 밤 중 하나에서 우리는 문제를 세 가지 실행 가능한 단계로 분해했습니다. 첫 번째는 DynamoDB의 변화에 ​​대해 알림을받는 것이 었습니다.

DynamODB의 변경 사항에 대해 알림 받기

도전 1 위는 DynamodB에서 무언가가 바뀌 었다는 것을 어떻게 알 수 있습니까? 인터넷 검색 후, 나는 DynamoDB 스트림을 활성화해야한다는 것을 발견했습니다. 결과적으로, 그것은 처음에 나에게 고통스러운 명령 이었지만 하나의 CLI 명령이었다.

aws dynamodb update-table \
    --table-name RealTimeMetrics \
    --stream-specification StreamEnabled=true,StreamViewType=NEW_IMAGE

나는 여전히 오후 11시에 동료들 에게이 일을 할 때 얼마나 흥분했는지에 대해 이야기하는 것을 기억합니다.“작동 중입니다! 테이블은 모든 변경 사항을 스트리밍하고 있습니다!”

변경 이벤트 리스너 생성

그리고 이제 스트림이 활성화되면 이러한 이벤트를 잡을 수있는 것이 필요했습니다. 따라서 우리는 람다 기능을 만들었습니다. DynamoDB가 변경 사항을 알리기를 기다립니다.

import json
import boto3
import requests

OPENSEARCH_URL = "
INDEX_NAME = "metrics"
HEADERS = {"Content-Type": "application/json"}

def lambda_handler(event, context):
    records = event.get("Records", [])

    for record in records:
        if record["eventName"] in ["INSERT", "MODIFY"]:
            new_data = record["dynamodb"]["NewImage"]
            doc_id = new_data["id"]["S"]  # Primary key
              
            # Convert DynamoDB format to JSON
            doc_body = {
                "id": doc_id,
                "timestamp": new_data["timestamp"]["N"],
                "metric": new_data["metric"]["S"],
                "value": float(new_data["value"]["N"]),
            }

            # Send update to OpenSearch
            response = requests.put(f"{OPENSEARCH_URL}/{INDEX_NAME}/_doc/{doc_id}", 
                                   headers=HEADERS, 
                                   json=doc_body)
            print(f"Indexed {doc_id}: {response.status_code}")

    return {"statusCode": 200, "body": json.dumps("Processed Successfully")}

지금 간단하게 보이면 코드는 쓰기가 그렇게 간단하지 않았습니다. 작동하는 데 세 번의 시도가 필요했습니다. DynamODB의 응답 형식을 무시하고 있었기 때문에 시간 초과로 인해 초기 시도가 지속적으로 충돌했습니다.

가르치기 위해 OpenSearch를 가르치십시오

그 마지막 문제는 해결하기 가장 어려운 문제 였고 우리를 경비원에서 잡았습니다. OpenSearch를 즉시 업데이트하더라도 업데이트를 실시간으로 사용할 수 없었습니다. OpenSearch는 단순성을 위해 자체 배치 기술을 사용합니다.

“이것은 말이되지 않습니다.”우리 팀원은 신음했다. “우리는 실시간 데이터를 보내고 있으며 실시간으로 표시되지 않습니다!”

curl -X PUT " -H 'Content-Type: application/json' -d ' { "index": { "refresh_interval": "500ms", "number_of_replicas": 1 } }'

약간의 연구 및 시행 착오 후, 우리는 수정하는 데 필요한 매개 변수를 찾았습니다. 이 수정을하는 것은 큰 차이를 만들었습니다. *OpenSearch에 새로 고침주기를 기다리지 않고 0.5 초 이내에 새로운 데이터를 사용할 수 있도록 지시했습니다. DynamoDB에서 만들어진 후 첫 번째 이벤트가 밀리 초에 첫 번째 이벤트가 나타 났을 때 나는 자리에서 뛰어 내리려고했습니다.”

결과 : 초에서 밀리 초

새로운 제작 시스템과 함께 첫 주에는 우리에게 많은 것을 가르쳐주었습니다. 대시 보드는 더 이상 현실과 비동기식이 아니라 완전히 기능하는 서비스였습니다.

우리는 달성했습니다.

  • 평균 대기 시간

  • 더 이상 배치 지연이 없음 – 변화의 전파는 즉각적이었습니다.

  • 제로 인덱싱 병목 현상 – 더 작고 빈번한 업데이트가 더 효율적이었습니다.

  • 전체 시스템 탄력성 향상 – 더 이상 ‘전부 또는 전혀’배치 실패

업데이트 된 대시 보드를 리더십과 공유했을 때 즉시 변경 사항을 알았습니다. 차이는 명확했다. 우리의 CTO는“이것은 우리가 처음부터 필요한 것입니다.”라고 언급했습니다.

추가 스케일링 : 트래픽 피크 관리

새로운 접근 방식만큼이나 정상적인 트래픽이었을 때, 극단적 인 사용 스파이크 중에 어려움이있었습니다. 하루가 끝날 때 화해 기간 동안 이벤트 비율은 초당 수백에서 수천까지 증가합니다.

이 문제를 완화하기 위해 Amazon Kinesis Firehose를 버퍼로 추가했습니다. Lambda가 모든 단일 업데이트를 OpenSearch로 직접 보내는 대신 Firehose로 데이터를 스트리밍하도록 변경했습니다.

firehose_client = boto3.client("firehose")

firehose_client.put_record(
    DeliveryStreamName="MetricsStream",
    Record={"Data": json.dumps(doc_body)}
)

Firehose는 파이프 라인의 실시간 특성을 손상시키지 않고 처리량 요구 사항에 응답하여 자동으로 확장하여 OpenSearch를 위해 배송을 처리했습니다.

배운 교훈 : 속도를위한 탐구는 계속됩니다

우리는 실시간 데이터 시스템을 사용하면 지연 시간을 줄이기 위해 지속적으로 노력하는 것이 끝없는 전투라는 것을 배웠습니다. 우리는 지금 더 열심히 노력하고 있으며, 우리는 500ms로 측정 기준 지연을 얻었습니다.

  • Lambda 대신 OpenSearch 파이프 라인에서 변환 단계 실행

  • 빈번한 쿼리를 더 빠르게 검색하기 위해 AWS Elasticache를 사용합니다

  • 전 세계에 퍼지는 사용자를위한 Edge Computing을 고려합니다

재무 모니터링에서는 모든 마이크로 초 카운트가 있습니다. 선임 엔지니어 중 한 사람이 말했듯이 “모니터링에있어 즐거운 곳이 아닙니다. 문제보다 앞서 나가는 것입니다. 중간에 없습니다.”

무엇을 시도 했습니까?

실시간 데이터 문제의 문제를 해결해 보셨습니까? 당신은 그로 인해 무엇을 했습니까? AWS와 함께 가장자리에 대한 모험에 대해 배우고 싶어합니다.

출처 참조

Post Comment

당신은 놓쳤을 수도 있습니다