blob: aa8cce84737cd8254b985dfc6351e6959b21d7bc [file] [log] [blame] [view]
# HugeGraph PD API Reference
This document provides comprehensive API reference for HugeGraph PD, including gRPC services, Protocol Buffers definitions, and usage examples.
## Table of Contents
- [gRPC Services Overview](#grpc-services-overview)
- [Protocol Buffers Definitions](#protocol-buffers-definitions)
- [Core gRPC APIs](#core-grpc-apis)
- [Java Client Library](#java-client-library)
- [REST API](#rest-api)
## gRPC Services Overview
HugeGraph PD exposes multiple gRPC services for cluster management and coordination:
| Service | Proto File | Description |
|---------|------------|-------------|
| **PDGrpc** | `pdpb.proto` | Main PD service: store registration, partition queries, member management |
| **KvServiceGrpc** | `kv.proto` | Distributed key-value operations for metadata storage |
| **HgPdPulseGrpc** | `pd_pulse.proto` | Heartbeat and health monitoring for Store nodes |
| **HgPdWatchGrpc** | `pd_watch.proto` | Watch for partition and store change notifications |
| **DiscoveryServiceGrpc** | `discovery.proto` | Service discovery and registration |
**Proto Location**: `hugegraph-pd/hg-pd-grpc/src/main/proto/`
**Generated Stubs**: `hugegraph-pd/hg-pd-grpc/src/main/java/org/apache/hugegraph/pd/grpc/`
## Protocol Buffers Definitions
### Proto Files Structure
```
hg-pd-grpc/src/main/proto/
├── pdpb.proto # Main PD service RPCs
├── metapb.proto # Core metadata objects (Partition, Shard, Store)
├── meta.proto # Extended metadata definitions
├── pd_common.proto # Common types and enums
├── kv.proto # Key-value service
├── pd_pulse.proto # Heartbeat protocol
├── pd_watch.proto # Watch notification protocol
├── discovery.proto # Service discovery
└── metaTask.proto # Task coordination
```
### Key Message Types
#### Partition
Represents a data partition in the cluster.
```protobuf
message Partition {
uint64 id = 1;
string graph_name = 2;
uint64 start_key = 3;
uint64 end_key = 4;
repeated Shard shards = 5;
PartitionState state = 6;
uint64 version = 7;
}
enum PartitionState {
PState_None = 0;
PState_Normal = 1;
PState_Splitting = 2;
PState_Offline = 3;
}
```
#### Shard
Represents a replica of a partition.
```protobuf
message Shard {
uint64 store_id = 1;
ShardRole role = 2;
}
enum ShardRole {
None = 0;
Leader = 1;
Follower = 2;
Learner = 3;
}
```
#### Store
Represents a Store node in the cluster.
```protobuf
message Store {
uint64 id = 1;
string address = 2; // gRPC address (host:port)
string raft_address = 3; // Raft address for data replication
StoreState state = 4;
map<string, string> labels = 5; // Topology labels (rack, zone, region)
StoreStats stats = 6;
int64 last_heartbeat = 7; // Unix timestamp
uint64 version = 8;
}
enum StoreState {
Unknown = 0;
Up = 1; // Store is online and healthy
Offline = 2; // Store is temporarily offline
Tombstone = 3; // Store is permanently removed
Exiting = 4; // Store is in the process of shutting down
}
message StoreStats {
uint64 capacity = 1; // Total disk capacity (bytes)
uint64 available = 2; // Available disk space (bytes)
uint32 partition_count = 3; // Number of partitions on this store
uint32 leader_count = 4; // Number of partitions where this store is leader
}
```
#### Graph
Represents a graph in the cluster.
```protobuf
message Graph {
string graph_name = 1;
uint32 partition_count = 2;
GraphState state = 3;
}
enum GraphState {
Graph_Normal = 0;
Graph_Deleting = 1;
}
```
## Core gRPC APIs
### 1. PD Service (PDGrpc)
Main service for cluster management.
#### GetMembers
Retrieve all PD members in the cluster.
**Request**:
```protobuf
message GetMembersRequest {}
```
**Response**:
```protobuf
message GetMembersResponse {
ResponseHeader header = 1;
repeated Member members = 2;
Member leader = 3;
}
message Member {
string cluster_id = 1;
string member_id = 2;
string grpc_url = 3; // gRPC endpoint
string raft_url = 4; // Raft endpoint
MemberState state = 5;
}
```
**Java Example**:
```java
import org.apache.hugegraph.pd.grpc.PDGrpc;
import org.apache.hugegraph.pd.grpc.Pdpb;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
// Create gRPC channel
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 8686)
.usePlaintext()
.build();
// Create blocking stub
PDGrpc.PDBlockingStub stub = PDGrpc.newBlockingStub(channel);
// Get PD members
Pdpb.GetMembersRequest request = Pdpb.GetMembersRequest.newBuilder().build();
Pdpb.GetMembersResponse response = stub.getMembers(request);
System.out.println("Leader: " + response.getLeader().getGrpcUrl());
for (Pdpb.Member member : response.getMembersList()) {
System.out.println("Member: " + member.getGrpcUrl() + " State: " + member.getState());
}
// Clean up
channel.shutdown();
```
#### RegisterStore
Register a new Store node with PD.
**Request**:
```protobuf
message RegisterStoreRequest {
RequestHeader header = 1;
Store store = 2;
}
```
**Response**:
```protobuf
message RegisterStoreResponse {
ResponseHeader header = 1;
uint64 store_id = 2; // Assigned store ID
}
```
**Java Example**:
```java
import org.apache.hugegraph.pd.grpc.Metapb;
// Build store information
Metapb.Store store = Metapb.Store.newBuilder()
.setAddress("192.168.1.100:8500")
.setRaftAddress("192.168.1.100:8501")
.setState(Metapb.StoreState.Up)
.putLabels("zone", "zone-1")
.putLabels("rack", "rack-a")
.build();
// Register store
Pdpb.RegisterStoreRequest request = Pdpb.RegisterStoreRequest.newBuilder()
.setStore(store)
.build();
Pdpb.RegisterStoreResponse response = stub.registerStore(request);
long storeId = response.getStoreId();
System.out.println("Registered store with ID: " + storeId);
```
#### GetStoreInfo
Retrieve Store node information.
**Request**:
```protobuf
message GetStoreInfoRequest {
RequestHeader header = 1;
uint64 store_id = 2;
}
```
**Response**:
```protobuf
message GetStoreInfoResponse {
ResponseHeader header = 1;
Store store = 2;
}
```
**Java Example**:
```java
Pdpb.GetStoreInfoRequest request = Pdpb.GetStoreInfoRequest.newBuilder()
.setStoreId(storeId)
.build();
Pdpb.GetStoreInfoResponse response = stub.getStoreInfo(request);
Metapb.Store store = response.getStore();
System.out.println("Store " + store.getId() + " at " + store.getAddress());
System.out.println("State: " + store.getState());
System.out.println("Partitions: " + store.getStats().getPartitionCount());
System.out.println("Capacity: " + store.getStats().getCapacity() / (1024*1024*1024) + " GB");
```
#### GetPartition
Retrieve partition information by partition code.
**Request**:
```protobuf
message GetPartitionRequest {
RequestHeader header = 1;
string graph_name = 2;
uint64 partition_key = 3; // Hash code of the data key
}
```
**Response**:
```protobuf
message GetPartitionResponse {
ResponseHeader header = 1;
Partition partition = 2;
Shard leader = 3; // Current leader shard
}
```
**Java Example**:
```java
String graphName = "social_network";
long partitionKey = 12345L; // Hash of vertex/edge key
Pdpb.GetPartitionRequest request = Pdpb.GetPartitionRequest.newBuilder()
.setGraphName(graphName)
.setPartitionKey(partitionKey)
.build();
Pdpb.GetPartitionResponse response = stub.getPartition(request);
Metapb.Partition partition = response.getPartition();
Metapb.Shard leader = response.getLeader();
System.out.println("Partition " + partition.getId() + " range: [" +
partition.getStartKey() + ", " + partition.getEndKey() + ")");
System.out.println("Leader store: " + leader.getStoreId());
System.out.println("Replicas: " + partition.getShardsCount());
```
#### GetPartitionByCode
Retrieve partition by exact partition code (optimized for routing).
**Request**:
```protobuf
message GetPartitionByCodeRequest {
RequestHeader header = 1;
string graph_name = 2;
uint64 partition_id = 3;
}
```
**Response**:
```protobuf
message GetPartitionByCodeResponse {
ResponseHeader header = 1;
Partition partition = 2;
}
```
**Java Example**:
```java
Pdpb.GetPartitionByCodeRequest request = Pdpb.GetPartitionByCodeRequest.newBuilder()
.setGraphName("social_network")
.setPartitionId(5)
.build();
Pdpb.GetPartitionByCodeResponse response = stub.getPartitionByCode(request);
Metapb.Partition partition = response.getPartition();
// Find leader shard
Metapb.Shard leader = partition.getShardsList().stream()
.filter(s -> s.getRole() == Metapb.ShardRole.Leader)
.findFirst()
.orElse(null);
if (leader != null) {
System.out.println("Route query to store: " + leader.getStoreId());
}
```
### 2. KV Service (KvServiceGrpc)
Distributed key-value operations for metadata storage.
#### Put
Store a key-value pair.
**Request**:
```protobuf
message PutRequest {
string key = 1;
bytes value = 2;
int64 ttl = 3; // Time-to-live in seconds (0 = no expiration)
}
```
**Response**:
```protobuf
message PutResponse {
ResponseHeader header = 1;
}
```
**Java Example**:
```java
import org.apache.hugegraph.pd.grpc.kv.KvServiceGrpc;
import org.apache.hugegraph.pd.grpc.kv.Kv;
KvServiceGrpc.KvServiceBlockingStub kvStub = KvServiceGrpc.newBlockingStub(channel);
// Store configuration
String key = "config/max_retry_count";
String value = "5";
Kv.PutRequest request = Kv.PutRequest.newBuilder()
.setKey(key)
.setValue(com.google.protobuf.ByteString.copyFromUtf8(value))
.setTtl(0) // No expiration
.build();
Kv.PutResponse response = kvStub.put(request);
System.out.println("Stored: " + key);
```
#### Get
Retrieve a value by key.
**Request**:
```protobuf
message GetRequest {
string key = 1;
}
```
**Response**:
```protobuf
message GetResponse {
ResponseHeader header = 1;
bytes value = 2;
}
```
**Java Example**:
```java
Kv.GetRequest request = Kv.GetRequest.newBuilder()
.setKey("config/max_retry_count")
.build();
Kv.GetResponse response = kvStub.get(request);
String value = response.getValue().toStringUtf8();
System.out.println("Retrieved value: " + value);
```
#### Scan
Range scan for keys matching a prefix.
**Request**:
```protobuf
message ScanRequest {
string start_key = 1;
string end_key = 2;
int32 limit = 3; // Max number of results
}
```
**Response**:
```protobuf
message ScanResponse {
ResponseHeader header = 1;
repeated KvPair kvs = 2;
}
message KvPair {
string key = 1;
bytes value = 2;
}
```
**Java Example**:
```java
// Scan all configuration keys
Kv.ScanRequest request = Kv.ScanRequest.newBuilder()
.setStartKey("config/")
.setEndKey("config/\uffff") // End of prefix range
.setLimit(100)
.build();
Kv.ScanResponse response = kvStub.scan(request);
for (Kv.KvPair kv : response.getKvsList()) {
System.out.println(kv.getKey() + " = " + kv.getValue().toStringUtf8());
}
```
### 3. Pulse Service (HgPdPulseGrpc)
Heartbeat and health monitoring for Store nodes.
#### Pulse (Streaming)
Bidirectional streaming for continuous heartbeat.
**Request Stream**:
```protobuf
message PulseRequest {
PulseType pulse_type = 1;
oneof notice {
PulseCreatePartition create_partition = 2;
PulseTransferLeader transfer_leader = 3;
PulseMovePartition move_partition = 4;
PulseDeletePartition delete_partition = 5;
}
}
enum PulseType {
PULSE_TYPE_UNKNOWN = 0;
PULSE_TYPE_STORE_HEARTBEAT = 1;
PULSE_TYPE_PARTITION_HEARTBEAT = 2;
}
```
**Response Stream**:
```protobuf
message PulseResponse {
PulseType pulse_type = 1;
oneof notice {
PulseCreatePartition create_partition = 2;
PulseTransferLeader transfer_leader = 3;
PulseMovePartition move_partition = 4;
PulseDeletePartition delete_partition = 5;
}
}
```
**Java Example**:
```java
import org.apache.hugegraph.pd.grpc.pulse.HgPdPulseGrpc;
import org.apache.hugegraph.pd.grpc.pulse.HgPdPulse;
import io.grpc.stub.StreamObserver;
HgPdPulseGrpc.HgPdPulseStub asyncStub = HgPdPulseGrpc.newStub(channel);
// Response handler
StreamObserver<HgPdPulse.PulseResponse> responseObserver = new StreamObserver<>() {
@Override
public void onNext(HgPdPulse.PulseResponse response) {
System.out.println("Received instruction: " + response.getPulseType());
// Handle instructions from PD (partition transfer, split, etc.)
}
@Override
public void onError(Throwable t) {
System.err.println("Pulse stream error: " + t.getMessage());
}
@Override
public void onCompleted() {
System.out.println("Pulse stream completed");
}
};
// Create bidirectional stream
StreamObserver<HgPdPulse.PulseRequest> requestObserver = asyncStub.pulse(responseObserver);
// Send periodic heartbeat
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
HgPdPulse.PulseRequest heartbeat = HgPdPulse.PulseRequest.newBuilder()
.setPulseType(HgPdPulse.PulseType.PULSE_TYPE_STORE_HEARTBEAT)
.build();
requestObserver.onNext(heartbeat);
}, 0, 10, TimeUnit.SECONDS);
```
### 4. Watch Service (HgPdWatchGrpc)
Watch for partition and store change notifications.
#### WatchPartition
Watch for partition changes in a graph.
**Request**:
```protobuf
message WatchPartitionRequest {
RequestHeader header = 1;
string graph_name = 2;
WatchType watch_type = 3;
}
enum WatchType {
WATCH_TYPE_PARTITION_CHANGE = 0;
WATCH_TYPE_STORE_CHANGE = 1;
}
```
**Response Stream**:
```protobuf
message WatchPartitionResponse {
ResponseHeader header = 1;
WatchChangeType change_type = 2;
Partition partition = 3;
}
enum WatchChangeType {
WATCH_CHANGE_TYPE_ADD = 0;
WATCH_CHANGE_TYPE_DEL = 1;
WATCH_CHANGE_TYPE_ALTER = 2;
}
```
**Java Example**:
```java
import org.apache.hugegraph.pd.grpc.watch.HgPdWatchGrpc;
import org.apache.hugegraph.pd.grpc.watch.HgPdWatch;
HgPdWatchGrpc.HgPdWatchStub watchStub = HgPdWatchGrpc.newStub(channel);
// Watch partition changes
HgPdWatch.WatchPartitionRequest request = HgPdWatch.WatchPartitionRequest.newBuilder()
.setGraphName("social_network")
.setWatchType(HgPdWatch.WatchType.WATCH_TYPE_PARTITION_CHANGE)
.build();
StreamObserver<HgPdWatch.WatchPartitionResponse> responseObserver = new StreamObserver<>() {
@Override
public void onNext(HgPdWatch.WatchPartitionResponse response) {
WatchChangeType changeType = response.getChangeType();
Metapb.Partition partition = response.getPartition();
switch (changeType) {
case WATCH_CHANGE_TYPE_ADD:
System.out.println("Partition added: " + partition.getId());
break;
case WATCH_CHANGE_TYPE_DEL:
System.out.println("Partition deleted: " + partition.getId());
break;
case WATCH_CHANGE_TYPE_ALTER:
System.out.println("Partition changed: " + partition.getId());
// Refresh local cache
break;
}
}
@Override
public void onError(Throwable t) {
System.err.println("Watch error: " + t.getMessage());
}
@Override
public void onCompleted() {
System.out.println("Watch completed");
}
};
watchStub.watchPartition(request, responseObserver);
```
## Java Client Library
HugeGraph PD provides a high-level Java client library (`hg-pd-client`) that simplifies interaction with PD.
### PDClient
Main client class for PD operations.
**Initialization**:
```java
import org.apache.hugegraph.pd.client.PDClient;
import org.apache.hugegraph.pd.client.PDConfig;
// Configure PD client
PDConfig config = PDConfig.builder()
.pdServers("192.168.1.10:8686,192.168.1.11:8686,192.168.1.12:8686")
.connectTimeout(5000) // 5 seconds
.requestTimeout(10000) // 10 seconds
.enableCache(true) // Enable partition cache
.build();
// Create client
PDClient client = new PDClient(config);
// Use client...
// Clean up
client.close();
```
### Partition Operations
```java
import org.apache.hugegraph.pd.common.PartitionEngine;
// Get partition by key
String graphName = "social_network";
long vertexId = 12345L;
long partitionKey = PartitionEngine.calcHashcode(vertexId);
Metapb.Partition partition = client.getPartitionByKey(graphName, partitionKey);
System.out.println("Partition ID: " + partition.getId());
// Get all partitions for a graph
List<Metapb.Partition> partitions = client.getPartitionsByGraphName(graphName);
System.out.println("Total partitions: " + partitions.size());
// Get partition leader
Metapb.Shard leader = client.getPartitionLeader(graphName, partition.getId());
Metapb.Store leaderStore = client.getStore(leader.getStoreId());
System.out.println("Leader at: " + leaderStore.getAddress());
```
### Store Operations
```java
// Get all stores
List<Metapb.Store> stores = client.getStores();
for (Metapb.Store store : stores) {
System.out.println("Store " + store.getId() + ": " + store.getAddress() +
" (" + store.getState() + ")");
}
// Get active stores
List<Metapb.Store> activeStores = client.getActiveStores();
System.out.println("Active stores: " + activeStores.size());
// Get stores by graph
List<Metapb.Store> graphStores = client.getStoresByGraphName(graphName);
```
### Watch Operations
```java
import org.apache.hugegraph.pd.client.PDWatch;
// Create watch listener
PDWatch.Listener<Metapb.Partition> listener = new PDWatch.Listener<>() {
@Override
public void onNext(PDWatch.WatchEvent<Metapb.Partition> event) {
System.out.println("Partition " + event.getTarget().getId() +
" " + event.getType());
}
@Override
public void onError(Throwable error) {
System.err.println("Watch error: " + error.getMessage());
}
};
// Watch partition changes
PDWatch watch = client.watchPartition(graphName, listener);
// Stop watching
watch.close();
```
### KV Operations
```java
// Put key-value
client.put("config/setting1", "value1".getBytes());
// Get value
byte[] value = client.get("config/setting1");
System.out.println("Value: " + new String(value));
// Delete key
client.delete("config/setting1");
// Scan with prefix
Map<String, byte[]> results = client.scan("config/", "config/\uffff", 100);
for (Map.Entry<String, byte[]> entry : results.entrySet()) {
System.out.println(entry.getKey() + " = " + new String(entry.getValue()));
}
```
## REST API
PD exposes a REST API for management and monitoring (default port: 8620).
### Health Check
```bash
curl http://localhost:8620/actuator/health
```
**Response**:
```json
{
"status": "UP",
"groups": ["liveness", "readiness"]
}
```
### Metrics
```bash
curl http://localhost:8620/actuator/metrics
```
**Response** (Prometheus format):
```
# HELP pd_raft_state Raft state (0=Follower, 1=Candidate, 2=Leader)
# TYPE pd_raft_state gauge
pd_raft_state 2.0
# HELP pd_store_count Number of stores
# TYPE pd_store_count gauge
pd_store_count{state="Up"} 3.0
pd_store_count{state="Offline"} 0.0
# HELP pd_partition_count Number of partitions
# TYPE pd_partition_count gauge
pd_partition_count 36.0
```
### Partition API
#### List Partitions
```bash
curl http://localhost:8620/v1/partitions?graph_name=social_network
```
**Response**:
```json
{
"partitions": [
{
"id": 1,
"graph_name": "social_network",
"start_key": 0,
"end_key": 1000,
"shards": [
{"store_id": 1, "role": "Leader"},
{"store_id": 2, "role": "Follower"},
{"store_id": 3, "role": "Follower"}
],
"state": "PState_Normal"
}
]
}
```
### Store API
#### List Stores
```bash
curl http://localhost:8620/v1/stores
```
**Response**:
```json
{
"stores": [
{
"id": 1,
"address": "192.168.1.100:8500",
"raft_address": "192.168.1.100:8501",
"state": "Up",
"labels": {
"zone": "zone-1",
"rack": "rack-a"
},
"stats": {
"capacity": 107374182400,
"available": 53687091200,
"partition_count": 12,
"leader_count": 8
},
"last_heartbeat": 1620000000
}
]
}
```
## Error Handling
### gRPC Status Codes
PD uses standard gRPC status codes:
| Code | Name | Description |
|------|------|-------------|
| 0 | OK | Success |
| 1 | CANCELLED | Operation cancelled |
| 2 | UNKNOWN | Unknown error |
| 3 | INVALID_ARGUMENT | Invalid request parameters |
| 4 | DEADLINE_EXCEEDED | Timeout |
| 5 | NOT_FOUND | Resource not found (store, partition, etc.) |
| 6 | ALREADY_EXISTS | Resource already exists |
| 7 | PERMISSION_DENIED | Insufficient permissions |
| 8 | RESOURCE_EXHAUSTED | Quota exceeded |
| 14 | UNAVAILABLE | Service unavailable (not leader, Raft not ready) |
### Response Header
All responses include a `ResponseHeader` with error information:
```protobuf
message ResponseHeader {
uint64 cluster_id = 1;
Error error = 2;
}
message Error {
ErrorType type = 1;
string message = 2;
}
enum ErrorType {
OK = 0;
NOT_LEADER = 1; // Current node is not Raft leader
STORE_NOT_FOUND = 2;
PARTITION_NOT_FOUND = 3;
STORE_TOMBSTONE = 4; // Store is permanently removed
RAFT_ERROR = 5;
}
```
**Error Handling Example**:
```java
Pdpb.GetStoreInfoResponse response = stub.getStoreInfo(request);
if (response.getHeader().hasError()) {
Error error = response.getHeader().getError();
if (error.getType() == ErrorType.NOT_LEADER) {
// Retry with leader node
String leaderUrl = getLeaderFromMembers();
// Reconnect and retry...
} else {
System.err.println("Error: " + error.getMessage());
}
} else {
Metapb.Store store = response.getStore();
// Process store...
}
```
## Best Practices
### 1. Connection Management
- **Reuse gRPC channels**: Creating channels is expensive
- **Connection pooling**: Use multiple channels for high throughput
- **Automatic reconnection**: Handle disconnections gracefully
```java
// Good: Reuse channel
ManagedChannel channel = ManagedChannelBuilder
.forAddress("pd-host", 8686)
.usePlaintext()
.keepAliveTime(30, TimeUnit.SECONDS)
.idleTimeout(60, TimeUnit.SECONDS)
.build();
// Bad: Create new channel per request
// ManagedChannel channel = ...
// channel.shutdown() // Don't do this after every request
```
### 2. Leader Detection
- Always check `ResponseHeader.error.type` for `NOT_LEADER`
- Use `GetMembers()` to find current leader
- Cache leader information but refresh on errors
### 3. Partition Caching
- Cache partition routing information locally
- Use `WatchPartition` to invalidate cache on changes
- Set reasonable cache TTL (e.g., 5 minutes)
### 4. Retry Strategy
- Implement exponential backoff for retries
- Retry on transient errors (UNAVAILABLE, DEADLINE_EXCEEDED)
- Don't retry on permanent errors (NOT_FOUND, INVALID_ARGUMENT)
```java
int maxRetries = 3;
int retryDelay = 1000; // milliseconds
for (int i = 0; i < maxRetries; i++) {
try {
response = stub.getPartition(request);
break; // Success
} catch (StatusRuntimeException e) {
if (e.getStatus().getCode() == Status.Code.UNAVAILABLE && i < maxRetries - 1) {
Thread.sleep(retryDelay * (1 << i)); // Exponential backoff
} else {
throw e;
}
}
}
```
### 5. Timeout Configuration
- Set appropriate timeouts for all RPCs
- Use shorter timeouts for read operations
- Use longer timeouts for write operations
```java
PDGrpc.PDBlockingStub stub = PDGrpc.newBlockingStub(channel)
.withDeadlineAfter(5, TimeUnit.SECONDS);
```
## Summary
HugeGraph PD provides comprehensive gRPC APIs for:
- Cluster membership and leadership management
- Store registration and monitoring
- Partition routing and querying
- Distributed key-value operations
- Real-time change notifications
Use the high-level `PDClient` library for simplified integration, or use raw gRPC stubs for fine-grained control.
For architecture details, see [Architecture Documentation](architecture.md).
For configuration, see [Configuration Guide](configuration.md).