2.8 데이터 흐름 제어 및 서비스 품질(QoS) 관리
차세대 지능형 네트워크망이 아무리 빠르고 경로를 기가 막히게 잘 찾아낸다 한들, 한순간 수만 개의 데이터가 병목 구간(Bottleneck)으로 쏟아져 들어오면 라우터의 램(RAM)과 큐(Queue)가 꽉 차오르며 속수무책으로 시스템 다운(System Crash)을 맞이할 수밖에 없다. 데이터가 거센 물결이라면, 이 폭주하는 물결이 파이프를 터뜨리지 않고 안전하게 목적지까지 도달하도록 중간중간 댐을 세우고, 중요한 물자에게 호위 에스코트를 붙여주는 역할이 본 장에서 논할 서비스 품질(QoS, Quality of Service)과 데이터 흐름 제어이다.
과거 DDS가 로보틱스의 강자로 칭송받았던 이유는 이 방대한 QoS 정책을 극한의 튜닝 옵션으로 마련해 두었기 때문인데, 동시에 개발자들이 그 설정값의 파도 속에 익사하는 저주를 함께 내렸다.
Zenoh는 이 방대하고 복잡했던 QoS 정책을, 그저 개발자가 싣고자 하는 데이터 성격(Reliability 모델, 즉 최선 보장형이냐 완벽 보장형이냐)과 약간의 생존 척도(우선순위 부여 및 TTL 시간) 단 몇 가지의 투명한 선언만으로 우아하게 통제하도록 압축했다. 이번 절에서는 이 경이로운 흐름 제어의 진면목을 하나씩 분해한다. 통신망이 과부하에 걸렸을 때 즉시 압력을 역산하여 발송자 입을 스스로 틀어막는 마법의 액티브 백프레셔(Active Backpressure) 기술부터, 시공간을 초월하는 분산 타임스탬프 기반 데이터 무결성 철학까지, 인터넷 규모의 재난 상황을 견뎌내는 강력한 뼈대를 마주해 본다.
1. 메시지 전달 보장 수준(Reliability Model): Best-effort vs Reliable
서비스 품질(QoS)의 가장 첫 번째 대원칙이자 선택의 갈림길은, 내 애플리케이션의 본질이 “단 한 개의 데이터도 빠져선 안 되는 치밀함“을 원하는지, 아니면 “다소 소실되더라도 무조건 최신 데이터를 가장 빠르게 보고픈 신속성“을 원하는지에 대한 철학적 물음에 도달하는 것이다.
Zenoh는 이 극단적인 두 가지 데이터 성격에 투명하게 대처하기 위해, 발행자(Publisher)가 데이터를 내뿜을 때 프로토콜 와이어(Wire) 속에 “이 배달 물건이 얼마나 소중한가?“에 대한 전송 신뢰성(Reliability) 라벨 마크를 딱 두 종류로 추상화하여 제공한다. 이른바 최선 노력(Best-effort)과 완벽 보장(Reliable) 척도 모델이다.
1. Best-effort (최선 보장 모델)
마치 택배 아저씨가 초인종을 누르고 문 앞에 수백 개의 물건 던져넣기 바쁜 모양새다. 시스템 자원이나 네트워크가 과부하되어 라우터의 처리 큐(Queue)가 터지기 직전이라면, 혹은 무선 Wi-Fi망이 순간적으로 끊겨 패킷이 100건 날아갔다면 이 모델은 그것들을 가차 없이 버리고 잊는다(Drop). 수신자가 과거 데이터를 재요청(Retransmission)하기 위해 통신을 다시 주고받으며 시간을 낭비할 그 단 1밀리초조차 아까운 자율주행 라이다 센서 스트리밍 화면 전송 같은 곳에 오직 폭발적인 ’초지연 전송 및 방어’를 달성하게 해준다.
2. Reliable (완벽 보장 모델)
이 모드를 켜는 순간, Zenoh 엔진은 택배 물건을 우체국 등기로 등급 상향시킨다. 아무리 지연 시간이 늘어지고 대역폭이 소모된다 하더라도, 중간 라우터나 수신자는 이 패킷의 번호표 서명(ACK 응답)이 도착하지 않으면 상대방에게 즉각 재전송을 요구하여 무조건 끝까지 1건의 소실조차 없이 배송을 마친다. 중요한 제어 명령, 즉 로봇을 ’즉시 정지(Stop)’시키는 치명적인 비상 명령이나 반드시 보완 서버에 적재해야 할 데이터베이스 로깅(Logging)용 패킷 전송에는 이 등급 모델이 완벽한 무결성을 선사한다.
개발자는 위대한 설계 문서(QoS 설정 파일) 안에서 특정 ‘키(Key) 이름’ 단위로 이 두 마법을 지정해 놓기만 하면, 1만 대의 센서 중 어느 녀석에게는 빠른 길(UDP)을, 무거운 데이터에는 철저한 검증 배송(TCP 융합 모델)의 왕도를 스스로 통제하는 시스템 전체의 신(God)이 될 수 있다.
2. 메시지 전달 보장 수준(Reliability Model): Best-effort vs Reliable
분산 시스템에서 데이터를 쏠 때 개발자가 던지는 가장 근본적인 질문은 **“이 데이터가 목적지에 무조건 도착해야만 하는가?”**이다.
어떤 데이터는 유실되어도 곧바로 다음 데이터가 들어오기 때문에 상관없지만(예: 초당 100번씩 들어오는 모터 엔코더 틱 데이터), 어떤 데이터는 단 한 번의 유실도 치명적인 시스템 오작동을 유발한다(예: ‘비상 정지(Emergency Stop)’ 명령).
DDS(Data Distribution Service)와 같은 전통적인 미들웨어가 십수 개의 복잡다단한 QoS 프로필(Profile)을 제공하여 개발자를 설정 지옥에 빠뜨리는 반면, **Zenoh(제노)**는 가장 직관적이고 실용적인 이분법적 신뢰성 모델(Reliability Model)을 애플리케이션 계층에 제공한다: 바로 Best-effort와 Reliable이다.
2.1 Best-effort: 성능과 지연(Latency)의 극대화
Best-effort 모드로 선언된 퍼블리셔(Publisher)나 쿼리(Query)는 이름 그대로 ‘최선을 다해 전송하되, 결과는 책임지지 않는’ 철학을 따른다.
- 오버헤드 제로: Zenoh 엔진은 메시지에 순서 번호(Sequence Number)를 기록하거나, 수신 측으로부터 잘 받았다는 확인 응답(ACK/NACK)을 기다리는 어떠한 보조 연산도 수행하지 않는다.
- 큐 관리: 만일 전송 계층(Transport) 버퍼가 가득 차서 병목이 발생하면, Best-effort 메시지는 큐의 뒷선에서 대기하는 대신 과감히 쓰레기통으로 버려진다(Drop).
새로운 데이터가 이전 데이터의 가치를 즉시 소멸시키는 센서 스트리밍 데이터나 고해상도 초당 프레임(FPS) 비디오 피드 등에 이 모드를 활성화하면, 재전송으로 인한 대역폭 낭비와 네트워크 정체를 원천 차단하여 극단적인 초저지연(Ultra-Low Latency)을 획득할 수 있다.
2.2 Reliable: 무결성과 순서 보장의 사수
반대로 Reliable 모드로 메시지가 투척(Put)되면, Zenoh의 QoS 컨트롤러는 이 바이트 뭉치를 ’반드시 살파(Delivery)해야 하는 1급 객체’로 격상시킨다.
- 종단 간(End-to-End) 확인: 하위 트랜스포트가 TCP(신뢰성 보장)이건 UDP(비 신뢰성)이건 상관없이, 응용 계층 레벨에서 수신 피어의 ACK(Acknowledge) 신호를 물리적으로 추적한다.
- 인메모리 버퍼링 및 재전송: 수신 확인이 오지 않으면, 송신 측 라우터의 ’Reliability Buffer’에 캐싱되어 있던 복사본을 꺼내 정해진 타임아웃 주기에 따라 재전송(Retransmission) 스케줄러에 집어넣는다.
- 순서 번호(Sequence Ordering): 네트워크 경로가 달라져 패킷의 순서가 뒤집혀서(Out-of-order) 도착하더라도, 수신 측 엔진이 시퀀스 넘버를 분석하여 사용자 공간(User Space)에는 반드시 올바른 순서를 재정렬하여 밀어 올려준다.
Zenoh가 빛나는 지점은 이 두 가지 이질적인 철학의 트래픽이 완전히 동일한 와이어(Wire)와 동일한 단일 세션(Session) 위에서 완벽히 격리(Isolation)된 채 동시에 흐른다는 것이다. 로봇의 4K 카메라 피드(Best-effort)와 브레이크 제어 명령(Reliable)은 단일 연결통로 속에서 자신만의 철학을 관철하며 병렬적으로 질주한다.
3. 네트워크 혼잡 제어(Congestion Control)와 능동적 배압(Backpressure) 알고리즘
데이터 중심의 퍼블리시-서브스크라이브(Publish-Subscribe) 네트워크에서 맞닥뜨리는 가장 치명적인 문제 중 하나는 처리 능력이 서로 다른 노드 간의 속도 불일치다. 고성능 서버가 초당 수만 개의 메시지를 토해낼 때, 이를 수신하는 저전력 에지 디바이스(Edge Device)가 제때 데이터를 처리하지 못하면 버퍼(Buffer)가 폭주하고 결국 시스템 전체가 마비된다. **Zenoh(제노)**는 이러한 비대칭 통신 환경에서 병목 현상을 우아하게 해결하기 위해 네트워크 혼잡 제어(Congestion Control)와 능동적 배압(Active Backpressure) 메커니즘을 내장하고 있다.
3.1 혼잡 붕괴(Congestion Collapse) 방지의 필요성
전통적인 UDP 기반의 브로드캐스트 시스템이나 단순한 큐잉 기반의 미들웨어에서는 수신자의 상태를 무시하고 송신자가 데이터를 밀어 넣는 데에만 집중한다. 이로 인해 트래픽 포화 상태가 발생하면 패킷 손실(Packet Loss)이 기하급수적으로 증가하며, 손실된 패킷을 재전송하려는 시도가 다시 트래픽을 가중시키는 이른바 혼잡 붕괴 현상이 발생한다.
Zenoh는 이러한 문제를 구조적으로 원천 차단하기 위해, 네트워크의 각 홉(Hop) 단위로 데이터의 흐름을 조율하는 능동적인 교통 통제 시스템을 가동한다.
3.2 능동적 배압(Active Backpressure) 메커니즘의 원리
능동적 배압 알고리즘은 수신자의 큐(Queue)가 가득 차기 시작할 때, 그 압력(Pressure)을 역방향으로 송신자에게 전달하여 데이터 생성 속도 자체를 억제하는 기술이다.
- 크레딧 기반 흐름 제어(Credit-based Flow Control): Zenoh는 와이어 프로토콜 수준에서 데이터 전송단과 수신단 사이에 크레딧(Credit)이라는 토큰을 교환한다. 수신자는 자신이 현재 처리할 수 있는 버퍼의 여유 공간만큼 송신자에게 크레딧을 부여한다.
- 송신 중단 및 조절: 송신자는 보유한 크레딧이 소진되면, 더 이상 네트워크로 패킷을 쏟아내지 못하고 대기(Blocking) 하거나 데이터를 드롭(Drop)해야 한다.
- 연쇄적 압력 전달: 이 압력은 라우터를 거쳐 최초의 퍼블리셔(Publisher) 애플리케이션의 메모리 계층까지 역류한다. 결과적으로 애플리케이션 레벨에서 데이터 생성 주기를 능동적으로 늦추게 된다.
sequenceDiagram
participant P as 퍼블리셔 (Edge Sensor)
participant R as Zenoh 라우터
participant S as 서브스크라이버 (Cloud DB)
Note over P, S: 평시 (크레딧 여유 상태)
P->>R: 트래픽 전송 (1000 msg/s)
R->>S: 트래픽 릴레이 (1000 msg/s)
S-->>R: 크레딧 보충 (Ack & Window Update)
Note over S: DB 부하 발생 (버퍼 포화 임계점 도달)
S-->>R: 크레딧 고갈 통보 (Backpressure 발생)
Note over R: 라우터 송신 큐 제어 모드 진입
R-->>P: 배압 신호 연쇄 전달 (Backpressure Propagation)
Note over P: 퍼블리셔 송신 속도 억제
P->>R: 트래픽 전송 (100 msg/s 로 감소)
3.3 TCP 혼잡 제어와의 차이별
혼잡 제어 하면 흔히 TCP의 윈도우 슬라이딩 알고리즘(예: CUBIC, BBR)을 떠올리기 쉽다. TCP의 혼잡 제어는 주로 ’네트워크 회선 자체의 대역폭 한계와 패킷 유실’에 반응하여 윈도우 크기를 조절하는 수동적인 방식이다.
하지만 Zenoh의 배압 메커니즘은 L4 수준의 TCP 제어를 넘어서, L7 애플리케이션 계층의 ’데이터 소비 속도(Consumption Rate)’를 직관적으로 반영한다. 나아가 TCP가 아닌 UDP 상단에서 Zenoh 프로토콜을 구동할 때조차도 브로커나 라우터가 직접 크레딧을 계산하여 능동적으로 흐름을 제어함으로써, 에지-투-클라우드(Edge-to-Cloud) 전 구간에 걸친 엔드-투-엔드(End-to-End) 트래픽 최적화를 달성한다. 이로 인해 메모리가 수 킬로바이트(KB)에 불과한 마이크로컨트롤러(MCU)도 클라우드 시스템과 안전하게 맞물려 돌아갈 수 있는 것이다.
4. 메시지 우선순위(Priority) 부여 시스템 및 큐잉(Queueing) 모델
로보틱스나 자율주행, 산업용 자동화 네트워크에는 동일한 물리적 회선을 향해 서로 자격이 다른 데이터들이 한꺼번에 쏟아진다. 초당 기가바이트(GB) 단위로 뿜어져 나오는 라이다(LiDAR) 포인트 클라우드 데이터와, 즉시 드론의 자세를 제어해야 하는 수 바이트짜리 긴급 정지(Emergency Stop) 제어 명령이 동일한 큐(Queue)에서 대기한다면 어떻게 될까? 크고 둔탁한 데이터 덩어리에 막혀 생명과 직결된 제어 명령이 지연착(Head-of-Line Blocking)을 겪게 될 것이다.
이를 방지하기 위해 **Zenoh(제노)**는 항공기 1등석 탑승구처럼 데이터의 경중에 따라 처리 순서를 완벽하게 분리해 내는 세밀한 우선순위(Priority) 시스템과 큐잉 모델을 제공한다.
4.1 Zenoh의 8단계 우선순위 클래스(Priority Classes)
Zenoh는 모든 메시지에 대해 1바이트의 헤더 공간을 할당하여, 설계 시점에 정의된 극단적인 다차선 도로를 구현해 냈다. 우선순위는 0에서 7까지 총 8단계의 클래스로 나뉘며, 가장 낮은 숫자(0)가 실시간(Real-time) 처리가 요구되는 최상위 우선순위를 갖는다.
주요 클래스의 활용 예시는 다음과 같다.
- 우선순위 0 (Real-time Control): 모터 제어 명령, 관성 측정 장치(IMU) 보정 데이터, 긴급 정지 프로토콜. 라우터에 도착하는 즉시 다른 큐의 처리를 중단시키고(Preemption) 가장 먼저 망을 통과한다.
- 우선순위 1~3 (Interactive & Telemetry): 초저지연을 요하는 화상 스트리밍 프레임, 텔레프레즌스 음성 데이터.
- 우선순위 4~6 (Bulk Data): 라이다 맵핑 데이터, 고해상도 이미지 전송, 센서 히스토리 벌크 업로드. 백그라운드에서 여유 대역폭을 틈타 전송된다.
- 우선순위 7 (Background Logging): 중요도가 가장 떨어지는 디버그 로그, 시스템 통계 수치 전송.
4.2 우선순위 큐잉(Priority Queueing) 모델의 작동 방식
라우터나 피어 노드의 내부 네트워크 계층에서는 패킷을 단순한 FIFO(First-In, First-Out) 큐에 밀어 넣지 않는다. Zenoh 엔진은 8개의 레인(Lane)으로 분할된 다중 큐(Multi-Queue) 아키텍처를 가동한다.
4.2.1 가중치 기반 공정 큐잉 (Weighted Fair Queueing, WFQ) 적용
단순한 엄격한 우선순위(Strict Priority) 방식을 사용하면, 우선순위가 높은 제어 신호가 폭주할 경우 낮은 우선순위의 데이터는 영원히 전송되지 못하는 기아 현상(Starvation)에 빠진다.
이를 우회하기 위해 Zenoh의 프레이밍(Framing) 및 스케줄링 층에서는 대역폭의 파이를 우선순위에 비례하여 쪼개주는 스마트 스케줄러를 가동한다. 평상시에는 우선순위 0의 패킷이 절대적인 새치기를 허용받지만, 벌크 전송이 극심할 때에도 최소한의 대역폭은 유지시켜 전체 시스템의 데이터 흐름이 고르게 진행되도록 제어(QoS Bandwidth Allocation)한다.
4.3 코드 적용 예시
메시지 배포(Publish) 시 우선순위를 부여하는 API 인터페이스는 극도로 단순하다. 데이터의 성격에 따라 선언적으로(Declarative) 우선순위를 못 박기만 하면, 하부의 Zenoh C 코어 런타임이 모든 큐잉과 다중화(Multiplexing)를 책임진다.
// Rust API 예시: 제어 명령과 벌크 데이터의 전송
use zenoh::qos::Priority;
// 긴급 제어 명령: 최고 우선순위(RealTime) 할당
session
.put("robot/base/cmd_vel", control_payload)
.priority(Priority::RealTime) // 내부적으로 0으로 매핑됨
.wait()
.unwrap();
// 고화질 카메라 프레임: 중간 우선순위(DataHigh) 할당
session
.put("robot/cam/rgb/frame", image_payload)
.priority(Priority::DataHigh) // 내부적으로 3~4 대역으로 매핑됨
.wait()
.unwrap();
이처럼 Zenoh의 우선순위 부여 시스템은 복잡한 트래픽 쉐이핑(Traffic Shaping) 장비 없이도, 미들웨어 자체의 순수 알고리즘만으로 로보틱스의 결정론적(Deterministic) 제어 타이밍을 아키텍처 깊숙이 보장해 준다.
5. 데이터 타당성(Validity) 보장 및 메시지 생존 시간(TTL/Drop) 정책 관리
데이터 지향 아키텍처에서 모든 메시지의 가치가 영구적인 것은 아니다. 10밀리초(ms) 전에 측정한 드론의 자이로스코프(Gyroscope) 데이터는 1초 뒤에 시스템 버퍼에서 발굴되어 렌더링될지라도 아무런 제어적 가치가 없는 쓰레기(Stale Data)에 불과하다. 이 낡은 데이터를 전달하기 위해 소중한 네트워크 대역폭과 라우터 큐(Queue)를 낭비하는 것은 인프라의 죄악이다.
**Zenoh(제노)**는 데이터의 수명(Lifetime)과 시간적 타당성(Temporal Validity)을 엔진 단에서 강제할 수 있도록, 세밀한 파편 삭제(Drop) 정책과 메시지 생존 시간(Time-To-Live, TTL) 속성을 기본 기능으로 제공한다.
5.1 메시지 생존 시간(Time-To-Live, TTL) 개념의 적용
전통적인 IP 계층에서의 TTL이 “이 패킷이 몇 개의 라우터 홉(Hop)을 거치면 폐기될 것인가“를 의미했다면, L7 데이터 버스에서 Zenoh가 취급하는 TTL은 철저히 **시간적 만료(Time Expiration)**에 방점을 둔다.
- 동작 원리: 특정 센서 토픽이 생성될 때 “이 데이터는 500ms 동안만 유효하다“는 꼬리표(Metadata)를 붙인다.
- 라우터의 자체 검열: 데이터가 클라우드로 전송되는 과정에서, 4.9.4절에서 언급했던 라우트 병목이나 다중화 전파 지연 탓에 네트워크 체류 시간이 500ms를 초과하게 되면 어떻게 될까? Zenoh 라우터는 이 패킷을 다음 노드로 넘기는 수고를 멈추고 메모리 상에서 지체 없이 폐기(Drop)해 버린다. 이를 통해 무의미하게 망을 점유하는 좀비 패킷들을 선제적으로 청소할 수 있다.
5.2 큐잉 오버플로우 시의 삭제(Drop) 정책
만약 2.8.2절에서 다룬 능동적 배압(Backpressure)이 발동했음에도 불구하고, 애플리케이션의 버퍼에 데이터가 한계치까지 차올랐다면 시스템은 결단을 내려야 한다. 버퍼를 터뜨리고 프로세스를 죽일 것인가, 아니면 눈물을 머금고 데이터를 버릴 것인가?
Zenoh는 큐 공간 부족 시 어떤 데이터를 버릴 것인지 통제하는 세밀한 정책(Drop Policy)을 제공하여 시스템 패닉을 회피한다.
- 최신 데이터 버림 (Drop Newest): 큐가 꽉 찬 상태에서 새로운 데이터가 들어오면, 이 불청객(새 데이터)의 입장을 거부하고 폐기한다. 가장 극적으로 안정성을 확보해야 하거나 데이터의 연쇄적 순서(Sequence)가 중요한 시스템에서 사용한다.
- 오래된 데이터 버림 (Drop Oldest): 새로운 데이터가 들어올 때, 큐의 가장 깊숙한 곳(Head)에 자리 잡고 있던 오래된 낡은 데이터를 밀어내어 삭제하고 새 데이터를 수용한다. 로보틱스의 텔레메트리(Telemetry)나 센서 퓨전처럼 과거의 이력이 무의미하고 최신 상태 값이 절대적으로 중요한 시나리오에서 채택한다.
- 버리지 않고 대기 (Block): 큐 공간이 확보될 때까지 스레드를 무기한 정지(Block)시킨다. 절대 유실되어서는 안 되는 과금 데이터 로깅이나 결정론적 커맨드 시스템에서 활용된다.
// Rust API 예시: 텔레메트리 데이터 속성에 Drop Oldest 반영
use zenoh::qos::CongestionControl;
// 큐가 가득 차면 낡은 센서 데이터를 우선적으로 폐기하라
session
.put("drone/sensor/imu", payload)
.congestion_control(CongestionControl::Drop)
.wait()
.unwrap();
이와 같이 런타임에서의 TTL과 강제 삭제 정책을 적절히 버무리면, 대역폭이 오락가락하는 비정형적이고 열악한 에지-클라우드 환경에서도 시스템 코어의 메모리 누수와 오버플로우를 원천 방어하며 무중단(Zero-downtime) 서비스의 핵심 초석을 다질 수 있다.
6. 시간 동기화 문제 해결 및 분산 타임스탬프(Timestamp) 활용 방안
수천 대의 로봇과 에지 서버가 전 세계에 흩어져 데이터를 주고받는 거대한 엣지-투-클라우드(Edge-to-Cloud) 환경에서 가장 해결하기 까다로운 난제 중 하나는 바로 ’시간(Time)’이다. 각 장비의 시스템 로컬 클럭(Local Clock)은 하드웨어 오차로 인해 하루에도 수 밀리초(ms)씩 어긋나며(Clock Drift), NTP(Network Time Protocol) 서버를 사용하더라도 네트워크 지연으로 인해 완벽한 동기화는 물리적으로 불가능하다.
이러한 상황에서 여러 노드가 동시에 동일한 키 경로(Key Path)의 데이터를 수정하려고 할 때, 단순한 로컬 시간 기반의 덮어쓰기(Overwrite)를 허용하면 과거의 데이터가 최신 데이터를 지워버리는 치명적인 데이터 충돌(Data Conflict) 현상이 발생한다. **Zenoh(제노)**는 중앙 서버 없이도 분산 환경에서 시간의 인과관계(Causality)를 추적할 수 있는 분산 타임스탬프(Distributed Timestamp) 아키텍처를 통해 이 골칫거리를 우아하게 해결한다.
6.1 하이브리드 논리 클럭(Hybrid Logical Clocks, HLC)의 도입
오프라인 기반의 완전한 로컬 시간(Physical Clock)도 믿을 수 없고, 인과관계만 따지는 순수 논리 클럭(Logic Clock, 예: Lamport Timestamp)만 쓰기에는 현실 세계의 ‘초(Second)’ 단위 모니터링이 불가능하다.
Zenoh 코어는 이 두 시스템의 장점만을 취합한 하이브리드 논리 클럭(HLC) 모델을 활용하여 데이터의 버전 관리(Versioning)와 타임스탬프를 부여한다.
- 물리적 시간 + 논리적 카운터: HLC는 기본적으로 노드의 NTP 물리 시간을 기반으로 하되, 동일한 밀리초(ms) 내에서 여러 이벤트가 발생할 경우 논리적인 카운터(Counter)를 덧붙여 순서를 엄격하게 구별한다.
- 교차 보정(Cross-calibration): 네트워크를 통해 다른 피어(Peer)의 데이터를 수신할 때, 송신자의 HLC 타임스탬프가 내 로컬 타임스탬프보다 미래를 가리키고 있다면 내 HLC를 즉시 송신자의 시간으로 끌어올린다(Fast-forward). 이를 통해 전체 네트워크의 시계를 가장 빠른 짐작값으로 멱등하게(Idempotent) 동기화시키는 마법을 부린다.
6.2 분산 데이터 충돌 해결 병합(Conflict-Free Replicated Data Type, CRDT)
Zenoh가 HLC 타임스탬프를 그토록 꼼꼼하게 패킷에 새겨 넣는 궁극적인 이유는, 브로커 없이 P2P로 흩어져 있는 데이터 스토리지들 간의 충돌 없는 병합(Consolidation)을 구현하기 위해서다.
2.9절에서 다룰 스토리지 노드가 클라우드와 에지에 각각 떠 있다고 치자. 네트워크가 끊긴(Air-gapped) 상태에서 각각의 로봇이 데이터를 밀어 넣고, 이후 통신망이 복구되어 스토리지 노드끼리 동기화(Sync)를 시도할 때, Zenoh는 분산 데이터 처리 기법인 LWW(Last-Writer-Wins, 최종 작성자 승리) 알고리즘을 가동한다.
- 스토리지 A와 B에 저장된 동일한 키(Key) 값의 패킷을 비교할 때, 바이트 페이로드(Payload)를 열어볼 필요 없이 헤더에 붙은 HLC 타임스탬프만 비교한다.
- 숫자(물리 시간 + 논리 카운터)가 더 큰 쪽의 패킷이 이기고, 작은 쪽은 조용히 폐기 처리된다.
- NTP 시계가 틀어져 있었더라도, 앞서 설명한 패킷 교환을 통한 교차 보정 덕분에 치고받는 메시지의 인과관계 시퀀스(Sequence)는 절대 꼬이지 않는 시스템 복원력(Resilience)을 자랑한다.
6.3 타임스탬프 부착 강제 옵션 제어
개발자는 필요에 따라 메시지를 퍼블리시(Publish)할 때 이 무거운 타임스탬프 메타데이터를 강제로 붙일지 말지 조율할 수 있다.
- 비부착 모드: 초당 수만 번 날아오고 순서가 조금 섞여도 상관없는 아날로그 센서 데이터나 비디오 스트림 프레임이라면, 타임스탬프 바이트 공간조차 아까우므로 생략하여 대역폭을 쥐어짠다.
- 부착 필수 (With Timestamp): 분산 데이터베이스나 스토리지에 영구적으로 기록되어야 하는 이벤트 데이터(예: 결제, 문 열림 등)는 지연 발송(Late Arrival)되더라도 스토리지 단에서 정확히 병합되어야 하므로 타임스탬프를 명시적으로 파라미터에 추가한다.
이로써 인프라 엔지니어는 고질적인 분산 시스템의 시간 틀어짐(Clock Skew) 스트레스에서 벗어나, Zenoh 미들웨어 코어 런타임에 데이터 버전 관리의 모든 책임을 넘기고 비즈니스 로직 작성에만 몰두할 수 있게 된다.