GH GambleHub

정확히 한 번 vs 적어도 한 번

1) 의미론에 대해 토론하는 이유

배달 의미론은 수신자가 충돌 및 복귀 할 때 메시지를 얼마나 자주 볼 것인지를 결정합니다

반복없이 최대한 한 번에 손실이 발생할 수 있습니다 (거의 허용되지 않음).
적어도 한 번은 잃지 않지만 복제가 가능합니다 (대부분의 중개인/대기열의 기본값).
정확히 한 번-각 메시지는 관찰 된 효과 측면에서 정확히 한 번 처리됩니다.

주요 진실: 글로벌 거래와 동기식 일관성이없는 분산 세계에서는 "깨끗한" 엔드 투 엔드 정확히 한 번은 달성 할 수 없습니다. 우리는 효과적으로 정확하게 한 번만 구축합니다. 운송에 대한 반복을 허용하지만 관찰 된 효과가 "한 번처럼" 만들도록 처리를 imempotent로 만듭니다.


2) 실패 패턴과 중복이 발생하는 위치

다음으로 인해 재생이 나타납니다

손실 ack/커밋 (프로듀서/브로커/컨 서머는 "확인을 듣지 못했습니다").
지도자/복제품의 재선거, 네트워크 중단 후 복구.
모든 지역에서 타임 아웃/후퇴 (kliyent → 브로커 → konsyumer → 싱크).

결과: 운송의 "고유성" 에 의존 할 수 없습니다. 효과 관리: 데이터베이스에 쓰기, 돈을 삭제, 편지 발송 등


3) 공급자와 정확히 무엇인가

3. 1 카프카

벽돌 제공:
  • Idempotent Producer ('활성화. demempotence = 참 ') -수축 할 때 생산자 측의 중복을 방지합니다.
  • 트랜잭션-메시지를 여러 배치로 원자 적으로 게시하고 소비 오프셋 ("갭" 이없는 읽기 프로세스 쓰기 패턴) 을 커밋합니다.
  • 압축-마지막 값을 키별로 저장합니다.

그러나 "체인의 끝" (싱크: DB/결제/메일) 에는 여전히 demempotency가 필요합니다. 그렇지 않으면 핸들러의 더블은 효과를 두 배로 만듭니다.

3. 2 NATS/토끼/SQS

기본값은 ack/reduction에서 적어도 한 번입니다. 정확히 한 번은 열쇠, 데드 스토어, upsert와 같은 응용 프로그램 수준에서 달성됩니다.

결론: 정확히 한 번의 전송은 정확히 한 번의 효과입니다. 후자는 핸들러에서 수행됩니다.


4) 적어도 한 번 이상 효과적으로 정확하게 한 번 구축하는 방법

4. 1 이데올로기 키

각 명령/이벤트에는 'payment _ id', 'order _ id # step', 'saga _ id # n' 과 같은 자연스러운 키가 있습니다. 핸들러:
  • "이미 보았습니까?" 확인 -TTL/Retsch의 Dedup-stor (Redis/DB).
  • 당신이 보았을 때, 이전에 계산 된 결과를 반복하거나 작동하지 않습니다.
리디스 스케치:
lua
-- SET key if not exists; expires in 24h local ok = redis.call("SET", KEYS[1], ARGV[1], "NX", "EX", 86400)
if ok then return "PROCESS" else return "SKIP" end

4. 베이스에서 2 업저트 (dempotent cink)

출품작은 버전/양을 확인하는 UPSERT/ON CONFLICT를 통해 이루어집니다.

PostgreSQL:
sql
INSERT INTO payments(id, status, amount, updated_at)
VALUES ($1, $2, $3, now())
ON CONFLICT (id) DO UPDATE
SET status = EXCLUDED.status,
updated_at = now()
WHERE payments.status <> EXCLUDED.status;

4. 3 거래 전송/받은 편지함

