syntax = "proto3";
package d_engine.server.cluster;
import "proto/common.proto";
import "proto/server/storage.proto";
option go_package = "github.com/deventlab/d-engine/proto/server/cluster";
//Configuration change request specifies the operation type
message ClusterConfChangeRequest {
// the request sender id, (might be fake leader or real leader)
uint32 id = 1;
// leader term
uint64 term = 2;
// so follower can redirect clients
// when receiver receives the configure, they need compare the version
// value is timestamp by default.
uint64 version = 3;
d_engine.common.MembershipChange change = 4;
}
message ClusterConfUpdateResponse {
enum ErrorCode {
ERROR_CODE_UNSPECIFIED = 0;
ERROR_CODE_NOT_LEADER = 1; // Request sent to non-leader
ERROR_CODE_VERSION_CONFLICT = 2; // Stale configuration version
ERROR_CODE_TERM_OUTDATED = 3; // Stale leader term
ERROR_CODE_INVALID_CHANGE = 4; // Malformed change request
ERROR_CODE_INTERNAL_ERROR = 5; // Server-side processing error
}
// record down the response owner id
uint32 id = 1;
uint64 term = 2;
uint64 version = 3;
bool success = 4;
ErrorCode error_code = 5;
}
message MetadataRequest {}
message ClusterMembership {
uint64 version = 1;
repeated NodeMeta nodes = 2;
// Current leader ID - dynamic runtime info
// None (absent): leader unknown or still electing
// Some(id): id is the current leader node ID
optional uint32 current_leader_id = 3;
}
message NodeMeta {
uint32 id = 1;
string address = 2; //"ip:port"
int32 role = 3;
d_engine.common.NodeStatus status = 4; // Add new status fields (such as active/draining)
}
// Request from new node to join the cluster
message JoinRequest {
// Unique ID for the new node
uint32 node_id = 1;
// Role for the new node
int32 node_role = 2;
// Network address of the new node
string address = 3;
// Desired status of the node (PROMOTABLE or READ_ONLY)
d_engine.common.NodeStatus status = 4;
}
enum ConfigState {
// Stable state - cluster uses a single configuration
STABLE = 0;
// Configuration Transition State - The cluster is applying configuration changes
CONFIG_TRANSITION = 1;
// Election State - The cluster is in the process of leader election
ELECTION_IN_PROGRESS = 2;
}
message JoinResponse {
// Returns true if joining is successful, false otherwise
bool success = 1;
// Error message (if any)
string error = 2;
// Current cluster configuration (including all nodes and roles)
ClusterMembership config = 3;
// Current cluster configuration version (new nodes must save this version number and bring it with subsequent requests)
uint64 config_version = 4;
// If the new node needs to receive a snapshot, this field contains the snapshot metadata
// If no snapshot is needed, this field is empty
optional d_engine.server.storage.SnapshotMetadata snapshot_metadata = 5;
// The ID of the current leader (if it exists), the new node can connect to the leader to get the snapshot
uint32 leader_id = 6;
}
// New messages
message LeaderDiscoveryRequest {
// Requester's node ID
uint32 node_id = 1;
// Requester's address
string requester_address = 2;
}
message LeaderDiscoveryResponse {
// Current leader's node ID (0 if unknown)
uint32 leader_id = 1;
// Current leader's address (empty if unknown)
string leader_address = 2;
// Current term
uint64 term = 3;
}
service ClusterManagementService {
rpc UpdateClusterConf(ClusterConfChangeRequest) returns (ClusterConfUpdateResponse);
rpc GetClusterMetadata(MetadataRequest) returns (ClusterMembership);
// Request to join the cluster as a new learner node
rpc JoinCluster(JoinRequest) returns (JoinResponse);
// New RPC for leader discovery
rpc DiscoverLeader(LeaderDiscoveryRequest) returns (LeaderDiscoveryResponse);
}