kvcache-ai / kvcache-ai/Mooncake

[RFC]: Hot Standby Mode for Master Service Metadata High Availability

Open
#1,200 4 comments 3 reactions 0 assignees View on GitHub
good first issue
Dominant language
C++
Stars
6.6k
Forks
1.2k
Avg merge
3d 5h
Merged PRs (30d)
312

Description

## Introduction

To address the issue where all metadata is lost after a master service failure and recovery, this solution proposes a **hot-standby synchronization mechanism** and a **hybrid approach** combining hot standby with snapshots, ensuring rapid data recovery following a master service failure.

## Motivation

The current HA (High Availability) mode only supports rapid restart of the master service after a failure but cannot guarantee fast data recovery. The pain points of the current HA mode are as follows:

| Issue | Impact | Severity |
| -------------------------------------------------- | ------------------------------------------------------------ | ---------- |
| **Complete loss of metadata after Leader failure** | All KV cache location information is lost and must be rebuilt | 🔴 Critical |
| **Follower lacks data pre-warming** | After successful election, the new Leader must start serving from scratch | 🟠 Medium |
| **Long recovery time** | Relies on clients to re-register; may take several minutes to tens of minutes | 🟠 Medium |
| **Service unavailability window** | Requests cannot be processed during Leader failover | 🟡 Low |

## Goals:

1. **Near-zero RPO**: Minimize the data loss window
2. **Fast failover**: Achieve second-level RTO (Recovery Time Objective)
3. **Data consistency guarantee**: Ensure correct synchronization between primary and standby
4. **Verifiability**: Include mechanisms to validate data integrity
5. **Low performance overhead**: Avoid impacting normal business operations

## Proposal

### 1. Core Design Approach

**Use etcd as an intermediate reliability component to implement OpLog primary-standby synchronization**:

- **OpLog Mechanism**: Primary Master records all state change operations to OpLog
- **etcd Storage**: OpLog is written to etcd, leveraging etcd's strong consistency and persistence capabilities
- **Watch Mechanism**: Standby Master receives OpLog in real-time through etcd Watch mechanism
- **Ordering Guarantee**: Guarantee operation order through global sequence_id and key-level key_sequence_id

### 2. Architecture Design

#### (1) Overall Architecture

The overall architecture diagram shows the interaction relationships between Primary Master, etcd Cluster, and Standby Master:

Image

**Architecture Description**:

- **Primary Master**: Handles client requests, records OpLog and writes to etcd
- **etcd Cluster**: Acts as intermediate storage, providing strong consistency and Watch mechanism
- **Standby Master**: Receives OpLog in real-time by watching etcd and applies to local metadata store

#### (2) Data Flow Diagram

The data flow diagram shows the complete flow from Client request to Standby synchronization:

Image

**Flow Description**:

a. Client sends `PutEnd` request to Primary Master
b. Primary Master records operation through `OpLogManager`, generating sequence_id
c. `EtcdOpLogStore` writes OpLog to etcd
d. etcd notifies Standby Master through Watch mechanism
e. `OpLogWatcher` receives events and passes to `OpLogApplier`
f. `OpLogApplier` checks order and applies to Standby's metadata store

#### (3) Failover Sequence

The failover sequence diagram shows the complete process from Primary failure to Standby promotion to Primary:

Image

**Flow Description**:

a. **Normal Operation**: Primary maintains Lease, Standby continuously synchronizes OpLog through Watch
b. **Primary Failure**: Primary's Lease expires, etcd notifies Standby
c. **Standby Promotion**: Stop Standby service, initialize Lease, clean expired metadata, start leader election

### 3. Core Component Design

#### (1) OpLogManager (Primary Side)

**Responsibilities**:

- Record all state change operations (PUT_END, PUT_REVOKE, REMOVE)
- Generate global sequence_id and key-level key_sequence_id
- Maintain memory buffer (for fast queries)

**Key Methods**:

```cpp
class OpLogManager {
uint64_t Append(OpType type, const std::string& key,
const std::string& payload = "");
std::vector GetEntriesSince(uint64_t since_seq_id,
size_t limit = 1000) const;
uint64_t GetLastSequenceId() const;
};
```

#### (2) EtcdOpLogStore (Primary Side)

**Responsibilities**:

- Write OpLog to etcd
- Update latest sequence_id
- Record snapshot corresponding sequence_id
- Clean up old OpLog

**etcd Key Design**:

- OpLog Entry: `mooncake-store/oplog/{cluster_id}/{sequence_id}`
- Latest Sequence ID: `mooncake-store/oplog/{cluster_id}/latest`
- Snapshot Sequence ID: `mooncake-store/oplog/{cluster_id}/snapshot/{snapshot_id}/sequence_id`

#### (3) OpLogWatcher (Standby Side)

**Responsibilities**:

- Watch etcd OpLog changes
- Read historical OpLog (for initial synchronization)
- Process Watch events and pass to OpLogApplier

**Key Methods**:

```cpp
class OpLogWatcher {
void Start();
void Stop();
bool ReadOpLogSince(uint64_t start_seq_id,
std::vector& entries);
};
```

#### (4) OpLogApplier (Standby Side)

**Responsibilities**:

- Apply OpLog Entry to local metadata store
- Check global and key-level order
- Handle sequence number discontinuities and out-of-order cases
- Periodically clean up key_sequence_map_ (memory optimization)

**Key Methods**:

