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트리거구현 위치설명버퍼 크기 ≥ 100enqueueOrFlushOnThresholdFLUSH_BUFFER_SIZE = 100일 때,즉시 Flush 실행3 초 무입력enqueueWithTimer → TimerScheduler.startSingleTimer100건이 안 돼도,3 초 동안 새 로그가 없으면 자동 Flush이득Redshift 커밋 횟수↓, 네트워크 RTT↓실시간성 + Bulk 처리2-B. 비동기 + Fail-Soft 구조 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 자동 생성 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) 경험이 없는 독자용 요약계층클래스로 보는 역할설명ApplicationEventTracerController → ServiceHTTP 요청을 Actor 메시지로 변환해 비동기 파이프라인 시작Actor RootActorManager“스프링 빈 대신 ActorRef” 를 동적 생성해주는 ActorManager. CreateChildActor 메시지를 수신하면 자식 Actor를 context.spawn 후 replyTo 로 돌려준다WiringAkkaConfiguration① ActorSystem 부트스트랩② Spring Bean bulkManagerActor 생성 시 Manager에게 Ask-Pattern(미래)로 요청 → ActorRef<BulkManagerActor>를 주입Domain ActorBulkManagerActor버퍼·타이머·에러 위임 담당 상태 머신idle() → active() 전이타이머/버퍼 기준 Flush예외 시 자식 Worker Actor로 분배Worker ActorBulkWorkerActor실제 DB 호출 + 분할 재시도 담당pipeToSelf로 Mono -> Actor 메시지 변환이분할 재귀 + 타임아웃 무재시도 정책💡 Actor 동작 흐름 (텍스트 버전)Controller → BulkManagerActor.ReceiveData(Data)Manager : 버퍼 적재 → (Timer or Threshold) → FlushFlush 성공 : 로그 카운트 기록 후 IdleFlush 실패 : worker1, worker2에게 절반씩 ProcessBatchWorker : Mono 결과를 Success or Failure로 자기 자신에게 전송 → 이분할 재시도 혹은 로그 후 종료 Actor 클래스 구조 ActorSystem └─ ActorManager └─ BulkManagerActor (부모) ├─ BulkWorkerActor#1 (자식) └─ BulkWorkerActor#2 (자식) 4. 데이터 처리 순서
액터모델활용 -비용절감편
1. Pekko(Akka)란?
Pekko(Akka의 Apache 포크)는 Actor 모델에 기반한 고성능 비동기 메시지 기반 시스템을 개발하기 위한 툴킷입니다.
주로 동시성, 분산 처리, 장애 복원성이 필요한 시스템에서 사용됩니다.
전통적인 Java/Kotlin 쓰레드 기반 프로그래밍은 공유 상태와 동기화 문제로 인해 복잡해지기 쉽습니다.
Pekko는 이를 해결하기 위해 다음과 같은 방식을 취합니다:
Actor 단위로 상태와 동작을 캡슐화하고, 오직 비동기 메시지로만 통신합니다. 이로 인해 경합 조건(Race Condition) 없이 안전한 상태 관리를 할 수 있습니다.
Actor는 경량 프로세스처럼 동작하며 수천, 수만 개도 가볍게 생성할 수 있습니다.
Typed API를 통해 메시지 타입을 컴파일 타임에 고정할 수 있어 안정성이 높아지고, 가독성도 향상됩니다.
Supervisor 전략을 통해 각 Actor의 오류를 상위 Actor가 감지하고, 재시작 또는 중단 등의 정책으로 시스템 전체를 무너지지 않게 보호합니다.
예를 들어, “은행 계좌”를 하나의 Actor로 보고, 입출금 요청을 메시지로 전달하는 방식으로 동작을 구현할 수 있습니다. 이런 방식은 이벤트 기반 시스템, 채팅 서버, 실시간 알림, IoT 메시지 처리 시스템 등에 효과적으로 적용됩니다.
2. 데이터 처리 로직 최적화 전략
2-A. “100 건 or 3 초” Buffer Flush
트리거
구현 위치
설명
버퍼 크기 ≥ 100
enqueueOrFlushOnThresholdFLUSH_BUFFER_SIZE = 100일 때,즉시 Flush 실행
3 초 무입력
enqueueWithTimer→TimerScheduler.startSingleTimer100건이 안 돼도,
3 초 동안 새 로그가 없으면 자동 Flush
2-B. 비동기 + Fail-Soft 구조
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 자동 생성
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. 핵심 클래스 구조 & 동작 개요
계층
클래스로 보는 역할
설명
Application
EventTracerController→ ServiceHTTP 요청을 Actor 메시지로 변환해 비동기 파이프라인 시작
Actor Root
ActorManager“스프링 빈 대신 ActorRef” 를 동적 생성해주는
ActorManager.CreateChildActor메시지를 수신하면자식 Actor를context.spawn후replyTo로 돌려준다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 동작 흐름 (텍스트 버전)
Controller →
BulkManagerActor.ReceiveData(Data)Manager : 버퍼 적재 → (Timer or Threshold) → Flush
Flush 성공 : 로그 카운트 기록 후 Idle
Flush 실패 :
worker1,worker2에게 절반씩ProcessBatchWorker : Mono 결과를
SuccessorFailure로 자기 자신에게 전송 → 이분할 재시도 혹은 로그 후 종료ActorSystem └─ ActorManager └─ BulkManagerActor (부모) ├─ BulkWorkerActor#1 (자식) └─ BulkWorkerActor#2 (자식)4. 데이터 처리 순서