grep

Engineering

Building Resilient, High Performance ScyllaDB Clusters with Super Disk

하이퍼커넥트

2025년 5월 15일

원문에서 보기 ↗

안녕하세요, SRE - Database Platform Unit(DBP)의 stewart입니다.

Hyperconnect는 전사 NoSQL 데이터베이스로 ScyllaDB를 약 4년 동안 운영하며, ML feature store[2] 등 전사의 매우 다양한 서비스의 고성능 요구사항을 충족해왔습니다. 하지만 최근 장애 모의 훈련에서 cluster rolling update 시 node 복구 시간이 지나치게 길다는 문제를 발견했습니다. 본 글에서는 이 문제를 해결하기 위해 도입한 Super Disk (Write-mostly RAID) 기능이 어떻게 cluster rolling update 시간을 기존 대비 최대 10배 이상 단축 시켰으며 , 성능상에는 어떤 차이가 있는지, 이러한 결론을 바탕으로 해당 기능을 Kubernetes 환경에서 더욱 효율적으로 관리하기 위해 도입한 Windmill 기반 자동화 과정까지 단계별로 소개합니다.

1. ScyllaDB 란?

먼저 DBP에서 전사 NoSQL DB로 사용하고 있는 ScyllaDB에 대해 소개하려고 합니다. ScyllaDB는 Cassandra와 호환되는 wide-column Database로, 다음과 같은 특징과 장점을 지닙니다.

Discord, Zillow 등 여러 글로벌 기업이 ScyllaDB를 활용해 성공 사례를 공유하고 있으며, Hyperconnect 또한 지난 4년 동안 Production 환경에서 안정적으로 운영하고 있습니다.


2. 문제 상황: ScyllaDB node 장애 시 node 롤링 시간이 너무 느리다!

최근 SRE 팀에서 진행한 장애 모의 훈련[5]에서는 ScyllaDB node 장애 상황도 함께 테스트했습니다.

테스트에서는 ScyllaDB가 운영 중인 EC2 인스턴스를 강제로 종료한 후 복구 시간을 측정하였으며, disk 용량에 따라 평균 3~4시간, 데이터 양이 많은 경우에는 최대 12시간 정도 소요되는 것으로 나타났습니다.

이 경우, 일반적인 read/write 성능에는 문제가 없지만, 여러 node가 동시에 장애가 발생하면 cluster 전체 복구에 상당한 지연이 발생합니다. 이는 ScyllaDB가 Local SSD 타입 인스턴스(i4i)를 사용해야 하기 때문입니다.

Cassandra와 달리 ScyllaDB는 Local NVMe SSD 사용을 권장[6]]하는데, 이는 ScyllaDB가 Cassandra와는 전혀 다른 caching 및 disk I/O 전략을 사용하기 때문입니다. 이 전략을 제대로 이해하려면 먼저 ScyllaDB의 caching 방식에 대해 알아볼 필요가 있습니다.

ScyllaDB 의 WhitePaper 7 Reasons Not to Put an External Cache in Front of Your Database를 참고해 보면, ScyllaDB의 Embedded Cache 설계에 대해 상세하게 알 수 있습니다.

  1. Cassandra의 Cache 접근 방식

    cassandra-cache.png

    • Cassandra는 OS 차원의 Linux page cache와 자체 key cache, row cache 등을 함께 사용합니다.
    • Cassandra의 경우, DB Admin이 각 cache 메모리 크기(JVM heap/off-heap 포함)를 세밀히 조정해야 하며 워크로드가 동적으로 변할 때마다 지속적인 튜닝이 필요합니다.
    • 또한 page cache를 사용할 경우, Linux page cache가 4KB 단위로 동작하기에, 4KB 미만의 데이터가 많은 NoSQL 워크로드에서는 read amplification(읽기 증폭)이 발생할 수 있습니다.
  2. ScyllaDB의 Embedded Cache

    • Linux page cache 대신 내부적으로 cache를 일원화해 관리합니다.
    • 객체 단위로 caching하고, memtable, compaction, cache controller 등 여러 컨트롤러가 동적으로 cache 크기를 조정합니다.
    • cache 미스 시 DMA로 disk에서 데이터를 비동기로 읽어오고, Seastar 프레임워크로 컨텍스트 스위치 오버헤드를 최소화합니다.
    • 운영자는 cache 크기를 수동으로 분배할 필요가 없으며, ScyllaDB가 자동 최적화를 수행하므로 관리 부담이 크게 줄어듭니다.