인터페이스: 비즈니스 트랜잭션 및 이벤트 게시 항목은 동일한 데이터베이스 트랜잭션에서 발생합니 백그라운드 게시자는 아웃 박스를 읽고 브로커에게 보냅니다 → 상태와 이벤트 사이에 불일치가 없습니다.
받은 편지함: 들어오는 명령의 경우 '메시지 _ id' 및 실행 전의 결과를 저장하십시오. 재 처리는 기록을보고 부작용을 반복하지 않습니다.

4. 4 일관된 체인 처리 (읽기 → 프로세스 → 쓰기)

Kafka: 거래는 하나의 원자 블록에서 "오프셋 → 는 → 커밋의 결과를 기록했습니다".
트랜잭션없이: "먼저 결과/받은 편지함을 적은 다음 ack"; 충돌시 복제본에는받은 편지함이 표시되고 종료되지 않습니다.

4. 5 SAGA/오프셋

demempotency가 불가능한 경우 (외부 제공 업체가 돈을 썼을 때) 보상 작업 (환불/공허) 과 dempotent 외부 API를 사용합니다 (동일한 'Idempotency-Key' 로 반복되는 'POST' 는 동일한 결과를 제공합니다).


5) 적어도 한 번은 충분할 때

키 기반 압축으로 캐시/구체화 된보기의 업데이트.
재증가가 허용되는 카운터/메트릭스 (또는 버전으로 델타 저장).
보조 문자가 중요하지 않은 위치를 알기 (어쨌든 키를 넣는 것이 좋습니다).

규칙: 이중이 비즈니스 의미를 변경하지 않거나 → 적어도 한 번 + 부분 보호를 쉽게 찾을 수 있습니다.


6) 성능 및 비용

정확히 한 번 ("효과적으로") 더 많은 비용이 듭니다. 추가 레코드 (받은 편지함/전송), 저장 키, 거래, 진단이 더 어렵습니다.
적어도 한 번은 더 저렴하고 단순하며 처리량/p99에서 더 좋습니다.
평가: 이중 × 확률의 두 배 대 보호 비용.


7) 샘플 구성 및 코드

7. 1 카프카 생산자 (demempotence + traveles)

properties enable.idempotence=true acks=all retries=INT_MAX max.in.flight.requests.per.connection=5 transactional.id=orders-writer-1
java producer.initTransactions();
producer.beginTransaction();
producer.send(recordA);
producer.send(recordB);
// также можно atomically commit consumer offsets producer.commitTransaction();

7. 받은 편지함 콘솔 2 개 (의사 코드)

pseudo if (inbox.exists(msg.id)) return inbox.result(msg.id)
begin tx if!inbox.insert(msg.id) then return inbox.result(msg.id)
result = handle(msg)
sink.upsert(result)     # идемпотентный синк inbox.set_result(msg.id, result)
commit ack(msg)

7. 3 HTIdempotency-Key (외부 애플리케이션)


POST /payments
Idempotency-Key: 7f1c-42-...
Body: { "payment_id": "p-123", "amount": 10.00 }

동일한 키로 반복되는 POST → 동일한 결과/상태.


8) 관찰 가능성 및 지표

'복제 _ trides _ total' -이중이 몇 번이나 잡혔습니까 (받은 편지함/Redis에 따름).
'idempotency _ hit _ rate' - demempotency에 의한 반복 "저장" 비율.
'txn _ reditate _ rate' (Kafka/DB) -롤백의 비율.
'outbox _ backlog- 출판 지연.

(PHP 3 = 3.0.6, PHP 4)

감사 로그: '메시지 _ id', 'demempotency _ key', 'saga _ id', '시도'.


9) 테스트 플레이 북 (게임 데이)

재생 보내기: 인공 타임 아웃이있는 프로듀서 재생.
"싱크대와 ack" 사이의 충돌: 받은 편지함/Upsert가 이중을 방지해야합니다.
재배송: 브로커의 재배송 증가; 데드 업을 확인하십시오.
외부 API의 이념성: 동일한 키로 반복 된 POST가 동일한 답변입니다.
납 변경/네트워크 중단: Kafka 거래/소비자 행동을 확인하십시오.


