Lettuce client 설정

에러 응답이 와야 레디스 클러스터 failover가 실행될텐데 AWS 랙 화재로 응답 자체가 안 오는 상황이없음
- (해결) periodic refresh는 주기적으로 강제 새로고침으로 해결 가능
레투스는 flag가 fail 난 노드를 자동으로 필터링 해주지 않음
- (해결)

- 코드에서 수동으로 토폴리지에서 제외해줌
레디스 클러스터와 확장성
확장성(scalability): 증가하는 트래픽에 유연하게 대응할 수 있는 능력
- 스케일 업: 수직 확장.서버 하드웨어를 높은 사양으로 업그레이드 (디스크, cpu, 메모리)
- 스케일 아웃: 수평 확장. 장비 추가
레디스에서의 확장성
- 키 eviction이 자주 발생한다면? 메모리 스케일 업 고려
- 키 eviction: 레디스가 maxmemory만큼 데이터 차 있는데 또 데이터 저장할 때 발생
- 레디스 처리량 증가시키려면? 스케일 아웃 고려
- 단일 스레드라 cpu 스케일업 한다해도 여러 cpu 코어 동시 활용 못함
- 여러 서버로 분할하면 요청 병렬 처리 가능
레디스 클러스터 기능
레디스를 클러스터 모드로 사용하면 수평 확장, 분산 처리, 복제, 자동 페일오버 가능해짐
데이터 샤딩
샤딩: 데이터 저장소를 수평 확장 해 여러 서버에 데이터를 분할하는 DB 아키텍처 패턴
- 클러스터는 여러 쌍의 마스터-슬레이브로 구성되어 있음
- 마스터 최대 1000개까지 확장 가능
- 샤딩 관련 기능은 레디스 내부에서 자체적으로 관리함
샤딩 동작 방식
- 키 기준으로 샤딩 된다.
- 하나의 키는 항상 하나의 마스터 노드에 매핑된다.
- 클라가 매핑된 노드가 아닌 다른 노드로 키 접근하려할 때 키 할당된 마스터 노드로 연결을 리디렉션한다. (노드에서 리디렉션 할 노드 알려주고 클라에서 리디렉션 요청)
- 클라에서{키-할당된 노드} 맵 캐싱하면 시간 단축 가능 (노드 변경되면 refresh 필요)
고가용성