하지만 이렇게 Embedded Cache를 사용하면, Cache miss 시 disk I/O가 병목이 될 수 있습니다.

Cassandra처럼 Linux page cache에 크게 의존하는 구조였다면, Local SSD가 아니라도 page cache가 어느 정도 성능을 방어해 줄 수 있습니다. 그러나 ScyllaDB는 cache를 자체적으로 관리하고, 성능이 disk 속도에 직접적으로 좌우되므로, NVMe SSD의 빠른 접근 성능이 훨씬 중요해집니다.

그렇기에 현재 Hyperconnect에서는 AWS가 제공하는 i4i.4xlarge(Local NVMe disk 내장) instance를 이용해 ScyllaDB를 운영 중입니다. 그러나 클라우드 환경에서 Local NVMe가 붙은 instance를 사용할 경우, instance를 재부팅하면 disk 데이터가 전부 유실된다는 문제가 존재합니다.

물론 ScyllaDB의 다양한 HA 설정(Replication Factor, Cross-DC 설정, Consistency level 등)을 통해 node가 다운되더라도 데이터 유실은 방지할 수 있습니다. 하지만 node를 복구할 때, 다른 node에서 모든 데이터를 가져와야 하므로 복구 시간이 너무 길어지는 단점이 있습니다.

또한 일반적인 EC2 유지보수를 위해 Node를 재부팅하거나 AMI를 변경하는 작업에도 상당한 시간이 소요됩니다. 현재 운영 환경에서는 i4i.4xlarge(3.5TB) node 한 대를 교체하는 데 약 18~24시간이 걸려, 평소 유지보수 작업 시간도 크게 늘어나는 단점이 있습니다.


2.1 새로운 대안: ScyllaDB Super-disk

이러한 문제 해결을 위해 조사하던 중, Discord에서 작성한 How Discord Supercharges Network Disks for Extreme Low Latency을 접하게 되었습니다.

해당 글을 요약하면, Discord는 ScyllaDB를 운영할 때 Persistent Disk 기반 환경에서 Read latency 지연 문제를 겪었다고 합니다. 이후 Local SSD로 옮기려 했으나, 같은 문제(instance 재부팅 시 데이터 유실)를 확인했고, 이를 해결하기 위해 글 작성자들이 Super Disk 구성을 적용했습니다.

해당 구성은 Local SSD와 Persistent Disk를 RAID 할 뿐 아니라, 추가로 Write-mostly라는 RAID 설정을 통해 Persistent Disk로는 쓰기, Local SSD에서만 읽기를 수행하도록 구성해 읽기 지연을 크게 줄였다는 것이 골자입니다.

저희는 이 아이디어에 착안해 영속성과 latency를 둘 다 챙길 수 있는 Super Disk를 적용할 수 있을지 본격적으로 검토를 시작했습니다.


2.2 PoC 시작: ScyllaDB Super-disk

테스트 환경을 위해 같은 스펙(i4i.4xlarge)의 node로 구성된 cluster를 두 개 띄운 뒤, 하나는 Super Disk를 적용하지 않고, 다른 하나에는 Super Disk 설정을 적용해 비교 테스트를 진행했습니다.

사용한 Node spec 및 cluster 설정은 다음과 같습니다.

Instancepod CPUpod memoryscylla embeded memory sizeScylladb version
i4i.4xlarge14110Gi100G5.4.9

먼저 ScyllaDB를 배포하기 전에 EBS를 provisioning 하고, 해당 EBS를 instance에 attach 한 뒤 RAID를 구성했습니다. EBS 용량은 i4i.4xlarge 의 Nvme Disk와 유사하게 3.4TB 정도를 설정했습니다. (3492GB)

이후, EBS를 mount 한 instance 에서 다음과 같이 RAID를 생성했습니다.

## 새 RAID 생성 (데이터가 없어야 함)
yes | sudo mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/nvme1n1 --write-mostly  /dev/nvme2n1 --assume-clean
mkfs -t xfs /dev/md0
mount /dev/md0 /data