10) 반 패턴

운송에 동의하십시오: "우리는 정확히 한 번만 Kafka를 가지고 있으므로 키없이 할 수 있습니다" -아니요.
기록하기 전에 No-op ack: 포장되었지만 싱크대는 → 손실을 떨어 뜨 렸습니다.
DLQ/지터 퇴각 부족: 끝없는 리플레이와 폭풍.
자연 키 대신 랜덤 UUID: 중복 할 것이 없습니다.
인덱스가없는 생산 테이블 (핫 락 및 p99 테일) 과 함께받은 편지함/편지함 혼합.
외부 제공 업체에서 imempotent API가없는 비즈니스 거래.


11) 선택 점검표

1. 이중 가격 (돈/법률/UX) vs 보호 가격 (대기 시간/복잡성/비용).
2. 자연스러운 이벤트/운영 키가 있습니까? 그렇지 않은 경우 안정적인 것을 생각해보십시오.

3. 싱크대는 Upsert/verioning을 지원합니까? 그렇지 않으면-받은 편지함 + 보

4. 글로벌 거래가 필요합니까? 그렇지 않은 경우 SAGA로 분류하십시오.
5. 재생/장기 유지가 필요하십니까? 카프카 + 전송. 빠른 RPC/낮은 대기 시간이 필요하십니까? NATS + Idempotency-Key.
6. 멀티 테넌시 및 할당량: 키/공간 격리.
7. 관찰 가능성: demotency 및 백 로그 메트릭이 포함되어 있습니다.


12) FAQ

Q: "수학적" 정확히 한 번 엔드 투 엔드를 달성 할 수 있습니까?

A: 하나의 일관된 상점과 거래가있는 좁은 시나리오에서만. 일반적으로 아니오; demempotency를 통해 효과적으로 정확하게 한 번 사용하

Q: 더 빠른 것은 무엇입니까?
A: 적어도 한 번. 정확히 한 번 트랜잭션/키 스토리지 → p99 및 비용을 추가합니다.

Q: demempotence 키를 어디에 저장해야합니까?
A: TTL 또는받은 편지함 테이블 (PK = 메시지 _ id) 이있는 빠른 정지 (Redis). 지불의 경우-더 긴 (일/주).

Q: TTL 디드 업 키를 선택하는 방법?
A: 최소 = 최대 재전송 시간 + 운영 마진 (일반적으로 24-72 시간). 금융을 위해-더.

Q: Kafka의 키로 압축하면 키가 필요합니까?
A: 예. 컴팩트는 스토리지를 줄이지 만 동기화를 dempotent로 만들지는 않습니다.


13) 총계

적어도 한 번은 기본적이고 신뢰할 수있는 운송 의미론입니다.
프로세서 수준: Idempotency-Key, 받은 편지함/전송률, Upsert/버전, SAGA/보상에서 비즈니스 효과로 정확히 한 번 달성됩니다.
선택은 작동 용이성의 중복 위험을 타협하는 것입니다. 자연 건반을 디자인하고, 타박상을 만들고, 관찰 가능성을 추가하고, 정기적으로 게임 일을 플레이하십시오. 그러면 파이프 라인은 retra와 고장의 폭풍에서도 예측 가능하고 안전합니다.

Contact

문의하기

질문이나 지원이 필요하시면 언제든지 연락하십시오.우리는 항상 도울 준비가 되어 있습니다!

통합 시작

Email — 필수. Telegram 또는 WhatsApp — 선택 사항.

이름 선택 사항
Email 선택 사항
제목 선택 사항
메시지 선택 사항
Telegram 선택 사항
@
Telegram을 입력하시면 Email과 함께 Telegram에서도 답변드립니다.
WhatsApp 선택 사항
형식: +국가 코드 + 번호 (예: +82XXXXXXXXX).

버튼을 클릭하면 데이터 처리에 동의하는 것으로 간주됩니다.