Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.





Image Added







1. Pekko(Akka)란?

Pekko(Akka의 Apache 포크)Actor 모델에 기반한 고성능 비동기 메시지 기반 시스템을 개발하기 위한 툴킷입니다.

주로 동시성, 분산 처리, 장애 복원성이 필요한 시스템에서 사용됩니다.

전통적인 Java/Kotlin 쓰레드 기반 프로그래밍은 공유 상태와 동기화 문제로 인해 복잡해지기 쉽습니다.

Pekko는 이를 해결하기 위해 다음과 같은 방식을 취합니다:

  • Actor 단위로 상태와 동작을 캡슐화하고, 오직 비동기 메시지로만 통신합니다. 이로 인해 경합 조건(Race Condition) 없이 안전한 상태 관리를 할 수 있습니다.

  • Actor는 경량 프로세스처럼 동작하며 수천, 수만 개도 가볍게 생성할 수 있습니다.

  • Typed API를 통해 메시지 타입을 컴파일 타임에 고정할 수 있어 안정성이 높아지고, 가독성도 향상됩니다.

  • Supervisor 전략을 통해 각 Actor의 오류를 상위 Actor가 감지하고, 재시작 또는 중단 등의 정책으로 시스템 전체를 무너지지 않게 보호합니다.

예를 들어, “은행 계좌”를 하나의 Actor로 보고, 입출금 요청을 메시지로 전달하는 방식으로 동작을 구현할 수 있습니다. 이런 방식은 이벤트 기반 시스템, 채팅 서버, 실시간 알림, IoT 메시지 처리 시스템 등에 효과적으로 적용됩니다.

☑️ Pekko는 Akka의 공식 포크이며, 현재는 Apache 재단에서 관리되고 있습니다.


2. 데이터 처리 로직 최적화 전략

2-A. “100 건 or 3 초” Buffer Flush

트리거

구현 위치

설명

버퍼 크기 ≥ 100

enqueueOrFlushOnThreshold

FLUSH_BUFFER_SIZE = 100일 때,
즉시 Flush 실행

3 초 무입력

enqueueWithTimerTimerScheduler.startSingleTimer

100건이 안 돼도,
3 초 동안 새 로그가 없으면 자동 Flush

이득

  • Redshift 커밋 횟수↓, 네트워크 RTT↓

  • 실시간성 + Bulk 처리

...

2-B. 비동기 + Fail-Soft 구조

Code Block
themeEmacs
handler.process(batch)          // R2DBC Batch INSERT
  .subscribeOn(Schedulers.boundedElastic()) // JDBC 블로킹 격리
  .timeout(Duration.ofSeconds(5))           // 지연 차단
  .onErrorResume { err -> delegateBatchToChildActors(err, batch) }
  .subscribe()


  • BoundedElastic : Reactor I/O 스레드를 막지 않고 별도 풀에서 DB 호출 수행.

  • 5 초 Timeout : 미응답 배치를 에러로 간주해 Child Actor에게 작업을 위임하여 처리 재시도.
    (응답이 늦어지는 것을 방지)

  • onErrorResume : 예외(에러) 발생시 Child Actor에게 동일 배치를 절반씩 위임해 문제 레코드를 격리/재시도. (최종적으로 문제 레코드 파악 가능)

...

2-C. Child Worker 2-계층 Retry

단계

Worker 내부 로직

결과

타임아웃·DB 장애

TimeoutException·DataAccessResourceFailureException → 단순 로깅 후 조용히 무시

시스템 폭주 방지 (트래픽 백프레셔 역할)

일반 예외

데이터 n > 1분할 / 재시도(splitAndRetry),
n = 1 → 실패 로그를 남긴 후 종료

원인 레코드 정확히 추적, 최대 log₂ N 회 재귀

...

2-D. SQL Batch 최적화

  • place-holder 자동 생성


Code Block
themeEmacs
val allValuesPlaceholders = requests.joinToString(", ") {
    (1..columnNames.size).joinToString(" , ", "(", ")") { "\$${placeholderIndex++}" }
}
  • N 행 × 15 컬럼 SQL을 한 줄로 생성 → 서버 ↔ DB Round Trip 1회로 단축 .

  • R2DBC bind : 파라미터 바인딩으로 SQL Injection 방어 및 타입 안전성을 확보.

...

2-E. 장애 전파 ≠ 스레드 차단

  • Manager-Actor에서 Mono.subscribe() 후 결과를 Fire-and-Forget → 호출 스레드(HTTP) 즉시 반환.

  • 실패해도 Worker가 독립 처리하므로 컨트롤러 타임아웃에 영향을 주지 않고 느슨한 연결을 유지.

...

2-F 예외·타임아웃 대응 전략 정리

단계

로직

설명

Manager 오류

onErrorResume에서 배치 분할 후,
두 개의 Child Actor (Worker)로 위임

대량 오류 → 폭발 가능성 최소화

Worker 타임아웃

TimeoutException이면 재시도 X, 단순 로깅

과부하 폭주 방지

Worker 일반 오류

절반으로 Split + 재전송 (재귀)

이분 탐색으로 오염 레코드 격리

...

3. 핵심 클래스 구조 & 동작 개요

Akka (Pekko) 경험이 없는 독자용 요약


계층

클래스로 보는 역할

설명

Application

EventTracerController → Service

HTTP 요청을 Actor 메시지로 변환해 비동기 파이프라인 시작

Actor Root

ActorManager

“스프링 빈 대신 ActorRef” 를 동적 생성해주는 ActorManager. CreateChildActor 메시지를 수신하면 자식 Actorcontext.spawnreplyTo 로 돌려준다

Wiring

AkkaConfiguration

ActorSystem 부트스트랩
② Spring Bean bulkManagerActor 생성 시 Manager에게 Ask-Pattern(미래)로 요청 → ActorRef<BulkManagerActor>를 주입

Domain Actor

BulkManagerActor

버퍼·타이머·에러 위임 담당 상태 머신

  • idle()active() 전이

  • 타이머/버퍼 기준 Flush

  • 예외 시 자식 Worker Actor로 분배

Worker Actor

BulkWorkerActor

실제 DB 호출 + 분할 재시도 담당

  • pipeToSelf로 Mono -> Actor 메시지 변환

  • 이분할 재귀 + 타임아웃 무재시도 정책

💡 Actor 동작 흐름 (텍스트 버전)

  1. ControllerBulkManagerActor.ReceiveData(Data)

  2. Manager : 버퍼 적재 → (Timer or Threshold) → Flush

  3. Flush 성공 : 로그 카운트 기록 후 Idle

  4. Flush 실패 : worker1, worker2에게 절반씩 ProcessBatch

  5. Worker : Mono 결과를 Success or Failure자기 자신에게 전송 → 이분할 재시도 혹은 로그 후 종료

deciduous treeImage Added Actor 클래스 구조

Code Block
themeEmacs
ActorSystem
 └─ ActorManager
     └─ BulkManagerActor (부모)
         ├─ BulkWorkerActor#1 (자식)
         └─ BulkWorkerActor#2 (자식)


4. 데이터 처리 순서

Image Added