## RAID 상태 확인
cat /proc/mdstat
Personalities : [raid1]
md0 : active raid1 nvme1n1[2] nvme2n1[0](W)
      3661496320 blocks super 1.2 [2/2] [UU]
      bitmap: 5/28 pages [20KB], 65536KB chunk

## 마운트 상태
lsblk -b -o NAME,SIZE,TYPE
NAME                   SIZE TYPE
nvme0n1        162135015424 disk
├─nvme0n1p1    162132901376 part
└─nvme0n1p128       1048576 part
nvme1n1       3750000000000 disk
└─md0         3749372231680 raid1
nvme2n1       3749506449408 disk
└─md0         3749372231680 raid1

이후 ScyllaDB를 배포한 후, 사내에서 Database benchmark을 수행할 때 사용하는 YCSB benchmark tool을 이용해 총 7억 Row (약 node 당 1.5TB)의 데이터를 삽입했습니다. 그런 다음 장애 상황에서 Super Disk가 적용된 cluster가 어떻게 동작하는지 확인했습니다.

2.3 장애 상황 시나리오 테스트

이 실험으로 확인한 것은, EBS가 장애 나거나 i4i instance가 장애를 일으켜도 데이터 유지 및 빠른 복구가 가능하다는 점입니다.

최악의 상황으로, 만약 두 disk 전부 문제가 생긴다고 해도, 기존에 사용했던 방식대로 ScyllaDB의 nodetool replace 명령을 통해 다른 Node의 데이터를 복사해와 해당 Node의 데이터를 복구하는 것도 가능합니다.

하지만 가장 중요한 성능(throughput/latency) 차이는 어떨까요? 다음 항목에서 성능 Benchmark 결과를 살펴봅니다.


2.4 Performance Benchmark

초기 Data Load 시 사용했던 tool 인 YCSB를 이용해 실제 Production과 유사한 read/write/update 비율로 Benchmark를 진행했습니다.

paramsvalue
recordcount700000000
operationcount800000000
readproportion70%
insertproportion20%
updateproportion10%
threadcount50
scylla.connection.pool.local.size1
scylla.connection.max-requests1024
consistencyLevelLOCAL_QUORUM

RAID를 적용하지 않은 i4i instance의 성능 벤치마크 결과

RAID를 적용한 i4i instance의 성능 벤치마크 결과

실제로 metric을 확인해 보니, Super Disk를 구성한 cluster와 그렇지 않은 cluster 간 성능 차이가 거의 없었습니다.

compaction 타이밍에 따라 Read latency가 일시적으로 튈 때가 있지만, 전반적인 throughput과 Write latency 모두 크게 차이가 없음을 확인했습니다. 기타 다른 워크로드 시나리오를 돌려 봐도, Super Disk 구성 여부에 따른 성능 차이는 미미했습니다.

추가적으로, Benchmark 결과 교차 검증을 위해, ScyllaDB에서 제공하는 ScyllaDB/Cassandra 를 benchmark 를 할 때 사용하는 tool 인 Latte 을 이용해, 다양한 상황에도 local ssd instance 와, raid 된 ScyllaDB 의 latency 가 비슷한지 측정을 진행하였습니다.

ScyllaDB latte 는 Rust 로 작성되었으며, benchmark 설정 테스트를 직접 custom 하게 write 할 수 있어 다양한 Case 에 대해 benchmark test 를 수행할 수 있습니다.

Benchmark 설정은 이번에 ScyllaDB 에서 주최한 ScyllaDB Monster Scale Summit[6] 에 소개된 ScyllaDB is No Longer "Just a Faster Cassandra" session을 참조하여, benchmark workload[7] 를 작성하여 직접 테스트했습니다.

paramsvalue
row_count100000000
cache_rows10000000
Rate200000
standard1_insert4%
standard1_insert_cache16%
standard1_select16%
standard1_select_cache64%

해당 workload 시나리오 후, 동일한 환경에서 Super Disk가 적용되지 않은 cluster와 Super Disk가 적용된 cluster를 각각 Benchmark한 결과, 다음과 같은 결과가 도출되었습니다. i4i.4xlarge wih no raid - benchmark results