```cpp
class OpLogApplier {
bool ApplyOpLogEntry(const OpLogEntry& entry);
bool CheckSequenceOrder(const OpLogEntry& entry);
void CleanupStaleKeySequences();
};
```

#### (5) HotStandbyService (Standby Side)

**Responsibilities**:

- Manage Standby mode lifecycle
- Coordinate OpLogWatcher and OpLogApplier
- Handle Standby promotion to Primary logic

**Key Methods**:

```cpp
class HotStandbyService {
void StartStandby();
void Stop();
void Promote();
};
```

### 4. OpLog Entry Data Structure

```cpp
struct OpLogEntry {
uint64_t sequence_id{0}; // Globally monotonically increasing sequence
uint64_t timestamp_ms{0}; // Timestamp (milliseconds)
OpType op_type{OpType::PUT_END}; // PUT_END, PUT_REVOKE, REMOVE
std::string object_key; // Object key
std::string payload; // Optional payload (carries replica info for PUT_END)
uint32_t checksum{0}; // Checksum
uint32_t prefix_hash{0}; // Key prefix hash
uint64_t key_sequence_id{0}; // Per-key operation sequence (for ordering guarantee)
};
```

**JSON Serialization Format**:

```json
{
"sequence_id": 12345,
"timestamp": 1704110400123,
"op_type": "PUT_END",
"key": "object_key_123",
"payload": "optional_payload",
"checksum": 1234567890,
"prefix_hash": 987654321,
"key_sequence_id": 5
}
```

### 5. Ordering Guarantee Mechanism

#### (1) Global Sequence Number (sequence_id)

- **Purpose**: Guarantee global order of all OpLog events
- **Generation**: Generated globally incrementally by Primary's `OpLogManager`
- **Check**: Standby checks if sequence_id is continuous

#### (2) Key-Level Sequence Number (key_sequence_id)

- **Purpose**: Guarantee operation order for the same key
- **Generation**: Incremented separately for each key on Primary side
- **Check**: Standby checks if key_sequence_id is increasing

#### (3) Out-of-Order Handling

When key_sequence_id out-of-order is detected:

- **Rollback**: Delete all state of the key from metadata_store
- **Replay**: Re-read all OpLog from etcd starting from the key's first sequence_id
- **Rewrite**: Re-apply all OpLog in correct order to rebuild metadata

### 6. Snapshot Integration

#### (1) Record Sequence ID During Snapshot

- When snapshot is generated, record current OpLog sequence_id
- Write snapshot info to etcd: `mooncake-store/oplog/{cluster_id}/snapshot/{snapshot_id}/sequence_id`

#### (2) OpLog Cleanup

- After snapshot generation, OpLog before snapshot can be cleaned up
- Cleanup strategy: Query minimum existing sequence_id from etcd, use DeleteRange to delete

### 7. Standby Service Integration

#### (1) Problem

In existing code, Standby only blocks and waits during leader election, without running Standby service to synchronize OpLog.

#### (2) Solution

In `MasterServiceSupervisor::Start()`:

- Check if there is currently a leader
- If there is a leader and it's not self → Start Standby service (watch OpLog and apply)
- After successful election → Stop Standby service and promote to Primary

### 8. Lease Initialization When Standby Promotes to Primary

#### (1) Problem

Objects on Standby all have lease = 0 (because OpLog only contains PUT_END, not renewal information), and all objects will expire immediately after promotion to Primary.

#### (2) Solution

In `HotStandbyService::Promote()`:

- Stop Standby service
- Iterate through all metadata
- For objects with lease_timeout = 0, grant default lease time (`default_kv_lease_ttl`)

### 9. Memory Optimization: key_sequence_map_ Cleanup

#### (1) Problem

`key_sequence_map_` on Standby side is used to track `key_sequence_id` for each key. After metadata is deleted, these entries are still retained, which may cause memory leaks during long-term operation.

#### (2) Solution

Implement periodic cleanup mechanism:

- **Cleanup Condition**: Last operation is `REMOVE` and more than 1 hour has passed
- **Cleanup Frequency**: Scan once per hour
- **Retention Strategy**: Keys with `PUT_END` and `PUT_REVOKE` operations are not cleaned

## Key Design Points Summary

1. **etcd as Intermediate Storage**: Leverage etcd's strong consistency and Watch mechanism
2. **Record Only Critical Operations**: PUT_END, PUT_REVOKE, REMOVE, do not record LEASE_RENEW
3. **Dual Sequence Number Guarantee**: Global sequence_id + key-level key_sequence_id
4. **Snapshot Integration**: Integrate with existing snapshot mechanism, support OpLog cleanup
5. **Standby Service Runs in Parallel**: Continuously synchronize data during election waiting period
6. **Memory Optimization**: Periodically clean up expired entries in key_sequence_map_

### Before submitting a new issue...

- [ ] Make sure you already searched for relevant issues and read the [documentation](https://kvcache-ai.github.io/Mooncake/)

Contributor guide

Open the contributing guide

Research direction

Start by reading MasterServiceSupervisor::Start() and the proposed HotStandbyService, OpLogManager, OpLogWatcher, and OpLogApplier responsibilities. Trace the existing leader-election and snapshot flows before deciding how the proposed etcd synchronization fits. Done means the design is resolved into an implementable change that covers synchronization, promotion, ordering, snapshots, and key_sequence_map_ cleanup.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend, distributed-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.