- 일반적으로 마스터 3대, 복제본 3대 노드로 구성
- 마스터에 장애 발생하면 인지한 다른 노드들이 복제본으로 자동 페일오버 시킴
- 노드는 클러스터 버스를 통해 서로 모니터링한다.
- 모든 노드는 다른 노드의 연결을 수신하기 위한 추가 TCP 포트가 열려 있다. => 클러스터 버스 포트
- 클라로부터 커맨드 받는 TCP포트와 독립되게 동작한다.
- 클러스터 버스 포트는
cluster_bus_port값 정의하지 않으면 일반 포트에 10000을 더한 값이다. (레디스 포트 6379면 16379)
- 클러스터는 모든 노드가 TCP 연결 사용해 다른 모든 노드와 연결되어 있는 풀 메쉬(full-mesh) 토폴로지 형태이다.
- 모든 노드는 N-1개 노드와 송수신 TCP 연결하고 있고, 계속 유지된다.
- 다른 노드로 PING 보냈을 때 PONG이 늦으면 연결을 새로 시도한다.
- 풀 메쉬로 인해 너무 많은 메시지를 교환해서 오버헤드가 발생하지 않을까하는 걱정은 안 해도 된다.
- 가십 프로토콜과 구성 업데이트 메커니즘으로 클러스터가 정상이면 너무 많은 메시지 교환하지 않는다
❓ 가십 프로토콜 (Gossip Protocol)
전체 노드에 소문을 퍼뜨리는 방식입니다. 동네 반상회에서 모두가 모여 떠드는 게 아니라, 길 가다 만난 이웃 몇 명에게만 소식을 전하는 방식이라고 보시면 됩니다.
- 선택적 통신: 각 노드는 매초마다 무작위로 선택된 몇 개의 노드에게만
PING패킷을 보냅니다.- 정보의 전염:
PING을 보낼 때 자기 정보만 보내는 게 아니라, 자기가 알고 있는 다른 노드들의 상태 정보(일부)를 얹어서 보냅니다.- 지수적 확산: A가 B에게 소식을 전하고, B가 C에게 전하는 식으로 정보가 퍼지기 때문에, 노드 수가 많아져도 전체 네트워크에 정보가 도달하는 시간은 매우 짧으면서 메시지 양은 일정하게 유지됩니다.
❓ 구성 업데이트 메커니증
정보가 충돌하거나 마스터가 바뀌었을 때, "누구 말이 맞는지" 결정하는 규칙입니다. 무분별한 업데이트를 막아 오버헤드를 줄입니다.
🛡️ 에포크 (Epoch) 시스템
레디스 클러스터는
configEpoch라는 일종의 '버전 번호'를 사용합니다.
- 논리적 타임스탬프: 페일오버가 발생할 때마다 이 숫자가 하나씩 올라갑니다.
- 충돌 해결: 만약 두 노드가 서로 "내가 이 슬롯(데이터 범위) 주인이야!"라고 우기면, 에포크 숫자가 더 높은(최신 버전인) 노드의 정보를 진실로 받아들이고 나머지는 즉시 업데이트를 중단합니다.
🔄 업데이트 전파 방식
- 수동적 수신: 노드들은 평소에 가만히 있다가, 자신보다 더 높은 에포크 정보를 가진
PONG패킷을 받으면 그때서야 자신의 로컬 구성도를 갱신합니다.- 즉각적인 거부: 잘못된 정보를 가진 노드가 접근하면, "너 정보 구식이야, 이게 최신이야"라며 즉시 최신 정보를 꽂아줍니다.
레디스 클러스터 동작 방법
해시슬롯을 이용한 데이터 샤딩
- 클러스터 구조에서 모든 데이터는 해시슬롯에 저장됨 (한 노드 당 최대 16384개)
- 마스터에만 해시슬롯이 할당된다.
- 모든 키는 해시함수를 통해 하나의 해시슬롯에 매핑됨
-
HASH_SLOT = CRC16(key) mod 16384
-
CRC16으로 암호화한 값을 16384로 나눔
- 해시슬롯은 노드내에서 자유롭게 옮겨질 수 있어서(데이터 변경되는 중에도) 마스터 노드 추가/삭제가 간단
해시태그
- 클러스터에서
MGET같은 다중 키 커맨드는 쓸 수 없다.- 해당 키가 있는 노드로 리디렉션 하기 때문에 한 번에 두 개 이상 키 접근해야하는 커맨드는 처리할 수 없음
- 해시태그 이용하면 문제 해결 가능
- 해시태그: 키에서 대괄호로 묶은 값만 해시함수 태워 해시슬롯에 저장
-
user:{123}:profile user:{123}:account
-
123만 해시함수 태움 → 같은 마스터에 있음을 보장
- ```java
MGET {user}1:name {user}2:name
해시 태그 이용하면 MGET 가능 (키 몰리는 건 고려해야함)
- (키) {user1}.follwers → (해시되는 값) user1
- (키) user{}id → (해시되는 값) 키 전체
- (키) user{{name}}id → (해시되는 값) {name
- (키) user{name}{id} → (해시되는 값) name

자동 재구성
- 센티널에서도 복제, 자동 페일오버 해줌
- 센티널은 별도 프로그램으로 감시하지만 클러스터는 노드끼리 서로 감시(by 클러스터 버스)한다는 차이점
- 클러스터에서의 재구성 종류
- 1. 자동 페일 오버 (복제본을 마스터로 승격)
- 2. 복제본 마이그레이션 (잉여 복제본을 신규 마스터에 연결)
1) 자동 페일오버
- 마스터 장애 남
- 복제본이 다른 마스터들한테 페일오버 시도해도 될지 투표 요청
- 과반수 이상 동의하면 복제본은 마스터로 승격됨
---------------------------------------------------------------------------
이 상황에서 신규 마스터가 또 장애나면?
- 마스터 하나라도 정상 아니면 전체 클러스터 사용 못 하게 된다.
cluster-require-full-coverage yes에 의해- 데이터 정합성을 위해 해시슬롯 정상 아니면(마스터 노드) 사용 못 하게 됨
- 이렇게 안 되려면 아무 마스터에 복제본 추가해둔다. (자동 복제본 마이그레이션이 균형 맞춰줌)
2) 자동 복제본 마이그레이션

- 마스터(6001) 장애나서 복제본(6005)가 승격됨
- 신규 마스터(6005)는 복제본이 없음
- 레디스 클러스터가 불균형을 파악해 복제본 2개 있는 그룹에서 하나 빼와서 복제본 채워줌 => 복제본 마이그레이션
- 마스터가 최소 1개의 복제본을 가지는 것을 보장한다.
-
cluster-allow-replica-migration yes cluster-migration-barrier 1 -> 최소 복제본 수
-
- 복제본 선출 기준: 가장 많은 복제본 가진 그룹, FAIL 아님, 노드 ID 가장 작은 복제본
- 마스터가 최소 1개의 복제본을 가지는 것을 보장한다.
레디스 클러스터 실행
클러스터 모드 사용하려면: 최소 3개 마스터 노드 있어야함 (복제본 각 1대까지 합치면 총 6대)
클러스터 초기화
_redis.conf_에 클러스터 설정
cluster-enabled yes
클러스터 생성 커맨드
src/redis-cli --cluster create 180.210.82.49:6379 180.210.81.179:6379 125.6.40.74:6379 {복제본 3개 ip:port 추가} --cluster-replicas 1
--cluster-replicas 1복제본 1개임을 명시- 임의 순서대로 3개는 마스터, 3개는 복제본 된다.
- 커맨드 진행하면서 yes 눌러야함

- 해시슬롯은 마스터에만 할당되고 복제본은 할당 안 됨
- 복제본은 마스터와 동일한 데이터 저장하기 때문
❓ 왜 해시슬롯은 최대 16384개?
PING/PONG 패킷 주고받을 때 마스터가 갖고있는 슬롯 비트맵 주고 받음
2KB면 네트워크 패킷 주고 받을 때 가벼운 정도임
2KB = 2048Byte = 16384 Bit 라 16384개
클러스터 상태 확인
redis-cli cluster nodes
08d4439b3a01ea49c460bcca6e84269ea67f8721 192.168.0.82:6379@16379 myself,master - 0 1705915601000 1 connected 0-5460
<id> <ip:port@cport> <flags> <master> <PING-sent> <PONG-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>
노드 변경할 때 자주 씀!
redis-cli로 클러스터 접근하기와 리디렉션
클러스터 접근하려면 클러스터 모드 지원하는 클라이언트 필요(e.g. redis-cli)
서버 1에서 user:1 set 시도함
> set user:1 true
(error) MOVED 10778 192.168.0.22:6379
해당 키에 대한 해시슬롯은 10778임. 10778은 22서버에 있다고 에러남.

→ 일반적인 클라이언트를 이용하면 해시슬롯이 맞는 노드에서만 커맨드 수행시킬 수 있다.
해결 방법
- Jedis, Redisson 같은 클라이언트 사용
- redis-cli
-c옵션 사용 (자동으로 redirection 해줌)- ```
127.0.0.1:6379> set user:1 true
-> Redirected to slot [10778] located at 192.168.0.22:6379
OK
192.168.0.22:6379>
이후 22서버로 클라 대상 주소도 옮겨짐
- 대부분 클라는 리디렉션 정보 캐싱해 맵 생성해둬서 성능 향상 시킴
- 마스터 노드 추가/삭제되거나 페이오버 발생했을 때 리프레시함
<br>
## 페일오버 테스트
### 커맨드로 페일오버 (수동 페일오버)
- 마스터에 1개 이상 복제본 있어야함
- 페일오버 발생시킬 복제본에서 커맨드 실행
```java
> cluster failover
페일오버 진행하는 동안 기존 마스터에 연결된 클라는 잠시 블락됨. (복제 딜레이)
- 복제본이 마스터 오프셋 따라잡기 위함
- 따라잡기 완료 후 페일오버 시작
마스터 동작 중지 시키기 (자동 페일오버)
redis-cli -h 192.168.0.22 -p 6379 shutdown
- _redis.conf_의
cluster-node-timeout시간(디폴트 15000, 15초) 동안 마스터로부터 응답 없으면 페일오버 트리거
레디스 클러스터 운영하기
클러스터 리샤딩 (해시슬롯 이동)
리샤딩: 마스터 노드의 해시슬롯 일부를 다른 마스터로 이동시키는 것
- 리샤딩은 중단 없이 진행된다.
redis-cli --cluster reshard 192.168.0.22 6379
이후 인터렉션하면서 이동시킬 슬롯 개수, 받을 노드 ID 입력
커맨드 한 번으로 바로 리샤딩하고 싶다면?
redis-cli --cluster reshared <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
--cluster-yes모든 프롬프트에 자동으로 yes입력
클러스터 확장 - 신규 노드 추가
- 추가할 노드는 데이터가 저장되지 않은 상태여야 함 (아니면 not empty 에러)
마스터 추가하기
redis-cli --cluster add-node <추가할 노드 ID:PORT> <기존 노드 IP:PORT>
--cluster add-node:마스터 노드 추가<기존 노드 IP:PORT>:클러스터에 속한 노드 중 하나
처음 추가되면 할당된 해시슬롯이 없어서 리샤당으로 받아야함
복제본 추가하기
redis-cli --cluster add-node <추가할 노드 IP:PORT> <기존 노드 IP:PORT> --cluster-slave [--cluster-master-id <기존 마스터 ID>]
--cluster-slave: 복제본 노드로 추가--cluster-master-id: 복제본이 될 특정 마스터 지정- 없으면 클러스터가 복제본 균형 보고 가장 복제본 적은 마스터에 연결해줌
노드 제거하기
redis-cli --cluster del-node <기존 노드 IP:PORT> <삭제할 노드 ID>
--cluster del-node: 노드 제거- 제거할 노드도 데이터가 없는 상태여야 한다. (해시슬롯을 다른 노드로 다 옮겨야함)
- 수동 페일오버로 복제본으로 만든 뒤 제거할 수 있다.
로그
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
CLUSTER FORGET
- 다른 노드들한테 해당 노드 지우라고 커맨드 보냄
- 커맨드 받은 노드는 테이블에서 정보 지운뒤 60초 동안은 삭제된 노드 ID 가진 노드와 신규로 연결되지 않게 설정한다.
- 가십 프로토콜로 클러스터의 신규 노드를 자동으로 감지해 새 노드를 추가하기 때문에 삭제된 노드가 재연결되지 않게 설정하는 거임
CLUSTER RESET
CLUSTER RESET [SOFT/HARD] (안 쓰면 SOFT)
- 제거될 노드에서 수행된다.
작업 단계
1) 복제본은 마스터로 전환되고, 모든 데이터는 삭제된다.
- 마스터 전환 이유: 마스터가 돼야 데이터 삭제할 수 있고, 모든 정보를 지우기 때문에 마스터에 대한 정보도 지우기 때문
- 마스터이고 저장된 키가 있으면 reset이 중단됨 (
HARD RESET이면 강제로 삭제됨)
2) 모든 해시슬롯이 해제되고, 페일오버 진행중이었다면 진행상태도 초기화된다.
3) 다른 노드 데이터가 초기화된다. 기존 클러스터 버스 통해 연결됐던 노드 인식 불가
------------------여기까지 SOFT RESET------------------
4) currentEpoch, configEpoch, lastVoteEpoch 값이 0으로 초기화된다.
5) 노드 ID가 새로운 임의 ID로 변경된다.
------------------여기까지 HARD RESET------------------
레디스 클러스터로 데이터 마이그레이션하기
기존에 싱글이나 센티널로 사용하던 레디스를 클러스터로 마이그레이션하기
- 다중 키 커맨드 쓰는 경우엔 해시태그로 로직 변경 필요
redis-cli --cluster import 192.168.0.11:6379 --cluster-from 192.168.0.88:6379 --cluster-copy
import: 타깃 노드--cluster-from: 소스 노드
마이그레이션할 땐 소스 노드에 클라 중단시키는게 좋다. 마이그 중 변경되는 데이터는 클러스터에 반영되지 않기 때문
복제본으로 읽기 성능 향상
- 클라는 기본적으로 키 요청하면 마스터에 리디렉션 한다.
- 읽기 요청을 복제본에서 수행하게 하고 싶으면
- 복제본으로 맺어지는 커넥션을
> READONLY모드로 변경한다
- 복제본으로 맺어지는 커넥션을
Lettus
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA) // <--- 핵심 설정!
.clientOptions(clusterClientOptions)
.build();
// ReadFrom.REPLICA: 무조건 복제본에서만 읽음.
// ReadFrom.REPLICA_PREFERRED: 복제본에서 먼저 읽되, 없으면 마스터에서 읽음.
레디스 클러스터 동작 방법
내부적으로 어떻게 동작하는지 자세히 알아보자
하트비트 패킷
- 노드들끼리 지속적으로 PING, PONG 주고 받음
- 이 두 패킷을 묶어 하트비트 패킷이라고 한다.
- 일반적으로 주고받는 패킷 + 가십 섹션(랜덤한 몇 개의 다른 노드 정보)
- 패킷 헤더 정보
- 노드 ID
- 현재 에포크/구성 에포크: 분산 환경에서 일관성을 유지하기 위한 정보
- 처음 클러스터 생성하면 다 1이고, 노드들끼리 통신할 때 받은 에포크 값이 크면 업데이트
- 노드 플래그: 노드가 마스터인지, 복제본인지 등의 노드 정보
- 비트맵: 마스터가 제공하는 해시슬롯의 비트맵 정보
- TCP 포트: 발신 노드의 TCP 포트
- 클러스터 포트: 발신 노드의 노드 간 커뮤니케티션을 위한 포트
- 클러스터 상태: 발신 노드 관점에서 봤을 때의 클러스터 상태
- 마스터 노드 ID: 복제본 노드인 경우 마스터의 노드 ID
- 하트비트 패킷인 경우 가십 섹션을 추가로 포함: 패킷을 발신하는 노드가 알고 있는 클러스터 내 다른 노드 정보
해시슬롯 구성이 전파되는 법 2가지
- 커맨드에서 사용한 키의 해시슬롯의 위치를 알려주는 것이 중요한 기능
- 해시슬롯 구성은 두 가지 방법으로 전파됨
- 1. 하트비트 패킷: 마스터가 PING, PONG 보낼 때 자신의 해시슬롯을 패킷에 추가
- 2. 업데이트 메시지: 보낸 쪽의 에포크가 작으면 패킷 받은 노드가 신규 에포크 정보 보내서 업데이트 하게 한다.
- 클러스터 처음 생성했을 땐 해시슬롯은 NULL로 초기화됨
- 위의 두 방법으로 받은 해시슬롯으로 업데이트한다.
- 해시슬롯 변경은 페일오버, 리샤딩 중에만 발생한다
- 둘 다 에포크 증가됨. 작업 후 변경사항을 클러스터 전체에 전파시킨다.
노드 핸드셰이크
- 한 노드가 클러스터에 합류하기 위해
CLUSTER MEET커맨드를 다른 노드에 보낸다. - 커맨드 수신한 노드는 알고 있는 다른 노드들에게 전파
- 전파돼서 받은 노드가 신규 노드와 모르는 상태면
CLUSTER MEET커맨드 보내서 신규 연결 맺는다. (이미 알고 있으면 무시) CLUSTER MEET는 방향성이 없어서 A → B로 보내 연결됐으면 B → A로는 안해도 된다.- 이렇게 노드끼리 완전히 연결되는 풀 메쉬(full-mesh)연결을 구성함
클러스터 라이브 재구성
운영중에 마스터 추가/삭제 가능
- (추가) 빈 노드 클러스터에 추가 후 일부 해시슬롯을 옮겨받는다.
- (삭제) 갖고 있던 해시슬롯 다른 노드로 보낸다.
해시슬롯 보내는 법
A → B 노드로 해시슬롯 8보내기
- A에서 실행:
CLUSTER SETSLOT 8 MIGRATING B - B에서 실행:
CLUSTER SETSLOT 8 IMPORTING A
만약 커맨드 실행중에 노드 A에서 8에 대한 커맨드 받았을 때
- 읽기 쿼리면 A에서 수행
- 새로운 키 생성하는 쿼리면 B로 리디렉션
해시슬롯 8에 속한 키들도 마이그해야함
-
CLUSTER GETKEYINSLOT slot count
slot이 가진 모든 키 반환
- 반환된 모든 키에 대해 A에서 아래 커맨드 전송
- ```java
MIGRATE target-host target-port key target-database id timeout
- 키 전송 후 OK 받으면 기존 노드에서 삭제한다.
- 키 마이그 하는 동안 락 걸려서 경쟁 상황은 발생하지 않는다.
- 마이그 완료되면 두 노드에게 모두 아래 커맨드 전송
-
SETSLOT slot NODE node-id
다른 모든 노드로도 전파됨
리디렉션
클러스터가 반환하는 리디렉션은 1) MOVED, 2) ASK 두 개임
MOVE 리디렉션
- “요청하는 해시슬롯이 저 노드에 있으니 저 쪽에 보내”
- 키 해시슬롯에 맞는 마스터 노드를 해시슬롯 맵에서 찾아서 MOVED에러로 응답한다.
- 클라 쪽 해시슬롯 맵에 추가
ASK 리디렉션
- “지금 요청한 쿼리는 저 노드에서 수행해. 다음에 같은 쿼리는 다시 나한테 보내고”
- 해시슬롯이 이동되는 과정에서만 발생
- 클라 쪽 해시슬롯 맵 업데이트 안 함
장애감지와 페일오버
장애감지 플래그는 1) PFAIL, 2) FAIL 2개임
PFAIL (Possible failure)
- 특정 노드에
NODE_TIMEOUT시간 이상 도달할 수 없으면 해당 노드에 대해 PFAIL 표시- PING 패킷 보냈는데
NODE_TIMEOUT시간보다 더 오래 PONG 받지 못한 경우 - 노드 간 왕복시간(RTT, Round Trip TIme)보다
NODE_TIMEOUT가 항상 커야한다. (받고 보내는 시간 걸리니까)
- PING 패킷 보냈는데
- 마스터, 복제본 관계없이 모든 노드에 PFAIL 플래깅 가능
FAIL
- 페일오버 트리거 위해선 FAIL 상태 여야 함
- A가 B를 PFAIL로 플래깅하고 나서 다른 노드가 보낸 B 상태 확인했는데 그 쪽도 PFAIL, FAIL라고 하면 A가 B를 FAIL로 플래깅한다.
복제본 선출
아래 조건에서 복제본은 페일오버 직접 시도한다.
- 마스터가 FAIL임
- 마스터가 1개 이상 해시슬롯 갖고 있음
- 마스터와 복제 끊어진지 오래다. (파라미터로 조절)
위 조건 모두 만족시 복제본이 에포크 1증가하고 다른 마스터들한테 FAILOVER_AUTH_REQUEST 패킷 보내서 투표 요청함
- 다른 마스터가
FAILOVER_AUTH_ACK패킷 보내서 투표 동의함 - 이 때 마스터가 동시에 다른 복제본 승격 방지 위해
NODE_TIMEOUT*2시간 동안 같은 마스터로 승격하려는 다른 복제본에게 투표 못 함 - 복제본 쪽에서 현재 에포크 보다 작은 에포크로 온
AUTH_ACK는 무시함
과반수 마스터 동의되면 복제본이 페일오버 시작
- 만약
NODE_TIMEOUT*2시간 동안 과반수 마스터로부터 ACK없으면 페일오버 중단됨 NODE_TIMEOUT*4시간 후에 다시 새 투표 시도함
복제본이 아래 딜레이 시간 기다리고 투표 요청 보냄
-
DELAY = 500ms + 랜던 지연 시간 (0~500ms) + SLAVE_RANK*1000ms
SLAVE_RANK 복제본 우선순위 (마스터 FAIL되면 복제본끼리 통신해서 우선순위 정함. 최근 오프셋 가지면 0순위)
- 같은 마스터의 복제본이 동시에 요청하게 하지 않기 위함
'SearchDeveloper > 개발자를 위한 레디스' 카테고리의 다른 글
| 12장 클라이언트 관리 (0) | 2026.06.07 |
|---|---|
| 11장 보안 (0) | 2026.06.07 |
| 9장 센티널 (0) | 2026.06.07 |
| 8장 복제 (1) | 2026.06.07 |
| 7장 레디스 데이터 백업 방법 (0) | 2026.02.08 |