SUMMARY STATS ══════════════════════════════════════════════════════════════════════════════════════════════════════════════
    Elapsed time       [s]   1800.010
        CPU time       [s]  17936.141
 CPU utilisation       [%]       33.2
          Cycles      [op]  359999850
          Errors      [op]          0
              └─       [%]        0.0
        Requests     [req]  359999850
              └─  [req/op]        1.0
         Retries     [ret]          0
              └─ [ret/req]    0.00000
            Rows     [row]  104598453
              └─ [row/req]        0.3
     Concurrency     [req]         14 ± 0
              └─       [%]         84
      Throughput    [op/s]     199999 ± 229
              ├─   [req/s]     199999 ± 229
              └─   [row/s]      58110 ± 66
   Cycle latency      [ms]      6.677 ± 14.502
 Request latency      [ms]      1.118 ± 0.000

CYCLE LATENCY for standard1_select_cache [ms]  ═════════════════════════════════════════════════════════════════════════════
          Min                   0.662 ± 0.103
           25                   1.046 ± 0.089
           50                   1.180 ± 0.093
           75                   1.372 ± 0.178
           90                   1.719 ± 0.888
           95                   2.599 ± 60.779
           98                  12.829 ± 521.082
           99                 257.032 ± 679.258
           99.9               511.967 ± 668.300
           99.99              536.347 ± 668.300
          Max                 553.648 ± 668.300

CYCLE LATENCY for standard1_insert [ms]  ═══════════════════════════════════════════════════════════════════════════════════
          Min                   0.703 ± 0.081
           25                   1.048 ± 0.052
           50                   1.174 ± 0.068
           75                   1.351 ± 0.100
           90                   1.628 ± 0.444
           95                   2.587 ± 2.974
           98                  12.665 ± 368.587
           99                 256.770 ± 552.164
           99.9               511.705 ± 472.421
           99.99              536.084 ± 472.421
          Max                 553.124 ± 472.421

CYCLE LATENCY for standard1_insert_cache [ms]  ═════════════════════════════════════════════════════════════════════════════
          Min                   0.708 ± 0.108
           25                   1.048 ± 0.067
           50                   1.174 ± 0.093
           75                   1.350 ± 0.148
           90                   1.628 ± 1.064
           95                   2.587 ± 59.545
           98                  12.763 ± 498.869
           99                 257.294 ± 648.260
           99.9               511.705 ± 655.849
           99.99              536.347 ± 655.849
          Max                 554.172 ± 655.849

CYCLE LATENCY for standard1_select [ms]  ═══════════════════════════════════════════════════════════════════════════════════
          Min                   0.672 ± 0.106
           25                   1.036 ± 0.068
           50                   1.169 ± 0.090
           75                   1.362 ± 0.184
           90                   1.709 ± 1.011
           95                   2.593 ± 43.601
           98                  12.747 ± 506.751
           99                 256.770 ± 629.359
           99.9               511.705 ± 559.417
           99.99              536.347 ± 559.417
          Max                 552.600 ± 559.417

i4i.4xlarge wih EBS raid - benchmark results

SUMMARY STATS ══════════════════════════════════════════════════════════════════════════════════════════════════════════════
    Elapsed time       [s]   1800.011
        CPU time       [s]  18014.552
 CPU utilisation       [%]       33.4
          Cycles      [op]  359999836
          Errors      [op]          0
              └─       [%]        0.0
        Requests     [req]  359999836
              └─  [req/op]        1.0
         Retries     [ret]          0
              └─ [ret/req]    0.00000
            Rows     [row]  104598449
              └─ [row/req]        0.3
     Concurrency     [req]         13 ± 0
              └─       [%]         79
      Throughput    [op/s]     199999 ± 519
              ├─   [req/s]     199999 ± 519
              └─   [row/s]      58110 ± 151
   Cycle latency      [ms]     11.578 ± 27.880
 Request latency      [ms]      1.029 ± 0.000

CYCLE LATENCY for standard1_select_cache [ms]  ═════════════════════════════════════════════════════════════════════════════
          Min                   0.568 ± 0.131
           25                   0.960 ± 0.086
           50                   1.110 ± 0.124
           75                   1.326 ± 0.182
           90                   1.574 ± 0.334
           95                   1.794 ± 51.754
           98                   4.440 ± 322.745
           99                 273.154 ± 974.993
           99.9               509.949 ± 984.576
           99.99              571.815 ± 984.576
          Max                 589.641 ± 984.576

CYCLE LATENCY for standard1_select [ms]  ═══════════════════════════════════════════════════════════════════════════════════
          Min                   0.572 ± 0.122
           25                   0.950 ± 0.069
           50                   1.099 ± 0.128
           75                   1.317 ± 0.181
           90                   1.566 ± 0.312
           95                   1.784 ± 57.992
           98                   4.420 ± 346.955
           99                 272.892 ± 997.156
           99.9               509.949 ± 899.543
           99.99              571.815 ± 899.543
          Max                 589.641 ± 899.543

CYCLE LATENCY for standard1_insert [ms]  ═══════════════════════════════════════════════════════════════════════════════════
          Min                   0.614 ± 0.090
           25                   0.960 ± 0.068
           50                   1.104 ± 0.093
           75                   1.316 ± 0.146
           90                   1.551 ± 0.207
           95                   1.748 ± 1.830
           98                   4.420 ± 029.409
           99                 277.086 ± 521.071
           99.9               510.998 ± 801.777
           99.99              571.815 ± 801.777
          Max                 587.544 ± 801.777

CYCLE LATENCY for standard1_insert_cache [ms]  ═════════════════════════════════════════════════════════════════════════════
          Min                   0.610 ± 0.100
           25                   0.959 ± 0.076
           50                   1.103 ± 0.104
           75                   1.315 ± 0.162
           90                   1.551 ± 0.254
           95                   1.748 ± 157.607
           98                   4.403 ± 200.208
           99                 271.843 ± 933.148
           99.9               509.949 ± 954.778
           99.99              571.815 ± 954.778
          Max                 587.544 ± 954.778

ScyllaDB Latte를 활용한 벤치마크 결과, 현재 production workload 스펙 기준으로 RAID를 적용한 cluster와 적용하지 않은 cluster 간의 읽기/쓰기 속도 차이는 크지 않았습니다.

또한, 해당 benchmark 설정 외 다른 워크로드 시나리오를 적용해 본 결과, Super Disk 구성 여부에 따른 성능 차이도 미미한 것으로 나타났습니다.

이러한 실험을 통해, 다음과 같은 결론을 내렸습니다.

이러한 결과들을 종합해 보면, RAID 기반 Super Disk 구성이 기존 i4i(Local SSD) 방식과 동일한 성능을 유지하면서도 장애 복구 시간을 단축하고 데이터 전송 비용을 줄일 수 있음이 명확해졌습니다.

이에 따라, 내부 검토를 거쳐 SRE 팀은 ScyllaDB에 Super Disk 기능을 도입하기로 최종 결정하였습니다.


3. Super Disk Migration 시작!

Super Disk 도입을 결정한 뒤 RAID 관련 작업을 자동화해야 했습니다. script 자체는 크게 복잡하지 않았지만, 이를 어디서 어떻게 실행할지가 문제였습니다.

저희는 이러한 요구사항을 DevOps 팀과 같이 논의했고, 최근 DevOps team 에서 전사 도입 중인 Windmill 라는 Developer Platform tool 을 이용하여 이 작업을 간소화했습니다.

Windmill은 "Developer platform for APIs, background jobs, workflows and UIs"를 표방하는 도구로,

Airflow, Retool, Scheduler의 장점을 결합한 자동화 지원 툴입니다.

이러한 장점을 고려해, ScyllaDB node 교체 작업 자동화에 Windmill을 활용했습니다.

3.1 ScyllaDB Super Disk Workflow

ScyllaDB Super Disk를 적용하기 위해, 총 2가지의 workFlow 를 작성하였습니다.

이 과정을 QA/Stage 환경에서 수십 차례 반복 테스트해 발생할 수 있는 예외 상황을 핸들링했고, 오류 발생 시 Flow를 중단 후 재시작 가능하도록 보완했습니다. Windmill은 각 Flow Step 단위로 작업 결과를 UI에 표시하고, 특정 Step만 골라 재실행할 수 있어 디버깅이 매우 편리했습니다.

3.2 Workflow in production

script 안정성을 위해, QA와 Stage 환경에서 정해진 횟수 이상 연속 성공을 목표로 충분히 검증한 뒤, Production에서도 동일하게 Migration을 진행했습니다.

결과적으로 기존 대비 node 교체 시간이 매우 단축되었으며, 실 운영 환경에서 Throughput이나 Latency 등 주요 지표도 정상적으로 유지됨을 확인했습니다.

아래는 Production 환경에서 No-Raid와 Raid 성능을 비교한 실제 환경의 metric 입니다.

RAID를 적용한 i4i instance의 Production 성능 결과

RAID를 적용하지 않은 i4i instance의 Production 성능 결과

실제로 Benchmark와 마찬가지로 Throughput·Latency 차이가 거의 없었습니다.

또한 disk 메트릭을 확인해 보면, --write-mostly 옵션이 정상 작동해 Local SSD에서만 Read가 발생함을 확인했습니다.

write-mostly.png

4.글을 마치며

이번 글에서 공유드린 내용을 요약하자면 아래와 같습니다.

  1. Super Disk 적용으로 빠른 Latency와 안정성을 동시에 확보
    • 기존 i4i(Local SSD) 운영 방식과 비슷한 성능을 내면서, 데이터 유실 시에도 빠르게 EBS 복구가 가능해 RPO가 크게 개선되었습니다.
      • 기존에는 node 하나를 복구하는데, 18시간~24시간 정도 소요되었으나 현재 RAID 방식으로 수행할 경우 1시간 안에 node 를 복구 가능하여, 약 20배 빠른 복구 속도를 가질수 있게 되었습니다.
    • 또한 장애 모의 훈련 시, 로컬 SSD 장애 및 유실에 대한 우려가 있었는데, RAID EBS 가 Backup 역할을 수행할 수 있어 EBS 에 문제가 없다면 RAID 재구성을 통해 비교적 단시간에 node를 복구할 수 있게 되었습니다.
  2. node 교체 작업 시간 단축
    • 전에는 node를 교체하면 모든 데이터를 복제해 왔어야 했기 때문에 node 하나당 하루 이상 소요되어 운영 부담이 매우 컸습니다.
    • 이제는 RAID 복구만 거치면 되므로 교체 작업에 걸리는 시간이 약 95% 단축되어, 더 자주 정기 유지보수를 할 수 있게 되었습니다.
    • 다른 node에서 데이터를 가져와 복구할 때 발생하던 AWS Data Transfer 비용이 절감되어, Node 교체 시 드는 비용 절감 효과를 얻었습니다.
    • 긴급 상황 시 RAID 복구를 기다리지 않고 EBS만으로 서비스를 빠르게 재개할 수도 있는 옵션도 확보하여, 장애 대응 전략의 유연성을 높였습니다.
  3. Windmill을 통한 자동화
    • AWS, Kubernetes, ScyllaDB 관련 각종 CLI와 API를 하나의 통합된 Flow로 구성하여 복잡한 multi-step 작업을 일관되게 처리할 수 있게 되었습니다.
    • 자동화된 예외 처리와 재시도 메커니즘을 통해 작업 실패율을 98% 이상 감소시켰고, 휴먼 에러로 인한 교체 작업 실패 가능성을 크게 줄였습니다.

앞으로도 Hyperconnect SRE 팀은 team 내부에서 운영하는 모든 Database 및 messaging platform 의 안정성과 성능 향상, 운영 자동화를 위해 지속적인 테스트, 자동화 도구 도입, 그리고 내부 공유를 통한 개선 노력을 이어갈 예정입니다.

긴 글 읽어주셔서 감사합니다!

References

[1] ML Feature Store @ Hyperconnect (DEVIEW 2023)

[2] ScyllaDB vs. Cassandra

[3] Cassandra 4.0 vs. Scylla Open Source 4.4 Benchmark

[4] SRE 팀은 장애에 어떻게 대응할까? Hyperconnect 장애 모의 훈련

[5] ScyllaDB Disk Requirements

[6] 7 Reasons Not to Put an External Cache in Front of Your Database

[7] How Discord Supercharges Network Disks for Extreme Low Latency

[8] YCSB - Yahoo! Cloud Serving Benchmark

[9] Latte - ScyllaDB Benchmark Tool

[10] ScyllaDB Monster Scale Summit

[11] ScyllaDB is No Longer "Just a Faster Cassandra" (ScyllaDB Monster Scale Summit Tech Talk)

[12] Benchmark Workload example for ScyllaDB Latte

[13] Log-Structured Merge-Tree (LSM Tree)

[14] Hinted Handoff (ScyllaDB Documentation)