mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-21 06:36:54 +00:00
feat: Phase 10 — control-plane closure (identity, convergence, idempotence)
Stable identity on wire: - ServerID fields in proto (replica_server_id, server_id on ReplicaAddrMessage) - volumeServerId wired through volume.go → BlockService.SetServerID - Identity derived from canonical server ID, not transport addresses Assignment convergence: - V2 idempotence via lastAppliedAssignment.equals (full replica set comparison) - setupPrimaryReplication/Multi idempotence guards - ProcessAssignments with V2 + V1 dual-path assignment handling Master-driven control loop: - RecoveryManager: serialized cancel-and-drain via done channels - Per-replica heartbeat state reporting (ReplicaShipperStatus) - masterServerBackend: VolumeBackend calling real MasterServer in-process - RestoreBlockSnapshot RPC (master + volume server proto) QA tests (P10 P1-P4): - Identity: ServerID on wire, fail-closed on missing - Convergence: assignment delivery, epoch monotonicity, registry coherence - Idempotence: repeated assignment, multi-replica set comparison - Control loop: integrationMaster + real allocator + proto round-trip Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
c7eb87c587
commit
3ec8fab2f1
@@ -0,0 +1,109 @@
|
||||
# Phase 10 Decisions
|
||||
|
||||
## Decision 1: Phase 10 is control-plane closure, not backend execution rework
|
||||
|
||||
`Phase 09` already closed the main backend execution gaps on the chosen path.
|
||||
|
||||
`Phase 10` should therefore focus on:
|
||||
|
||||
1. real control delivery
|
||||
2. reassignment / result convergence
|
||||
3. identity cleanup
|
||||
|
||||
It should not reopen accepted backend execution semantics unless a true control-plane bug forces a narrow correction.
|
||||
|
||||
## Decision 2: Phase 10 remains bounded to the chosen path
|
||||
|
||||
Default scope remains:
|
||||
|
||||
1. `RF=2`
|
||||
2. `sync_all`
|
||||
3. existing master / volume-server heartbeat path
|
||||
|
||||
Future durability modes or wider topology support should not be absorbed casually into this phase.
|
||||
|
||||
## Decision 3: Identity cleanup belongs to control-plane closure
|
||||
|
||||
The current local server identity remains transport-shaped (`listenAddr`).
|
||||
|
||||
`Phase 10` is the right place to strengthen this because identity coherence affects:
|
||||
|
||||
1. assignment truth
|
||||
2. sender/replica identity continuity
|
||||
3. end-to-end control-path correctness
|
||||
|
||||
## Decision 4: Rebuild-server idempotence cleanup is bounded residual work, not the phase itself
|
||||
|
||||
The repeated-primary-assignment warning around rebuild-server start is a valid residual note.
|
||||
|
||||
It may be addressed in `Phase 10` only if:
|
||||
|
||||
1. it is directly relevant to real control/runtime ownership or assignment idempotence
|
||||
2. it stays bounded
|
||||
|
||||
It must not turn `Phase 10` into a broad runtime polish phase.
|
||||
|
||||
## Decision 5: P1 identity and control-truth closure accepted
|
||||
|
||||
`P1` closes the stable-identity/control-truth gap on the chosen block assignment wire.
|
||||
|
||||
Accepted properties:
|
||||
|
||||
1. stable server identity is now carried additively on the block assignment proto wire:
|
||||
- scalar `replica_server_id`
|
||||
- per-replica `server_id`
|
||||
2. generated protobuf output, not hand-maintained output, is now the accepted basis for the wire shape
|
||||
3. master create-path and chosen failover/primary-refresh assignment generation now preserve stable identity on the chosen path
|
||||
4. volume-server block/control path now uses the same canonical `volumeServerId` as the main volume server
|
||||
5. `ControlBridge` continues to fail closed when stable identity is missing
|
||||
|
||||
Evidence closure:
|
||||
|
||||
1. proto/decode proof now covers stable identity round-trip
|
||||
2. real ingress proof now covers:
|
||||
- proto assignment
|
||||
- decode
|
||||
- `ProcessAssignments()`
|
||||
- `ControlBridge`
|
||||
- engine sender `ReplicaID`
|
||||
3. canonical local identity proof now covers non-default local ID
|
||||
4. missing-ID fail-closed proof is covered directly
|
||||
|
||||
## Decision 6: P2 reassignment/result convergence accepted under the chosen-path volume-server ingress bound
|
||||
|
||||
`P2` closes the main reassignment/result-convergence gap on the chosen path without reopening accepted backend execution semantics.
|
||||
|
||||
Accepted properties:
|
||||
|
||||
1. reassignment through the accepted chosen-path ingress now proves old sender truth is removed and new sender truth is created
|
||||
2. stale runtime ownership is now proved as a live drain case, not only a bookkeeping absence case
|
||||
3. reported truth is now checked through `CollectBlockVolumeHeartbeat()`, the same reporting surface used by the live heartbeat loop
|
||||
4. the accepted no-split-truth claim is bounded to:
|
||||
- engine sender truth
|
||||
- stale-runtime residue removed
|
||||
- heartbeat output truth
|
||||
5. `P2` does not claim full master-driven failover/gRPC-infrastructure closure beyond the accepted volume-server-side ingress boundary
|
||||
|
||||
Evidence closure:
|
||||
|
||||
1. real reassignment proof covers `vs2 -> vs3` sender replacement on the chosen path
|
||||
2. stale-owner proof now blocks a live old goroutine and verifies drain during reassignment
|
||||
3. heartbeat proof now checks actual heartbeat output rather than local helper state
|
||||
4. delivery wording is bounded so it does not overclaim a proved live replacement owner in the no-split-truth test
|
||||
|
||||
## Decision 7: P3 bounded repeated-assignment/idempotence cleanup accepted on the chosen path
|
||||
|
||||
`P3` closes the bounded repeated-assignment residual left after accepted `P2`.
|
||||
|
||||
Accepted properties:
|
||||
|
||||
1. repeated unchanged chosen-path assignment is now skipped before duplicate V2 orchestrator/recovery work is started
|
||||
2. the corresponding V1 primary-replication setup path is also absorbed idempotently for unchanged truth
|
||||
3. changed chosen-path assignment still takes the accepted replacement/update path rather than being suppressed incorrectly
|
||||
4. `P3` remains bounded cleanup and does not claim general multi-replica idempotence or broad production hardening
|
||||
|
||||
Evidence closure:
|
||||
|
||||
1. repeated-assignment proof now checks stable V2 event count rather than only stable helper/reporting state
|
||||
2. changed-assignment guard proof keeps accepted replacement behavior intact
|
||||
3. externally visible heartbeat state remains coherent after repeated unchanged assignment
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,228 @@
|
||||
# Phase 10
|
||||
|
||||
Date: 2026-04-02
|
||||
Status: complete
|
||||
Purpose: close the main end-to-end control-plane gaps on the chosen `RF=2 sync_all` path without reopening accepted backend execution semantics
|
||||
|
||||
## Why This Phase Exists
|
||||
|
||||
`Phase 09` closed the main backend execution gaps on the chosen path:
|
||||
|
||||
1. real `TransferFullBase`
|
||||
2. real `TransferSnapshot`
|
||||
3. real `TruncateWAL` under the accepted narrowed contract
|
||||
4. stronger live runtime ownership on the volume-server path
|
||||
|
||||
What still does not exist is stronger end-to-end control-plane closure.
|
||||
|
||||
The main remaining gap is no longer:
|
||||
|
||||
1. whether the backend execution path is real
|
||||
|
||||
It is now:
|
||||
|
||||
1. whether the real control path drives and reflects the chosen path coherently enough for product use
|
||||
|
||||
## Phase Goal
|
||||
|
||||
Strengthen from accepted assignment-entry closure to stronger end-to-end control-plane closure on the chosen path.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
|
||||
1. heartbeat / gRPC-level control delivery proof on the chosen path
|
||||
2. reassignment / failover result convergence through the real control path
|
||||
3. cleaner local identity than transport-shaped `listenAddr`
|
||||
4. bounded idempotence / repeated-assignment cleanup when it directly affects live control/runtime ownership
|
||||
|
||||
### Out of scope
|
||||
|
||||
1. reopening accepted `P1` / `P2` / `P3` / `P4` backend execution semantics
|
||||
2. `RF>2`
|
||||
3. `best_effort` / `sync_quorum`
|
||||
4. product-surface rebinding (`CSI` / `NVMe` / `iSCSI`)
|
||||
5. broad performance optimization
|
||||
|
||||
## Phase 10 Items
|
||||
|
||||
### P0: Control-Plane Closure Plan
|
||||
|
||||
Goal:
|
||||
|
||||
- start `Phase 10` with one substantial control-plane closure package, not a loose collection of follow-up fixes
|
||||
|
||||
Must prove:
|
||||
|
||||
1. the phase is centered on real control-path closure rather than backend execution rework
|
||||
2. the required closure targets are explicit:
|
||||
- heartbeat / gRPC delivery
|
||||
- reassignment / result convergence
|
||||
- identity cleanup
|
||||
- bounded repeated-assignment/idempotence cleanup
|
||||
3. the chosen-path bound remains explicit
|
||||
|
||||
Verification mechanism:
|
||||
|
||||
1. architect review:
|
||||
- control-plane scope is explicit and bounded
|
||||
- proposed slices do not reopen accepted backend execution semantics
|
||||
2. tester review:
|
||||
- required end-to-end proofs are explicit
|
||||
3. manager review:
|
||||
- the package is concrete enough to assign the first implementation slice
|
||||
|
||||
Output artifacts:
|
||||
|
||||
1. explicit control-plane closure targets
|
||||
2. explicit reject shapes
|
||||
3. initial slice order inside `Phase 10`
|
||||
|
||||
Execution note:
|
||||
|
||||
- use `phase-10-log.md` as the technical pack for:
|
||||
- semantic scope
|
||||
- execution scope
|
||||
- proof shapes
|
||||
- assignment templates for `sw` and `tester`
|
||||
|
||||
Reject if:
|
||||
|
||||
1. `Phase 10` is framed as a vague "polish/control" phase without concrete closure targets
|
||||
2. accepted `Phase 09` execution semantics are quietly reopened
|
||||
3. product surfaces or unrelated hardening work are absorbed into this phase
|
||||
4. no explicit end-to-end proof shape is defined
|
||||
|
||||
Status:
|
||||
|
||||
- accepted
|
||||
|
||||
### P1: Identity And Control-Truth Closure
|
||||
|
||||
Goal:
|
||||
|
||||
- close stable identity on the real chosen-path control wire so assignment truth, local ingest truth, and `ReplicaID` construction no longer depend on transport-shaped fallback
|
||||
|
||||
Accepted scope:
|
||||
|
||||
1. stable server identity preserved on the block assignment proto wire
|
||||
2. master assignment generation preserves stable identity on the chosen path
|
||||
3. volume-server local identity uses the same canonical server identity as the main volume server
|
||||
4. real ingress proof:
|
||||
- proto/decode
|
||||
- `ProcessAssignments()`
|
||||
- `ControlBridge`
|
||||
- engine sender identity
|
||||
5. fail-closed behavior for missing stable identity
|
||||
|
||||
Status:
|
||||
|
||||
- accepted
|
||||
|
||||
Carry-forward from `P1`:
|
||||
|
||||
1. fuller reassignment / failover result convergence is still open
|
||||
2. broader control-plane reporting closure is still open
|
||||
|
||||
### P2: Reassignment / Result Convergence
|
||||
|
||||
Goal:
|
||||
|
||||
- prove that reassignment and failover converge through the real control path without stale local ownership or stale reported truth lingering after control truth changes
|
||||
|
||||
Accepted scope:
|
||||
|
||||
1. real failover / reassignment convergence through the chosen control path
|
||||
2. no stale local runtime owner after control truth changes
|
||||
3. no stale control/reporting truth after reassignment
|
||||
4. one-chain proof through the real control path, not only local helper logic
|
||||
5. no overclaim of broader hardening or product-surface closure
|
||||
|
||||
Status:
|
||||
|
||||
- accepted
|
||||
|
||||
Carry-forward from `P2`:
|
||||
|
||||
1. `P2` proves stale owner removal and no stale residue after control truth changes
|
||||
2. bounded repeated-assignment/idempotence cleanup is still open where repeated primary assignment can still emit rebuild-server relisten warnings
|
||||
3. `P2` does not claim broad master-driven failover infrastructure closure beyond the accepted volume-server-side chosen-path ingress
|
||||
|
||||
### P3: Bounded Repeated-Assignment / Idempotence Cleanup
|
||||
|
||||
Goal:
|
||||
|
||||
- close the remaining low-severity repeated-assignment/runtime-idempotence gap on the chosen path so duplicate or replacement primary assignments do not leave avoidable relisten/restart noise or ambiguous live-control ownership
|
||||
|
||||
Accepted scope:
|
||||
|
||||
1. repeated primary assignment on the same chosen-path volume should converge idempotently
|
||||
2. rebuild-server/runtime side effects should not relaunch noisily when the authoritative control truth is unchanged or already active
|
||||
3. bounded proof that repeated-assignment cleanup does not reopen accepted `P2` convergence or accepted `Phase 09` execution semantics
|
||||
4. no expansion into broad runtime polish, product surfaces, or unrelated restart hardening
|
||||
|
||||
Status:
|
||||
|
||||
- accepted
|
||||
|
||||
Carry-forward from `P3`:
|
||||
|
||||
1. chosen-path repeated unchanged assignment is now absorbed idempotently across the accepted V2 + V1 live path
|
||||
2. `P3` remains bounded cleanup; it does not itself close the remaining master-driven heartbeat/gRPC control-loop gap
|
||||
3. fuller master-originated control delivery proof is still open
|
||||
|
||||
### P4: Master-Driven Control-Loop Closure
|
||||
|
||||
Goal:
|
||||
|
||||
- close the remaining chosen-path control-plane gap by proving that master-originated assignment truth delivered through the real heartbeat / gRPC control loop reaches the live volume-server path and converges without split truth
|
||||
|
||||
Required scope:
|
||||
|
||||
1. one bounded end-to-end proof from real master-produced chosen-path assignment truth into the live volume-server control path
|
||||
2. proof that the real heartbeat / gRPC delivery path preserves the already accepted identity and convergence properties
|
||||
3. proof that externally visible post-delivery state reflects the same new truth after the real master-driven path runs
|
||||
4. no reopening of accepted `P1` / `P2` / `P3` semantics except for narrow bugs directly exposed by the fuller control-loop proof
|
||||
5. no expansion into product surfaces, `RF>2`, or broad cluster-hardening work
|
||||
|
||||
Status:
|
||||
|
||||
- accepted
|
||||
|
||||
Carry-forward from `P4`:
|
||||
|
||||
1. bounded chosen-path master-driven heartbeat / gRPC control-loop closure is now accepted
|
||||
2. `P4` does not claim full live transport-stream deployment proof or broad product hardening
|
||||
3. the next phase should move to `Phase 11` product-surface rebinding
|
||||
|
||||
### Planned slice direction after `P0`
|
||||
|
||||
1. `P1`:
|
||||
- identity and control-truth closure on the live control path
|
||||
2. `P2`:
|
||||
- reassignment / failover result convergence through the real control path
|
||||
3. `P3`:
|
||||
- bounded idempotence / repeated-assignment cleanup after accepted `P1` / `P2`
|
||||
4. `P4`:
|
||||
- master-driven heartbeat / gRPC control-loop closure on the chosen path
|
||||
|
||||
## Assignment For `sw`
|
||||
|
||||
Current next tasks:
|
||||
|
||||
1. treat `Phase 10` as closed and keep accepted `P1` / `P2` / `P3` / `P4` semantics stable
|
||||
2. start `Phase 11` product-surface rebinding from `v2-phase-development-plan.md`
|
||||
3. keep the first `Phase 11` slice bounded to selected product surfaces rather than broad hardening
|
||||
4. do not reopen accepted backend execution or control-plane closure except for narrow bug fixes
|
||||
|
||||
## Assignment For `tester`
|
||||
|
||||
Current next tasks:
|
||||
|
||||
1. treat `P4` as accepted bounded control-loop closure on the chosen path
|
||||
2. validate the first `Phase 11` slice as bounded product-surface rebinding rather than renewed control-plane work
|
||||
3. keep no-overclaim active around:
|
||||
- accepted `Phase 09` execution closure
|
||||
- accepted `Phase 10` control-plane closure
|
||||
- selected `Phase 11` surface scope vs broader product readiness
|
||||
- chosen path vs future paths/modes
|
||||
@@ -343,6 +343,10 @@ func (v VolumeServerOptions) startVolumeServer(volumeFolders, maxVolumeCounts, v
|
||||
}
|
||||
blockService := weed_server.StartBlockService(*v.blockListen, *v.blockDir, *v.blockIQNPrefix, blockPortal, nvmeCfg)
|
||||
if blockService != nil {
|
||||
// V2 P10-1: Use the same canonical volume server identity as the main
|
||||
// volume server. One identity truth across VS, block service, control
|
||||
// bridge, and runtime ownership.
|
||||
blockService.SetServerID(volumeServerId)
|
||||
volumeServer.SetBlockService(blockService)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ service Seaweed {
|
||||
}
|
||||
rpc ListBlockSnapshots (ListBlockSnapshotsRequest) returns (ListBlockSnapshotsResponse) {
|
||||
}
|
||||
rpc RestoreBlockSnapshot (RestoreBlockSnapshotRequest) returns (RestoreBlockSnapshotResponse) {
|
||||
}
|
||||
rpc ExpandBlockVolume (ExpandBlockVolumeRequest) returns (ExpandBlockVolumeResponse) {
|
||||
}
|
||||
}
|
||||
@@ -528,11 +530,13 @@ message BlockVolumeAssignment {
|
||||
string replica_ctrl_addr = 6;
|
||||
string rebuild_addr = 7;
|
||||
repeated ReplicaAddrMessage replica_addrs = 8;
|
||||
string replica_server_id = 9; // V2: stable server identity for scalar replica
|
||||
}
|
||||
|
||||
message ReplicaAddrMessage {
|
||||
string data_addr = 1;
|
||||
string ctrl_addr = 2;
|
||||
string server_id = 3; // V2: stable server identity for this replica
|
||||
}
|
||||
|
||||
message CreateBlockVolumeRequest {
|
||||
@@ -605,6 +609,13 @@ message BlockSnapshotInfo {
|
||||
uint64 volume_size_bytes = 3;
|
||||
}
|
||||
|
||||
message RestoreBlockSnapshotRequest {
|
||||
string volume_name = 1;
|
||||
uint32 snapshot_id = 2;
|
||||
}
|
||||
message RestoreBlockSnapshotResponse {
|
||||
}
|
||||
|
||||
message ExpandBlockVolumeRequest {
|
||||
string name = 1;
|
||||
uint64 new_size_bytes = 2;
|
||||
|
||||
+206
-85
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v5.29.5
|
||||
// protoc v3.21.12
|
||||
// source: master.proto
|
||||
|
||||
package master_pb
|
||||
@@ -1773,7 +1773,9 @@ func (x *StatisticsResponse) GetFileCount() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
//
|
||||
// collection related
|
||||
//
|
||||
type Collection struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
@@ -1994,7 +1996,9 @@ func (*CollectionDeleteResponse) Descriptor() ([]byte, []int) {
|
||||
return file_master_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
//
|
||||
// volume related
|
||||
//
|
||||
type DiskInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
@@ -4149,6 +4153,7 @@ type BlockVolumeAssignment struct {
|
||||
ReplicaCtrlAddr string `protobuf:"bytes,6,opt,name=replica_ctrl_addr,json=replicaCtrlAddr,proto3" json:"replica_ctrl_addr,omitempty"`
|
||||
RebuildAddr string `protobuf:"bytes,7,opt,name=rebuild_addr,json=rebuildAddr,proto3" json:"rebuild_addr,omitempty"`
|
||||
ReplicaAddrs []*ReplicaAddrMessage `protobuf:"bytes,8,rep,name=replica_addrs,json=replicaAddrs,proto3" json:"replica_addrs,omitempty"`
|
||||
ReplicaServerId string `protobuf:"bytes,9,opt,name=replica_server_id,json=replicaServerId,proto3" json:"replica_server_id,omitempty"` // V2: stable server identity for scalar replica
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -4239,10 +4244,18 @@ func (x *BlockVolumeAssignment) GetReplicaAddrs() []*ReplicaAddrMessage {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *BlockVolumeAssignment) GetReplicaServerId() string {
|
||||
if x != nil {
|
||||
return x.ReplicaServerId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ReplicaAddrMessage struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
DataAddr string `protobuf:"bytes,1,opt,name=data_addr,json=dataAddr,proto3" json:"data_addr,omitempty"`
|
||||
CtrlAddr string `protobuf:"bytes,2,opt,name=ctrl_addr,json=ctrlAddr,proto3" json:"ctrl_addr,omitempty"`
|
||||
ServerId string `protobuf:"bytes,3,opt,name=server_id,json=serverId,proto3" json:"server_id,omitempty"` // V2: stable server identity for this replica
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -4291,6 +4304,13 @@ func (x *ReplicaAddrMessage) GetCtrlAddr() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ReplicaAddrMessage) GetServerId() string {
|
||||
if x != nil {
|
||||
return x.ServerId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CreateBlockVolumeRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
@@ -5063,6 +5083,94 @@ func (x *BlockSnapshotInfo) GetVolumeSizeBytes() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type RestoreBlockSnapshotRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
VolumeName string `protobuf:"bytes,1,opt,name=volume_name,json=volumeName,proto3" json:"volume_name,omitempty"`
|
||||
SnapshotId uint32 `protobuf:"varint,2,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) Reset() {
|
||||
*x = RestoreBlockSnapshotRequest{}
|
||||
mi := &file_master_proto_msgTypes[78]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RestoreBlockSnapshotRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[78]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RestoreBlockSnapshotRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RestoreBlockSnapshotRequest) Descriptor() ([]byte, []int) {
|
||||
return file_master_proto_rawDescGZIP(), []int{78}
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) GetVolumeName() string {
|
||||
if x != nil {
|
||||
return x.VolumeName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) GetSnapshotId() uint32 {
|
||||
if x != nil {
|
||||
return x.SnapshotId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RestoreBlockSnapshotResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotResponse) Reset() {
|
||||
*x = RestoreBlockSnapshotResponse{}
|
||||
mi := &file_master_proto_msgTypes[79]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RestoreBlockSnapshotResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RestoreBlockSnapshotResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[79]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RestoreBlockSnapshotResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RestoreBlockSnapshotResponse) Descriptor() ([]byte, []int) {
|
||||
return file_master_proto_rawDescGZIP(), []int{79}
|
||||
}
|
||||
|
||||
type ExpandBlockVolumeRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
@@ -5073,7 +5181,7 @@ type ExpandBlockVolumeRequest struct {
|
||||
|
||||
func (x *ExpandBlockVolumeRequest) Reset() {
|
||||
*x = ExpandBlockVolumeRequest{}
|
||||
mi := &file_master_proto_msgTypes[78]
|
||||
mi := &file_master_proto_msgTypes[80]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5085,7 +5193,7 @@ func (x *ExpandBlockVolumeRequest) String() string {
|
||||
func (*ExpandBlockVolumeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[78]
|
||||
mi := &file_master_proto_msgTypes[80]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5098,7 +5206,7 @@ func (x *ExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ExpandBlockVolumeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ExpandBlockVolumeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_master_proto_rawDescGZIP(), []int{78}
|
||||
return file_master_proto_rawDescGZIP(), []int{80}
|
||||
}
|
||||
|
||||
func (x *ExpandBlockVolumeRequest) GetName() string {
|
||||
@@ -5124,7 +5232,7 @@ type ExpandBlockVolumeResponse struct {
|
||||
|
||||
func (x *ExpandBlockVolumeResponse) Reset() {
|
||||
*x = ExpandBlockVolumeResponse{}
|
||||
mi := &file_master_proto_msgTypes[79]
|
||||
mi := &file_master_proto_msgTypes[81]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5136,7 +5244,7 @@ func (x *ExpandBlockVolumeResponse) String() string {
|
||||
func (*ExpandBlockVolumeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[79]
|
||||
mi := &file_master_proto_msgTypes[81]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5149,7 +5257,7 @@ func (x *ExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ExpandBlockVolumeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ExpandBlockVolumeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_master_proto_rawDescGZIP(), []int{79}
|
||||
return file_master_proto_rawDescGZIP(), []int{81}
|
||||
}
|
||||
|
||||
func (x *ExpandBlockVolumeResponse) GetCapacityBytes() uint64 {
|
||||
@@ -5170,7 +5278,7 @@ type SuperBlockExtra_ErasureCoding struct {
|
||||
|
||||
func (x *SuperBlockExtra_ErasureCoding) Reset() {
|
||||
*x = SuperBlockExtra_ErasureCoding{}
|
||||
mi := &file_master_proto_msgTypes[82]
|
||||
mi := &file_master_proto_msgTypes[84]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5182,7 +5290,7 @@ func (x *SuperBlockExtra_ErasureCoding) String() string {
|
||||
func (*SuperBlockExtra_ErasureCoding) ProtoMessage() {}
|
||||
|
||||
func (x *SuperBlockExtra_ErasureCoding) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[82]
|
||||
mi := &file_master_proto_msgTypes[84]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5231,7 +5339,7 @@ type LookupVolumeResponse_VolumeIdLocation struct {
|
||||
|
||||
func (x *LookupVolumeResponse_VolumeIdLocation) Reset() {
|
||||
*x = LookupVolumeResponse_VolumeIdLocation{}
|
||||
mi := &file_master_proto_msgTypes[83]
|
||||
mi := &file_master_proto_msgTypes[85]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5243,7 +5351,7 @@ func (x *LookupVolumeResponse_VolumeIdLocation) String() string {
|
||||
func (*LookupVolumeResponse_VolumeIdLocation) ProtoMessage() {}
|
||||
|
||||
func (x *LookupVolumeResponse_VolumeIdLocation) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[83]
|
||||
mi := &file_master_proto_msgTypes[85]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5297,7 +5405,7 @@ type LookupEcVolumeResponse_EcShardIdLocation struct {
|
||||
|
||||
func (x *LookupEcVolumeResponse_EcShardIdLocation) Reset() {
|
||||
*x = LookupEcVolumeResponse_EcShardIdLocation{}
|
||||
mi := &file_master_proto_msgTypes[88]
|
||||
mi := &file_master_proto_msgTypes[90]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5309,7 +5417,7 @@ func (x *LookupEcVolumeResponse_EcShardIdLocation) String() string {
|
||||
func (*LookupEcVolumeResponse_EcShardIdLocation) ProtoMessage() {}
|
||||
|
||||
func (x *LookupEcVolumeResponse_EcShardIdLocation) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[88]
|
||||
mi := &file_master_proto_msgTypes[90]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5352,7 +5460,7 @@ type ListClusterNodesResponse_ClusterNode struct {
|
||||
|
||||
func (x *ListClusterNodesResponse_ClusterNode) Reset() {
|
||||
*x = ListClusterNodesResponse_ClusterNode{}
|
||||
mi := &file_master_proto_msgTypes[89]
|
||||
mi := &file_master_proto_msgTypes[91]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5364,7 +5472,7 @@ func (x *ListClusterNodesResponse_ClusterNode) String() string {
|
||||
func (*ListClusterNodesResponse_ClusterNode) ProtoMessage() {}
|
||||
|
||||
func (x *ListClusterNodesResponse_ClusterNode) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[89]
|
||||
mi := &file_master_proto_msgTypes[91]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5427,7 +5535,7 @@ type RaftListClusterServersResponse_ClusterServers struct {
|
||||
|
||||
func (x *RaftListClusterServersResponse_ClusterServers) Reset() {
|
||||
*x = RaftListClusterServersResponse_ClusterServers{}
|
||||
mi := &file_master_proto_msgTypes[90]
|
||||
mi := &file_master_proto_msgTypes[92]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -5439,7 +5547,7 @@ func (x *RaftListClusterServersResponse_ClusterServers) String() string {
|
||||
func (*RaftListClusterServersResponse_ClusterServers) ProtoMessage() {}
|
||||
|
||||
func (x *RaftListClusterServersResponse_ClusterServers) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_master_proto_msgTypes[90]
|
||||
mi := &file_master_proto_msgTypes[92]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -5885,7 +5993,7 @@ const file_master_proto_rawDesc = "" +
|
||||
"volumeSize\x12\x1d\n" +
|
||||
"\n" +
|
||||
"block_size\x18\x03 \x01(\rR\tblockSize\x12\x1b\n" +
|
||||
"\tdisk_type\x18\x04 \x01(\tR\bdiskType\"\xb6\x02\n" +
|
||||
"\tdisk_type\x18\x04 \x01(\tR\bdiskType\"\xe2\x02\n" +
|
||||
"\x15BlockVolumeAssignment\x12\x12\n" +
|
||||
"\x04path\x18\x01 \x01(\tR\x04path\x12\x14\n" +
|
||||
"\x05epoch\x18\x02 \x01(\x04R\x05epoch\x12\x12\n" +
|
||||
@@ -5895,10 +6003,12 @@ const file_master_proto_rawDesc = "" +
|
||||
"\x11replica_data_addr\x18\x05 \x01(\tR\x0freplicaDataAddr\x12*\n" +
|
||||
"\x11replica_ctrl_addr\x18\x06 \x01(\tR\x0freplicaCtrlAddr\x12!\n" +
|
||||
"\frebuild_addr\x18\a \x01(\tR\vrebuildAddr\x12B\n" +
|
||||
"\rreplica_addrs\x18\b \x03(\v2\x1d.master_pb.ReplicaAddrMessageR\freplicaAddrs\"N\n" +
|
||||
"\rreplica_addrs\x18\b \x03(\v2\x1d.master_pb.ReplicaAddrMessageR\freplicaAddrs\x12*\n" +
|
||||
"\x11replica_server_id\x18\t \x01(\tR\x0freplicaServerId\"k\n" +
|
||||
"\x12ReplicaAddrMessage\x12\x1b\n" +
|
||||
"\tdata_addr\x18\x01 \x01(\tR\bdataAddr\x12\x1b\n" +
|
||||
"\tctrl_addr\x18\x02 \x01(\tR\bctrlAddr\"\xba\x01\n" +
|
||||
"\tctrl_addr\x18\x02 \x01(\tR\bctrlAddr\x12\x1b\n" +
|
||||
"\tserver_id\x18\x03 \x01(\tR\bserverId\"\xba\x01\n" +
|
||||
"\x18CreateBlockVolumeRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" +
|
||||
"\n" +
|
||||
@@ -5963,12 +6073,18 @@ const file_master_proto_rawDesc = "" +
|
||||
"snapshotId\x12\x1d\n" +
|
||||
"\n" +
|
||||
"created_at\x18\x02 \x01(\x03R\tcreatedAt\x12*\n" +
|
||||
"\x11volume_size_bytes\x18\x03 \x01(\x04R\x0fvolumeSizeBytes\"T\n" +
|
||||
"\x11volume_size_bytes\x18\x03 \x01(\x04R\x0fvolumeSizeBytes\"_\n" +
|
||||
"\x1bRestoreBlockSnapshotRequest\x12\x1f\n" +
|
||||
"\vvolume_name\x18\x01 \x01(\tR\n" +
|
||||
"volumeName\x12\x1f\n" +
|
||||
"\vsnapshot_id\x18\x02 \x01(\rR\n" +
|
||||
"snapshotId\"\x1e\n" +
|
||||
"\x1cRestoreBlockSnapshotResponse\"T\n" +
|
||||
"\x18ExpandBlockVolumeRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12$\n" +
|
||||
"\x0enew_size_bytes\x18\x02 \x01(\x04R\fnewSizeBytes\"B\n" +
|
||||
"\x19ExpandBlockVolumeResponse\x12%\n" +
|
||||
"\x0ecapacity_bytes\x18\x01 \x01(\x04R\rcapacityBytes2\x83\x16\n" +
|
||||
"\x0ecapacity_bytes\x18\x01 \x01(\x04R\rcapacityBytes2\xee\x16\n" +
|
||||
"\aSeaweed\x12I\n" +
|
||||
"\rSendHeartbeat\x12\x14.master_pb.Heartbeat\x1a\x1c.master_pb.HeartbeatResponse\"\x00(\x010\x01\x12X\n" +
|
||||
"\rKeepConnected\x12\x1f.master_pb.KeepConnectedRequest\x1a .master_pb.KeepConnectedResponse\"\x00(\x010\x01\x12Q\n" +
|
||||
@@ -6002,7 +6118,8 @@ const file_master_proto_rawDesc = "" +
|
||||
"\x11LookupBlockVolume\x12#.master_pb.LookupBlockVolumeRequest\x1a$.master_pb.LookupBlockVolumeResponse\"\x00\x12f\n" +
|
||||
"\x13CreateBlockSnapshot\x12%.master_pb.CreateBlockSnapshotRequest\x1a&.master_pb.CreateBlockSnapshotResponse\"\x00\x12f\n" +
|
||||
"\x13DeleteBlockSnapshot\x12%.master_pb.DeleteBlockSnapshotRequest\x1a&.master_pb.DeleteBlockSnapshotResponse\"\x00\x12c\n" +
|
||||
"\x12ListBlockSnapshots\x12$.master_pb.ListBlockSnapshotsRequest\x1a%.master_pb.ListBlockSnapshotsResponse\"\x00\x12`\n" +
|
||||
"\x12ListBlockSnapshots\x12$.master_pb.ListBlockSnapshotsRequest\x1a%.master_pb.ListBlockSnapshotsResponse\"\x00\x12i\n" +
|
||||
"\x14RestoreBlockSnapshot\x12&.master_pb.RestoreBlockSnapshotRequest\x1a'.master_pb.RestoreBlockSnapshotResponse\"\x00\x12`\n" +
|
||||
"\x11ExpandBlockVolume\x12#.master_pb.ExpandBlockVolumeRequest\x1a$.master_pb.ExpandBlockVolumeResponse\"\x00B2Z0github.com/seaweedfs/seaweedfs/weed/pb/master_pbb\x06proto3"
|
||||
|
||||
var (
|
||||
@@ -6017,7 +6134,7 @@ func file_master_proto_rawDescGZIP() []byte {
|
||||
return file_master_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_master_proto_msgTypes = make([]protoimpl.MessageInfo, 91)
|
||||
var file_master_proto_msgTypes = make([]protoimpl.MessageInfo, 93)
|
||||
var file_master_proto_goTypes = []any{
|
||||
(*Heartbeat)(nil), // 0: master_pb.Heartbeat
|
||||
(*HeartbeatResponse)(nil), // 1: master_pb.HeartbeatResponse
|
||||
@@ -6097,20 +6214,22 @@ var file_master_proto_goTypes = []any{
|
||||
(*ListBlockSnapshotsRequest)(nil), // 75: master_pb.ListBlockSnapshotsRequest
|
||||
(*ListBlockSnapshotsResponse)(nil), // 76: master_pb.ListBlockSnapshotsResponse
|
||||
(*BlockSnapshotInfo)(nil), // 77: master_pb.BlockSnapshotInfo
|
||||
(*ExpandBlockVolumeRequest)(nil), // 78: master_pb.ExpandBlockVolumeRequest
|
||||
(*ExpandBlockVolumeResponse)(nil), // 79: master_pb.ExpandBlockVolumeResponse
|
||||
nil, // 80: master_pb.Heartbeat.MaxVolumeCountsEntry
|
||||
nil, // 81: master_pb.StorageBackend.PropertiesEntry
|
||||
(*SuperBlockExtra_ErasureCoding)(nil), // 82: master_pb.SuperBlockExtra.ErasureCoding
|
||||
(*LookupVolumeResponse_VolumeIdLocation)(nil), // 83: master_pb.LookupVolumeResponse.VolumeIdLocation
|
||||
nil, // 84: master_pb.DataNodeInfo.DiskInfosEntry
|
||||
nil, // 85: master_pb.RackInfo.DiskInfosEntry
|
||||
nil, // 86: master_pb.DataCenterInfo.DiskInfosEntry
|
||||
nil, // 87: master_pb.TopologyInfo.DiskInfosEntry
|
||||
(*LookupEcVolumeResponse_EcShardIdLocation)(nil), // 88: master_pb.LookupEcVolumeResponse.EcShardIdLocation
|
||||
(*ListClusterNodesResponse_ClusterNode)(nil), // 89: master_pb.ListClusterNodesResponse.ClusterNode
|
||||
(*RaftListClusterServersResponse_ClusterServers)(nil), // 90: master_pb.RaftListClusterServersResponse.ClusterServers
|
||||
(*volume_server_pb.VolumeServerState)(nil), // 91: volume_server_pb.VolumeServerState
|
||||
(*RestoreBlockSnapshotRequest)(nil), // 78: master_pb.RestoreBlockSnapshotRequest
|
||||
(*RestoreBlockSnapshotResponse)(nil), // 79: master_pb.RestoreBlockSnapshotResponse
|
||||
(*ExpandBlockVolumeRequest)(nil), // 80: master_pb.ExpandBlockVolumeRequest
|
||||
(*ExpandBlockVolumeResponse)(nil), // 81: master_pb.ExpandBlockVolumeResponse
|
||||
nil, // 82: master_pb.Heartbeat.MaxVolumeCountsEntry
|
||||
nil, // 83: master_pb.StorageBackend.PropertiesEntry
|
||||
(*SuperBlockExtra_ErasureCoding)(nil), // 84: master_pb.SuperBlockExtra.ErasureCoding
|
||||
(*LookupVolumeResponse_VolumeIdLocation)(nil), // 85: master_pb.LookupVolumeResponse.VolumeIdLocation
|
||||
nil, // 86: master_pb.DataNodeInfo.DiskInfosEntry
|
||||
nil, // 87: master_pb.RackInfo.DiskInfosEntry
|
||||
nil, // 88: master_pb.DataCenterInfo.DiskInfosEntry
|
||||
nil, // 89: master_pb.TopologyInfo.DiskInfosEntry
|
||||
(*LookupEcVolumeResponse_EcShardIdLocation)(nil), // 90: master_pb.LookupEcVolumeResponse.EcShardIdLocation
|
||||
(*ListClusterNodesResponse_ClusterNode)(nil), // 91: master_pb.ListClusterNodesResponse.ClusterNode
|
||||
(*RaftListClusterServersResponse_ClusterServers)(nil), // 92: master_pb.RaftListClusterServersResponse.ClusterServers
|
||||
(*volume_server_pb.VolumeServerState)(nil), // 93: volume_server_pb.VolumeServerState
|
||||
}
|
||||
var file_master_proto_depIdxs = []int32{
|
||||
2, // 0: master_pb.Heartbeat.volumes:type_name -> master_pb.VolumeInformationMessage
|
||||
@@ -6119,35 +6238,35 @@ var file_master_proto_depIdxs = []int32{
|
||||
4, // 3: master_pb.Heartbeat.ec_shards:type_name -> master_pb.VolumeEcShardInformationMessage
|
||||
4, // 4: master_pb.Heartbeat.new_ec_shards:type_name -> master_pb.VolumeEcShardInformationMessage
|
||||
4, // 5: master_pb.Heartbeat.deleted_ec_shards:type_name -> master_pb.VolumeEcShardInformationMessage
|
||||
80, // 6: master_pb.Heartbeat.max_volume_counts:type_name -> master_pb.Heartbeat.MaxVolumeCountsEntry
|
||||
91, // 7: master_pb.Heartbeat.state:type_name -> volume_server_pb.VolumeServerState
|
||||
82, // 6: master_pb.Heartbeat.max_volume_counts:type_name -> master_pb.Heartbeat.MaxVolumeCountsEntry
|
||||
93, // 7: master_pb.Heartbeat.state:type_name -> volume_server_pb.VolumeServerState
|
||||
61, // 8: master_pb.Heartbeat.block_volume_infos:type_name -> master_pb.BlockVolumeInfoMessage
|
||||
62, // 9: master_pb.Heartbeat.new_block_volumes:type_name -> master_pb.BlockVolumeShortInfoMessage
|
||||
62, // 10: master_pb.Heartbeat.deleted_block_volumes:type_name -> master_pb.BlockVolumeShortInfoMessage
|
||||
5, // 11: master_pb.HeartbeatResponse.storage_backends:type_name -> master_pb.StorageBackend
|
||||
63, // 12: master_pb.HeartbeatResponse.block_volume_assignments:type_name -> master_pb.BlockVolumeAssignment
|
||||
81, // 13: master_pb.StorageBackend.properties:type_name -> master_pb.StorageBackend.PropertiesEntry
|
||||
82, // 14: master_pb.SuperBlockExtra.erasure_coding:type_name -> master_pb.SuperBlockExtra.ErasureCoding
|
||||
83, // 13: master_pb.StorageBackend.properties:type_name -> master_pb.StorageBackend.PropertiesEntry
|
||||
84, // 14: master_pb.SuperBlockExtra.erasure_coding:type_name -> master_pb.SuperBlockExtra.ErasureCoding
|
||||
9, // 15: master_pb.KeepConnectedResponse.volume_location:type_name -> master_pb.VolumeLocation
|
||||
10, // 16: master_pb.KeepConnectedResponse.cluster_node_update:type_name -> master_pb.ClusterNodeUpdate
|
||||
83, // 17: master_pb.LookupVolumeResponse.volume_id_locations:type_name -> master_pb.LookupVolumeResponse.VolumeIdLocation
|
||||
85, // 17: master_pb.LookupVolumeResponse.volume_id_locations:type_name -> master_pb.LookupVolumeResponse.VolumeIdLocation
|
||||
14, // 18: master_pb.AssignResponse.replicas:type_name -> master_pb.Location
|
||||
14, // 19: master_pb.AssignResponse.location:type_name -> master_pb.Location
|
||||
20, // 20: master_pb.CollectionListResponse.collections:type_name -> master_pb.Collection
|
||||
2, // 21: master_pb.DiskInfo.volume_infos:type_name -> master_pb.VolumeInformationMessage
|
||||
4, // 22: master_pb.DiskInfo.ec_shard_infos:type_name -> master_pb.VolumeEcShardInformationMessage
|
||||
84, // 23: master_pb.DataNodeInfo.diskInfos:type_name -> master_pb.DataNodeInfo.DiskInfosEntry
|
||||
86, // 23: master_pb.DataNodeInfo.diskInfos:type_name -> master_pb.DataNodeInfo.DiskInfosEntry
|
||||
26, // 24: master_pb.RackInfo.data_node_infos:type_name -> master_pb.DataNodeInfo
|
||||
85, // 25: master_pb.RackInfo.diskInfos:type_name -> master_pb.RackInfo.DiskInfosEntry
|
||||
87, // 25: master_pb.RackInfo.diskInfos:type_name -> master_pb.RackInfo.DiskInfosEntry
|
||||
27, // 26: master_pb.DataCenterInfo.rack_infos:type_name -> master_pb.RackInfo
|
||||
86, // 27: master_pb.DataCenterInfo.diskInfos:type_name -> master_pb.DataCenterInfo.DiskInfosEntry
|
||||
88, // 27: master_pb.DataCenterInfo.diskInfos:type_name -> master_pb.DataCenterInfo.DiskInfosEntry
|
||||
28, // 28: master_pb.TopologyInfo.data_center_infos:type_name -> master_pb.DataCenterInfo
|
||||
87, // 29: master_pb.TopologyInfo.diskInfos:type_name -> master_pb.TopologyInfo.DiskInfosEntry
|
||||
89, // 29: master_pb.TopologyInfo.diskInfos:type_name -> master_pb.TopologyInfo.DiskInfosEntry
|
||||
29, // 30: master_pb.VolumeListResponse.topology_info:type_name -> master_pb.TopologyInfo
|
||||
88, // 31: master_pb.LookupEcVolumeResponse.shard_id_locations:type_name -> master_pb.LookupEcVolumeResponse.EcShardIdLocation
|
||||
90, // 31: master_pb.LookupEcVolumeResponse.shard_id_locations:type_name -> master_pb.LookupEcVolumeResponse.EcShardIdLocation
|
||||
5, // 32: master_pb.GetMasterConfigurationResponse.storage_backends:type_name -> master_pb.StorageBackend
|
||||
89, // 33: master_pb.ListClusterNodesResponse.cluster_nodes:type_name -> master_pb.ListClusterNodesResponse.ClusterNode
|
||||
90, // 34: master_pb.RaftListClusterServersResponse.cluster_servers:type_name -> master_pb.RaftListClusterServersResponse.ClusterServers
|
||||
91, // 33: master_pb.ListClusterNodesResponse.cluster_nodes:type_name -> master_pb.ListClusterNodesResponse.ClusterNode
|
||||
92, // 34: master_pb.RaftListClusterServersResponse.cluster_servers:type_name -> master_pb.RaftListClusterServersResponse.ClusterServers
|
||||
64, // 35: master_pb.BlockVolumeAssignment.replica_addrs:type_name -> master_pb.ReplicaAddrMessage
|
||||
77, // 36: master_pb.ListBlockSnapshotsResponse.snapshots:type_name -> master_pb.BlockSnapshotInfo
|
||||
14, // 37: master_pb.LookupVolumeResponse.VolumeIdLocation.locations:type_name -> master_pb.Location
|
||||
@@ -6186,40 +6305,42 @@ var file_master_proto_depIdxs = []int32{
|
||||
71, // 70: master_pb.Seaweed.CreateBlockSnapshot:input_type -> master_pb.CreateBlockSnapshotRequest
|
||||
73, // 71: master_pb.Seaweed.DeleteBlockSnapshot:input_type -> master_pb.DeleteBlockSnapshotRequest
|
||||
75, // 72: master_pb.Seaweed.ListBlockSnapshots:input_type -> master_pb.ListBlockSnapshotsRequest
|
||||
78, // 73: master_pb.Seaweed.ExpandBlockVolume:input_type -> master_pb.ExpandBlockVolumeRequest
|
||||
1, // 74: master_pb.Seaweed.SendHeartbeat:output_type -> master_pb.HeartbeatResponse
|
||||
11, // 75: master_pb.Seaweed.KeepConnected:output_type -> master_pb.KeepConnectedResponse
|
||||
13, // 76: master_pb.Seaweed.LookupVolume:output_type -> master_pb.LookupVolumeResponse
|
||||
17, // 77: master_pb.Seaweed.Assign:output_type -> master_pb.AssignResponse
|
||||
17, // 78: master_pb.Seaweed.StreamAssign:output_type -> master_pb.AssignResponse
|
||||
19, // 79: master_pb.Seaweed.Statistics:output_type -> master_pb.StatisticsResponse
|
||||
22, // 80: master_pb.Seaweed.CollectionList:output_type -> master_pb.CollectionListResponse
|
||||
24, // 81: master_pb.Seaweed.CollectionDelete:output_type -> master_pb.CollectionDeleteResponse
|
||||
31, // 82: master_pb.Seaweed.VolumeList:output_type -> master_pb.VolumeListResponse
|
||||
33, // 83: master_pb.Seaweed.LookupEcVolume:output_type -> master_pb.LookupEcVolumeResponse
|
||||
35, // 84: master_pb.Seaweed.VacuumVolume:output_type -> master_pb.VacuumVolumeResponse
|
||||
37, // 85: master_pb.Seaweed.DisableVacuum:output_type -> master_pb.DisableVacuumResponse
|
||||
39, // 86: master_pb.Seaweed.EnableVacuum:output_type -> master_pb.EnableVacuumResponse
|
||||
41, // 87: master_pb.Seaweed.VolumeMarkReadonly:output_type -> master_pb.VolumeMarkReadonlyResponse
|
||||
43, // 88: master_pb.Seaweed.GetMasterConfiguration:output_type -> master_pb.GetMasterConfigurationResponse
|
||||
45, // 89: master_pb.Seaweed.ListClusterNodes:output_type -> master_pb.ListClusterNodesResponse
|
||||
47, // 90: master_pb.Seaweed.LeaseAdminToken:output_type -> master_pb.LeaseAdminTokenResponse
|
||||
49, // 91: master_pb.Seaweed.ReleaseAdminToken:output_type -> master_pb.ReleaseAdminTokenResponse
|
||||
51, // 92: master_pb.Seaweed.Ping:output_type -> master_pb.PingResponse
|
||||
57, // 93: master_pb.Seaweed.RaftListClusterServers:output_type -> master_pb.RaftListClusterServersResponse
|
||||
53, // 94: master_pb.Seaweed.RaftAddServer:output_type -> master_pb.RaftAddServerResponse
|
||||
55, // 95: master_pb.Seaweed.RaftRemoveServer:output_type -> master_pb.RaftRemoveServerResponse
|
||||
59, // 96: master_pb.Seaweed.RaftLeadershipTransfer:output_type -> master_pb.RaftLeadershipTransferResponse
|
||||
60, // 97: master_pb.Seaweed.VolumeGrow:output_type -> master_pb.VolumeGrowResponse
|
||||
66, // 98: master_pb.Seaweed.CreateBlockVolume:output_type -> master_pb.CreateBlockVolumeResponse
|
||||
68, // 99: master_pb.Seaweed.DeleteBlockVolume:output_type -> master_pb.DeleteBlockVolumeResponse
|
||||
70, // 100: master_pb.Seaweed.LookupBlockVolume:output_type -> master_pb.LookupBlockVolumeResponse
|
||||
72, // 101: master_pb.Seaweed.CreateBlockSnapshot:output_type -> master_pb.CreateBlockSnapshotResponse
|
||||
74, // 102: master_pb.Seaweed.DeleteBlockSnapshot:output_type -> master_pb.DeleteBlockSnapshotResponse
|
||||
76, // 103: master_pb.Seaweed.ListBlockSnapshots:output_type -> master_pb.ListBlockSnapshotsResponse
|
||||
79, // 104: master_pb.Seaweed.ExpandBlockVolume:output_type -> master_pb.ExpandBlockVolumeResponse
|
||||
74, // [74:105] is the sub-list for method output_type
|
||||
43, // [43:74] is the sub-list for method input_type
|
||||
78, // 73: master_pb.Seaweed.RestoreBlockSnapshot:input_type -> master_pb.RestoreBlockSnapshotRequest
|
||||
80, // 74: master_pb.Seaweed.ExpandBlockVolume:input_type -> master_pb.ExpandBlockVolumeRequest
|
||||
1, // 75: master_pb.Seaweed.SendHeartbeat:output_type -> master_pb.HeartbeatResponse
|
||||
11, // 76: master_pb.Seaweed.KeepConnected:output_type -> master_pb.KeepConnectedResponse
|
||||
13, // 77: master_pb.Seaweed.LookupVolume:output_type -> master_pb.LookupVolumeResponse
|
||||
17, // 78: master_pb.Seaweed.Assign:output_type -> master_pb.AssignResponse
|
||||
17, // 79: master_pb.Seaweed.StreamAssign:output_type -> master_pb.AssignResponse
|
||||
19, // 80: master_pb.Seaweed.Statistics:output_type -> master_pb.StatisticsResponse
|
||||
22, // 81: master_pb.Seaweed.CollectionList:output_type -> master_pb.CollectionListResponse
|
||||
24, // 82: master_pb.Seaweed.CollectionDelete:output_type -> master_pb.CollectionDeleteResponse
|
||||
31, // 83: master_pb.Seaweed.VolumeList:output_type -> master_pb.VolumeListResponse
|
||||
33, // 84: master_pb.Seaweed.LookupEcVolume:output_type -> master_pb.LookupEcVolumeResponse
|
||||
35, // 85: master_pb.Seaweed.VacuumVolume:output_type -> master_pb.VacuumVolumeResponse
|
||||
37, // 86: master_pb.Seaweed.DisableVacuum:output_type -> master_pb.DisableVacuumResponse
|
||||
39, // 87: master_pb.Seaweed.EnableVacuum:output_type -> master_pb.EnableVacuumResponse
|
||||
41, // 88: master_pb.Seaweed.VolumeMarkReadonly:output_type -> master_pb.VolumeMarkReadonlyResponse
|
||||
43, // 89: master_pb.Seaweed.GetMasterConfiguration:output_type -> master_pb.GetMasterConfigurationResponse
|
||||
45, // 90: master_pb.Seaweed.ListClusterNodes:output_type -> master_pb.ListClusterNodesResponse
|
||||
47, // 91: master_pb.Seaweed.LeaseAdminToken:output_type -> master_pb.LeaseAdminTokenResponse
|
||||
49, // 92: master_pb.Seaweed.ReleaseAdminToken:output_type -> master_pb.ReleaseAdminTokenResponse
|
||||
51, // 93: master_pb.Seaweed.Ping:output_type -> master_pb.PingResponse
|
||||
57, // 94: master_pb.Seaweed.RaftListClusterServers:output_type -> master_pb.RaftListClusterServersResponse
|
||||
53, // 95: master_pb.Seaweed.RaftAddServer:output_type -> master_pb.RaftAddServerResponse
|
||||
55, // 96: master_pb.Seaweed.RaftRemoveServer:output_type -> master_pb.RaftRemoveServerResponse
|
||||
59, // 97: master_pb.Seaweed.RaftLeadershipTransfer:output_type -> master_pb.RaftLeadershipTransferResponse
|
||||
60, // 98: master_pb.Seaweed.VolumeGrow:output_type -> master_pb.VolumeGrowResponse
|
||||
66, // 99: master_pb.Seaweed.CreateBlockVolume:output_type -> master_pb.CreateBlockVolumeResponse
|
||||
68, // 100: master_pb.Seaweed.DeleteBlockVolume:output_type -> master_pb.DeleteBlockVolumeResponse
|
||||
70, // 101: master_pb.Seaweed.LookupBlockVolume:output_type -> master_pb.LookupBlockVolumeResponse
|
||||
72, // 102: master_pb.Seaweed.CreateBlockSnapshot:output_type -> master_pb.CreateBlockSnapshotResponse
|
||||
74, // 103: master_pb.Seaweed.DeleteBlockSnapshot:output_type -> master_pb.DeleteBlockSnapshotResponse
|
||||
76, // 104: master_pb.Seaweed.ListBlockSnapshots:output_type -> master_pb.ListBlockSnapshotsResponse
|
||||
79, // 105: master_pb.Seaweed.RestoreBlockSnapshot:output_type -> master_pb.RestoreBlockSnapshotResponse
|
||||
81, // 106: master_pb.Seaweed.ExpandBlockVolume:output_type -> master_pb.ExpandBlockVolumeResponse
|
||||
75, // [75:107] is the sub-list for method output_type
|
||||
43, // [43:75] is the sub-list for method input_type
|
||||
43, // [43:43] is the sub-list for extension type_name
|
||||
43, // [43:43] is the sub-list for extension extendee
|
||||
0, // [0:43] is the sub-list for field type_name
|
||||
@@ -6236,7 +6357,7 @@ func file_master_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_master_proto_rawDesc), len(file_master_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 91,
|
||||
NumMessages: 93,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc v5.29.5
|
||||
// - protoc v3.21.12
|
||||
// source: master.proto
|
||||
|
||||
package master_pb
|
||||
@@ -49,6 +49,7 @@ const (
|
||||
Seaweed_CreateBlockSnapshot_FullMethodName = "/master_pb.Seaweed/CreateBlockSnapshot"
|
||||
Seaweed_DeleteBlockSnapshot_FullMethodName = "/master_pb.Seaweed/DeleteBlockSnapshot"
|
||||
Seaweed_ListBlockSnapshots_FullMethodName = "/master_pb.Seaweed/ListBlockSnapshots"
|
||||
Seaweed_RestoreBlockSnapshot_FullMethodName = "/master_pb.Seaweed/RestoreBlockSnapshot"
|
||||
Seaweed_ExpandBlockVolume_FullMethodName = "/master_pb.Seaweed/ExpandBlockVolume"
|
||||
)
|
||||
|
||||
@@ -86,6 +87,7 @@ type SeaweedClient interface {
|
||||
CreateBlockSnapshot(ctx context.Context, in *CreateBlockSnapshotRequest, opts ...grpc.CallOption) (*CreateBlockSnapshotResponse, error)
|
||||
DeleteBlockSnapshot(ctx context.Context, in *DeleteBlockSnapshotRequest, opts ...grpc.CallOption) (*DeleteBlockSnapshotResponse, error)
|
||||
ListBlockSnapshots(ctx context.Context, in *ListBlockSnapshotsRequest, opts ...grpc.CallOption) (*ListBlockSnapshotsResponse, error)
|
||||
RestoreBlockSnapshot(ctx context.Context, in *RestoreBlockSnapshotRequest, opts ...grpc.CallOption) (*RestoreBlockSnapshotResponse, error)
|
||||
ExpandBlockVolume(ctx context.Context, in *ExpandBlockVolumeRequest, opts ...grpc.CallOption) (*ExpandBlockVolumeResponse, error)
|
||||
}
|
||||
|
||||
@@ -406,6 +408,16 @@ func (c *seaweedClient) ListBlockSnapshots(ctx context.Context, in *ListBlockSna
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedClient) RestoreBlockSnapshot(ctx context.Context, in *RestoreBlockSnapshotRequest, opts ...grpc.CallOption) (*RestoreBlockSnapshotResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RestoreBlockSnapshotResponse)
|
||||
err := c.cc.Invoke(ctx, Seaweed_RestoreBlockSnapshot_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *seaweedClient) ExpandBlockVolume(ctx context.Context, in *ExpandBlockVolumeRequest, opts ...grpc.CallOption) (*ExpandBlockVolumeResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ExpandBlockVolumeResponse)
|
||||
@@ -450,6 +462,7 @@ type SeaweedServer interface {
|
||||
CreateBlockSnapshot(context.Context, *CreateBlockSnapshotRequest) (*CreateBlockSnapshotResponse, error)
|
||||
DeleteBlockSnapshot(context.Context, *DeleteBlockSnapshotRequest) (*DeleteBlockSnapshotResponse, error)
|
||||
ListBlockSnapshots(context.Context, *ListBlockSnapshotsRequest) (*ListBlockSnapshotsResponse, error)
|
||||
RestoreBlockSnapshot(context.Context, *RestoreBlockSnapshotRequest) (*RestoreBlockSnapshotResponse, error)
|
||||
ExpandBlockVolume(context.Context, *ExpandBlockVolumeRequest) (*ExpandBlockVolumeResponse, error)
|
||||
mustEmbedUnimplementedSeaweedServer()
|
||||
}
|
||||
@@ -551,6 +564,9 @@ func (UnimplementedSeaweedServer) DeleteBlockSnapshot(context.Context, *DeleteBl
|
||||
func (UnimplementedSeaweedServer) ListBlockSnapshots(context.Context, *ListBlockSnapshotsRequest) (*ListBlockSnapshotsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListBlockSnapshots not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) RestoreBlockSnapshot(context.Context, *RestoreBlockSnapshotRequest) (*RestoreBlockSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RestoreBlockSnapshot not implemented")
|
||||
}
|
||||
func (UnimplementedSeaweedServer) ExpandBlockVolume(context.Context, *ExpandBlockVolumeRequest) (*ExpandBlockVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ExpandBlockVolume not implemented")
|
||||
}
|
||||
@@ -1082,6 +1098,24 @@ func _Seaweed_ListBlockSnapshots_Handler(srv interface{}, ctx context.Context, d
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Seaweed_RestoreBlockSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RestoreBlockSnapshotRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SeaweedServer).RestoreBlockSnapshot(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Seaweed_RestoreBlockSnapshot_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SeaweedServer).RestoreBlockSnapshot(ctx, req.(*RestoreBlockSnapshotRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Seaweed_ExpandBlockVolume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ExpandBlockVolumeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -1215,6 +1249,10 @@ var Seaweed_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListBlockSnapshots",
|
||||
Handler: _Seaweed_ListBlockSnapshots_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RestoreBlockSnapshot",
|
||||
Handler: _Seaweed_RestoreBlockSnapshot_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ExpandBlockVolume",
|
||||
Handler: _Seaweed_ExpandBlockVolume_Handler,
|
||||
|
||||
@@ -149,6 +149,8 @@ service VolumeServer {
|
||||
}
|
||||
rpc ListBlockSnapshots (ListBlockSnapshotsRequest) returns (ListBlockSnapshotsResponse) {
|
||||
}
|
||||
rpc RestoreBlockSnapshot (RestoreBlockSnapshotRequest) returns (RestoreBlockSnapshotResponse) {
|
||||
}
|
||||
rpc ExpandBlockVolume (ExpandBlockVolumeRequest) returns (ExpandBlockVolumeResponse) {
|
||||
}
|
||||
rpc PrepareExpandBlockVolume (PrepareExpandBlockVolumeRequest) returns (PrepareExpandBlockVolumeResponse) {
|
||||
@@ -821,6 +823,13 @@ message DeleteBlockSnapshotRequest {
|
||||
message DeleteBlockSnapshotResponse {
|
||||
}
|
||||
|
||||
message RestoreBlockSnapshotRequest {
|
||||
string name = 1;
|
||||
uint32 snapshot_id = 2;
|
||||
}
|
||||
message RestoreBlockSnapshotResponse {
|
||||
}
|
||||
|
||||
message ListBlockSnapshotsRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v5.29.5
|
||||
// protoc v3.21.12
|
||||
// source: volume_server.proto
|
||||
|
||||
package volume_server_pb
|
||||
@@ -6633,6 +6633,94 @@ func (*DeleteBlockSnapshotResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{114}
|
||||
}
|
||||
|
||||
type RestoreBlockSnapshotRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
SnapshotId uint32 `protobuf:"varint,2,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) Reset() {
|
||||
*x = RestoreBlockSnapshotRequest{}
|
||||
mi := &file_volume_server_proto_msgTypes[115]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RestoreBlockSnapshotRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[115]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RestoreBlockSnapshotRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RestoreBlockSnapshotRequest) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{115}
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotRequest) GetSnapshotId() uint32 {
|
||||
if x != nil {
|
||||
return x.SnapshotId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RestoreBlockSnapshotResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotResponse) Reset() {
|
||||
*x = RestoreBlockSnapshotResponse{}
|
||||
mi := &file_volume_server_proto_msgTypes[116]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RestoreBlockSnapshotResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RestoreBlockSnapshotResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RestoreBlockSnapshotResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[116]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RestoreBlockSnapshotResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RestoreBlockSnapshotResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{116}
|
||||
}
|
||||
|
||||
type ListBlockSnapshotsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
@@ -6642,7 +6730,7 @@ type ListBlockSnapshotsRequest struct {
|
||||
|
||||
func (x *ListBlockSnapshotsRequest) Reset() {
|
||||
*x = ListBlockSnapshotsRequest{}
|
||||
mi := &file_volume_server_proto_msgTypes[115]
|
||||
mi := &file_volume_server_proto_msgTypes[117]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6654,7 +6742,7 @@ func (x *ListBlockSnapshotsRequest) String() string {
|
||||
func (*ListBlockSnapshotsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListBlockSnapshotsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[115]
|
||||
mi := &file_volume_server_proto_msgTypes[117]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6667,7 +6755,7 @@ func (x *ListBlockSnapshotsRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListBlockSnapshotsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListBlockSnapshotsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{115}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{117}
|
||||
}
|
||||
|
||||
func (x *ListBlockSnapshotsRequest) GetName() string {
|
||||
@@ -6686,7 +6774,7 @@ type ListBlockSnapshotsResponse struct {
|
||||
|
||||
func (x *ListBlockSnapshotsResponse) Reset() {
|
||||
*x = ListBlockSnapshotsResponse{}
|
||||
mi := &file_volume_server_proto_msgTypes[116]
|
||||
mi := &file_volume_server_proto_msgTypes[118]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6698,7 +6786,7 @@ func (x *ListBlockSnapshotsResponse) String() string {
|
||||
func (*ListBlockSnapshotsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListBlockSnapshotsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[116]
|
||||
mi := &file_volume_server_proto_msgTypes[118]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6711,7 +6799,7 @@ func (x *ListBlockSnapshotsResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListBlockSnapshotsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListBlockSnapshotsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{116}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{118}
|
||||
}
|
||||
|
||||
func (x *ListBlockSnapshotsResponse) GetSnapshots() []*BlockSnapshotInfo {
|
||||
@@ -6732,7 +6820,7 @@ type BlockSnapshotInfo struct {
|
||||
|
||||
func (x *BlockSnapshotInfo) Reset() {
|
||||
*x = BlockSnapshotInfo{}
|
||||
mi := &file_volume_server_proto_msgTypes[117]
|
||||
mi := &file_volume_server_proto_msgTypes[119]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6744,7 +6832,7 @@ func (x *BlockSnapshotInfo) String() string {
|
||||
func (*BlockSnapshotInfo) ProtoMessage() {}
|
||||
|
||||
func (x *BlockSnapshotInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[117]
|
||||
mi := &file_volume_server_proto_msgTypes[119]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6757,7 +6845,7 @@ func (x *BlockSnapshotInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BlockSnapshotInfo.ProtoReflect.Descriptor instead.
|
||||
func (*BlockSnapshotInfo) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{117}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{119}
|
||||
}
|
||||
|
||||
func (x *BlockSnapshotInfo) GetSnapshotId() uint32 {
|
||||
@@ -6791,7 +6879,7 @@ type ExpandBlockVolumeRequest struct {
|
||||
|
||||
func (x *ExpandBlockVolumeRequest) Reset() {
|
||||
*x = ExpandBlockVolumeRequest{}
|
||||
mi := &file_volume_server_proto_msgTypes[118]
|
||||
mi := &file_volume_server_proto_msgTypes[120]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6803,7 +6891,7 @@ func (x *ExpandBlockVolumeRequest) String() string {
|
||||
func (*ExpandBlockVolumeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[118]
|
||||
mi := &file_volume_server_proto_msgTypes[120]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6816,7 +6904,7 @@ func (x *ExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ExpandBlockVolumeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ExpandBlockVolumeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{118}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{120}
|
||||
}
|
||||
|
||||
func (x *ExpandBlockVolumeRequest) GetName() string {
|
||||
@@ -6842,7 +6930,7 @@ type ExpandBlockVolumeResponse struct {
|
||||
|
||||
func (x *ExpandBlockVolumeResponse) Reset() {
|
||||
*x = ExpandBlockVolumeResponse{}
|
||||
mi := &file_volume_server_proto_msgTypes[119]
|
||||
mi := &file_volume_server_proto_msgTypes[121]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6854,7 +6942,7 @@ func (x *ExpandBlockVolumeResponse) String() string {
|
||||
func (*ExpandBlockVolumeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[119]
|
||||
mi := &file_volume_server_proto_msgTypes[121]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6867,7 +6955,7 @@ func (x *ExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ExpandBlockVolumeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ExpandBlockVolumeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{119}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{121}
|
||||
}
|
||||
|
||||
func (x *ExpandBlockVolumeResponse) GetCapacityBytes() uint64 {
|
||||
@@ -6888,7 +6976,7 @@ type PrepareExpandBlockVolumeRequest struct {
|
||||
|
||||
func (x *PrepareExpandBlockVolumeRequest) Reset() {
|
||||
*x = PrepareExpandBlockVolumeRequest{}
|
||||
mi := &file_volume_server_proto_msgTypes[120]
|
||||
mi := &file_volume_server_proto_msgTypes[122]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6900,7 +6988,7 @@ func (x *PrepareExpandBlockVolumeRequest) String() string {
|
||||
func (*PrepareExpandBlockVolumeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PrepareExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[120]
|
||||
mi := &file_volume_server_proto_msgTypes[122]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6913,7 +7001,7 @@ func (x *PrepareExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PrepareExpandBlockVolumeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PrepareExpandBlockVolumeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{120}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{122}
|
||||
}
|
||||
|
||||
func (x *PrepareExpandBlockVolumeRequest) GetName() string {
|
||||
@@ -6945,7 +7033,7 @@ type PrepareExpandBlockVolumeResponse struct {
|
||||
|
||||
func (x *PrepareExpandBlockVolumeResponse) Reset() {
|
||||
*x = PrepareExpandBlockVolumeResponse{}
|
||||
mi := &file_volume_server_proto_msgTypes[121]
|
||||
mi := &file_volume_server_proto_msgTypes[123]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6957,7 +7045,7 @@ func (x *PrepareExpandBlockVolumeResponse) String() string {
|
||||
func (*PrepareExpandBlockVolumeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PrepareExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[121]
|
||||
mi := &file_volume_server_proto_msgTypes[123]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6970,7 +7058,7 @@ func (x *PrepareExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PrepareExpandBlockVolumeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PrepareExpandBlockVolumeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{121}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{123}
|
||||
}
|
||||
|
||||
type CommitExpandBlockVolumeRequest struct {
|
||||
@@ -6983,7 +7071,7 @@ type CommitExpandBlockVolumeRequest struct {
|
||||
|
||||
func (x *CommitExpandBlockVolumeRequest) Reset() {
|
||||
*x = CommitExpandBlockVolumeRequest{}
|
||||
mi := &file_volume_server_proto_msgTypes[122]
|
||||
mi := &file_volume_server_proto_msgTypes[124]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6995,7 +7083,7 @@ func (x *CommitExpandBlockVolumeRequest) String() string {
|
||||
func (*CommitExpandBlockVolumeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CommitExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[122]
|
||||
mi := &file_volume_server_proto_msgTypes[124]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7008,7 +7096,7 @@ func (x *CommitExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CommitExpandBlockVolumeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CommitExpandBlockVolumeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{122}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{124}
|
||||
}
|
||||
|
||||
func (x *CommitExpandBlockVolumeRequest) GetName() string {
|
||||
@@ -7034,7 +7122,7 @@ type CommitExpandBlockVolumeResponse struct {
|
||||
|
||||
func (x *CommitExpandBlockVolumeResponse) Reset() {
|
||||
*x = CommitExpandBlockVolumeResponse{}
|
||||
mi := &file_volume_server_proto_msgTypes[123]
|
||||
mi := &file_volume_server_proto_msgTypes[125]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7046,7 +7134,7 @@ func (x *CommitExpandBlockVolumeResponse) String() string {
|
||||
func (*CommitExpandBlockVolumeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CommitExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[123]
|
||||
mi := &file_volume_server_proto_msgTypes[125]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7059,7 +7147,7 @@ func (x *CommitExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CommitExpandBlockVolumeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CommitExpandBlockVolumeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{123}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{125}
|
||||
}
|
||||
|
||||
func (x *CommitExpandBlockVolumeResponse) GetCapacityBytes() uint64 {
|
||||
@@ -7079,7 +7167,7 @@ type CancelExpandBlockVolumeRequest struct {
|
||||
|
||||
func (x *CancelExpandBlockVolumeRequest) Reset() {
|
||||
*x = CancelExpandBlockVolumeRequest{}
|
||||
mi := &file_volume_server_proto_msgTypes[124]
|
||||
mi := &file_volume_server_proto_msgTypes[126]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7091,7 +7179,7 @@ func (x *CancelExpandBlockVolumeRequest) String() string {
|
||||
func (*CancelExpandBlockVolumeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CancelExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[124]
|
||||
mi := &file_volume_server_proto_msgTypes[126]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7104,7 +7192,7 @@ func (x *CancelExpandBlockVolumeRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CancelExpandBlockVolumeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CancelExpandBlockVolumeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{124}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{126}
|
||||
}
|
||||
|
||||
func (x *CancelExpandBlockVolumeRequest) GetName() string {
|
||||
@@ -7129,7 +7217,7 @@ type CancelExpandBlockVolumeResponse struct {
|
||||
|
||||
func (x *CancelExpandBlockVolumeResponse) Reset() {
|
||||
*x = CancelExpandBlockVolumeResponse{}
|
||||
mi := &file_volume_server_proto_msgTypes[125]
|
||||
mi := &file_volume_server_proto_msgTypes[127]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7141,7 +7229,7 @@ func (x *CancelExpandBlockVolumeResponse) String() string {
|
||||
func (*CancelExpandBlockVolumeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CancelExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[125]
|
||||
mi := &file_volume_server_proto_msgTypes[127]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7154,7 +7242,7 @@ func (x *CancelExpandBlockVolumeResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use CancelExpandBlockVolumeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CancelExpandBlockVolumeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{125}
|
||||
return file_volume_server_proto_rawDescGZIP(), []int{127}
|
||||
}
|
||||
|
||||
type FetchAndWriteNeedleRequest_Replica struct {
|
||||
@@ -7168,7 +7256,7 @@ type FetchAndWriteNeedleRequest_Replica struct {
|
||||
|
||||
func (x *FetchAndWriteNeedleRequest_Replica) Reset() {
|
||||
*x = FetchAndWriteNeedleRequest_Replica{}
|
||||
mi := &file_volume_server_proto_msgTypes[126]
|
||||
mi := &file_volume_server_proto_msgTypes[128]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7180,7 +7268,7 @@ func (x *FetchAndWriteNeedleRequest_Replica) String() string {
|
||||
func (*FetchAndWriteNeedleRequest_Replica) ProtoMessage() {}
|
||||
|
||||
func (x *FetchAndWriteNeedleRequest_Replica) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[126]
|
||||
mi := &file_volume_server_proto_msgTypes[128]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7228,7 +7316,7 @@ type QueryRequest_Filter struct {
|
||||
|
||||
func (x *QueryRequest_Filter) Reset() {
|
||||
*x = QueryRequest_Filter{}
|
||||
mi := &file_volume_server_proto_msgTypes[127]
|
||||
mi := &file_volume_server_proto_msgTypes[129]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7240,7 +7328,7 @@ func (x *QueryRequest_Filter) String() string {
|
||||
func (*QueryRequest_Filter) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_Filter) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[127]
|
||||
mi := &file_volume_server_proto_msgTypes[129]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7290,7 +7378,7 @@ type QueryRequest_InputSerialization struct {
|
||||
|
||||
func (x *QueryRequest_InputSerialization) Reset() {
|
||||
*x = QueryRequest_InputSerialization{}
|
||||
mi := &file_volume_server_proto_msgTypes[128]
|
||||
mi := &file_volume_server_proto_msgTypes[130]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7302,7 +7390,7 @@ func (x *QueryRequest_InputSerialization) String() string {
|
||||
func (*QueryRequest_InputSerialization) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_InputSerialization) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[128]
|
||||
mi := &file_volume_server_proto_msgTypes[130]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7356,7 +7444,7 @@ type QueryRequest_OutputSerialization struct {
|
||||
|
||||
func (x *QueryRequest_OutputSerialization) Reset() {
|
||||
*x = QueryRequest_OutputSerialization{}
|
||||
mi := &file_volume_server_proto_msgTypes[129]
|
||||
mi := &file_volume_server_proto_msgTypes[131]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7368,7 +7456,7 @@ func (x *QueryRequest_OutputSerialization) String() string {
|
||||
func (*QueryRequest_OutputSerialization) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_OutputSerialization) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[129]
|
||||
mi := &file_volume_server_proto_msgTypes[131]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7414,7 +7502,7 @@ type QueryRequest_InputSerialization_CSVInput struct {
|
||||
|
||||
func (x *QueryRequest_InputSerialization_CSVInput) Reset() {
|
||||
*x = QueryRequest_InputSerialization_CSVInput{}
|
||||
mi := &file_volume_server_proto_msgTypes[130]
|
||||
mi := &file_volume_server_proto_msgTypes[132]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7426,7 +7514,7 @@ func (x *QueryRequest_InputSerialization_CSVInput) String() string {
|
||||
func (*QueryRequest_InputSerialization_CSVInput) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_InputSerialization_CSVInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[130]
|
||||
mi := &file_volume_server_proto_msgTypes[132]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7500,7 +7588,7 @@ type QueryRequest_InputSerialization_JSONInput struct {
|
||||
|
||||
func (x *QueryRequest_InputSerialization_JSONInput) Reset() {
|
||||
*x = QueryRequest_InputSerialization_JSONInput{}
|
||||
mi := &file_volume_server_proto_msgTypes[131]
|
||||
mi := &file_volume_server_proto_msgTypes[133]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7512,7 +7600,7 @@ func (x *QueryRequest_InputSerialization_JSONInput) String() string {
|
||||
func (*QueryRequest_InputSerialization_JSONInput) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_InputSerialization_JSONInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[131]
|
||||
mi := &file_volume_server_proto_msgTypes[133]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7543,7 +7631,7 @@ type QueryRequest_InputSerialization_ParquetInput struct {
|
||||
|
||||
func (x *QueryRequest_InputSerialization_ParquetInput) Reset() {
|
||||
*x = QueryRequest_InputSerialization_ParquetInput{}
|
||||
mi := &file_volume_server_proto_msgTypes[132]
|
||||
mi := &file_volume_server_proto_msgTypes[134]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7555,7 +7643,7 @@ func (x *QueryRequest_InputSerialization_ParquetInput) String() string {
|
||||
func (*QueryRequest_InputSerialization_ParquetInput) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_InputSerialization_ParquetInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[132]
|
||||
mi := &file_volume_server_proto_msgTypes[134]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7584,7 +7672,7 @@ type QueryRequest_OutputSerialization_CSVOutput struct {
|
||||
|
||||
func (x *QueryRequest_OutputSerialization_CSVOutput) Reset() {
|
||||
*x = QueryRequest_OutputSerialization_CSVOutput{}
|
||||
mi := &file_volume_server_proto_msgTypes[133]
|
||||
mi := &file_volume_server_proto_msgTypes[135]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7596,7 +7684,7 @@ func (x *QueryRequest_OutputSerialization_CSVOutput) String() string {
|
||||
func (*QueryRequest_OutputSerialization_CSVOutput) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_OutputSerialization_CSVOutput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[133]
|
||||
mi := &file_volume_server_proto_msgTypes[135]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -7656,7 +7744,7 @@ type QueryRequest_OutputSerialization_JSONOutput struct {
|
||||
|
||||
func (x *QueryRequest_OutputSerialization_JSONOutput) Reset() {
|
||||
*x = QueryRequest_OutputSerialization_JSONOutput{}
|
||||
mi := &file_volume_server_proto_msgTypes[134]
|
||||
mi := &file_volume_server_proto_msgTypes[136]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -7668,7 +7756,7 @@ func (x *QueryRequest_OutputSerialization_JSONOutput) String() string {
|
||||
func (*QueryRequest_OutputSerialization_JSONOutput) ProtoMessage() {}
|
||||
|
||||
func (x *QueryRequest_OutputSerialization_JSONOutput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_volume_server_proto_msgTypes[134]
|
||||
mi := &file_volume_server_proto_msgTypes[136]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -8229,7 +8317,12 @@ const file_volume_server_proto_rawDesc = "" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n" +
|
||||
"\vsnapshot_id\x18\x02 \x01(\rR\n" +
|
||||
"snapshotId\"\x1d\n" +
|
||||
"\x1bDeleteBlockSnapshotResponse\"/\n" +
|
||||
"\x1bDeleteBlockSnapshotResponse\"R\n" +
|
||||
"\x1bRestoreBlockSnapshotRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n" +
|
||||
"\vsnapshot_id\x18\x02 \x01(\rR\n" +
|
||||
"snapshotId\"\x1e\n" +
|
||||
"\x1cRestoreBlockSnapshotResponse\"/\n" +
|
||||
"\x19ListBlockSnapshotsRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\"_\n" +
|
||||
"\x1aListBlockSnapshotsResponse\x12A\n" +
|
||||
@@ -8263,7 +8356,7 @@ const file_volume_server_proto_rawDesc = "" +
|
||||
"\aUNKNOWN\x10\x00\x12\t\n" +
|
||||
"\x05INDEX\x10\x01\x12\b\n" +
|
||||
"\x04FULL\x10\x02\x12\t\n" +
|
||||
"\x05LOCAL\x10\x032\xe11\n" +
|
||||
"\x05LOCAL\x10\x032\xda2\n" +
|
||||
"\fVolumeServer\x12\\\n" +
|
||||
"\vBatchDelete\x12$.volume_server_pb.BatchDeleteRequest\x1a%.volume_server_pb.BatchDeleteResponse\"\x00\x12n\n" +
|
||||
"\x11VacuumVolumeCheck\x12*.volume_server_pb.VacuumVolumeCheckRequest\x1a+.volume_server_pb.VacuumVolumeCheckResponse\"\x00\x12v\n" +
|
||||
@@ -8318,7 +8411,8 @@ const file_volume_server_proto_rawDesc = "" +
|
||||
"\x1dVolumeServerDeleteBlockVolume\x126.volume_server_pb.VolumeServerDeleteBlockVolumeRequest\x1a7.volume_server_pb.VolumeServerDeleteBlockVolumeResponse\"\x00\x12t\n" +
|
||||
"\x13SnapshotBlockVolume\x12,.volume_server_pb.SnapshotBlockVolumeRequest\x1a-.volume_server_pb.SnapshotBlockVolumeResponse\"\x00\x12t\n" +
|
||||
"\x13DeleteBlockSnapshot\x12,.volume_server_pb.DeleteBlockSnapshotRequest\x1a-.volume_server_pb.DeleteBlockSnapshotResponse\"\x00\x12q\n" +
|
||||
"\x12ListBlockSnapshots\x12+.volume_server_pb.ListBlockSnapshotsRequest\x1a,.volume_server_pb.ListBlockSnapshotsResponse\"\x00\x12n\n" +
|
||||
"\x12ListBlockSnapshots\x12+.volume_server_pb.ListBlockSnapshotsRequest\x1a,.volume_server_pb.ListBlockSnapshotsResponse\"\x00\x12w\n" +
|
||||
"\x14RestoreBlockSnapshot\x12-.volume_server_pb.RestoreBlockSnapshotRequest\x1a..volume_server_pb.RestoreBlockSnapshotResponse\"\x00\x12n\n" +
|
||||
"\x11ExpandBlockVolume\x12*.volume_server_pb.ExpandBlockVolumeRequest\x1a+.volume_server_pb.ExpandBlockVolumeResponse\"\x00\x12\x83\x01\n" +
|
||||
"\x18PrepareExpandBlockVolume\x121.volume_server_pb.PrepareExpandBlockVolumeRequest\x1a2.volume_server_pb.PrepareExpandBlockVolumeResponse\"\x00\x12\x80\x01\n" +
|
||||
"\x17CommitExpandBlockVolume\x120.volume_server_pb.CommitExpandBlockVolumeRequest\x1a1.volume_server_pb.CommitExpandBlockVolumeResponse\"\x00\x12\x80\x01\n" +
|
||||
@@ -8337,7 +8431,7 @@ func file_volume_server_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_volume_server_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_volume_server_proto_msgTypes = make([]protoimpl.MessageInfo, 135)
|
||||
var file_volume_server_proto_msgTypes = make([]protoimpl.MessageInfo, 137)
|
||||
var file_volume_server_proto_goTypes = []any{
|
||||
(VolumeScrubMode)(0), // 0: volume_server_pb.VolumeScrubMode
|
||||
(*VolumeServerState)(nil), // 1: volume_server_pb.VolumeServerState
|
||||
@@ -8455,28 +8549,30 @@ var file_volume_server_proto_goTypes = []any{
|
||||
(*SnapshotBlockVolumeResponse)(nil), // 113: volume_server_pb.SnapshotBlockVolumeResponse
|
||||
(*DeleteBlockSnapshotRequest)(nil), // 114: volume_server_pb.DeleteBlockSnapshotRequest
|
||||
(*DeleteBlockSnapshotResponse)(nil), // 115: volume_server_pb.DeleteBlockSnapshotResponse
|
||||
(*ListBlockSnapshotsRequest)(nil), // 116: volume_server_pb.ListBlockSnapshotsRequest
|
||||
(*ListBlockSnapshotsResponse)(nil), // 117: volume_server_pb.ListBlockSnapshotsResponse
|
||||
(*BlockSnapshotInfo)(nil), // 118: volume_server_pb.BlockSnapshotInfo
|
||||
(*ExpandBlockVolumeRequest)(nil), // 119: volume_server_pb.ExpandBlockVolumeRequest
|
||||
(*ExpandBlockVolumeResponse)(nil), // 120: volume_server_pb.ExpandBlockVolumeResponse
|
||||
(*PrepareExpandBlockVolumeRequest)(nil), // 121: volume_server_pb.PrepareExpandBlockVolumeRequest
|
||||
(*PrepareExpandBlockVolumeResponse)(nil), // 122: volume_server_pb.PrepareExpandBlockVolumeResponse
|
||||
(*CommitExpandBlockVolumeRequest)(nil), // 123: volume_server_pb.CommitExpandBlockVolumeRequest
|
||||
(*CommitExpandBlockVolumeResponse)(nil), // 124: volume_server_pb.CommitExpandBlockVolumeResponse
|
||||
(*CancelExpandBlockVolumeRequest)(nil), // 125: volume_server_pb.CancelExpandBlockVolumeRequest
|
||||
(*CancelExpandBlockVolumeResponse)(nil), // 126: volume_server_pb.CancelExpandBlockVolumeResponse
|
||||
(*FetchAndWriteNeedleRequest_Replica)(nil), // 127: volume_server_pb.FetchAndWriteNeedleRequest.Replica
|
||||
(*QueryRequest_Filter)(nil), // 128: volume_server_pb.QueryRequest.Filter
|
||||
(*QueryRequest_InputSerialization)(nil), // 129: volume_server_pb.QueryRequest.InputSerialization
|
||||
(*QueryRequest_OutputSerialization)(nil), // 130: volume_server_pb.QueryRequest.OutputSerialization
|
||||
(*QueryRequest_InputSerialization_CSVInput)(nil), // 131: volume_server_pb.QueryRequest.InputSerialization.CSVInput
|
||||
(*QueryRequest_InputSerialization_JSONInput)(nil), // 132: volume_server_pb.QueryRequest.InputSerialization.JSONInput
|
||||
(*QueryRequest_InputSerialization_ParquetInput)(nil), // 133: volume_server_pb.QueryRequest.InputSerialization.ParquetInput
|
||||
(*QueryRequest_OutputSerialization_CSVOutput)(nil), // 134: volume_server_pb.QueryRequest.OutputSerialization.CSVOutput
|
||||
(*QueryRequest_OutputSerialization_JSONOutput)(nil), // 135: volume_server_pb.QueryRequest.OutputSerialization.JSONOutput
|
||||
(*remote_pb.RemoteConf)(nil), // 136: remote_pb.RemoteConf
|
||||
(*remote_pb.RemoteStorageLocation)(nil), // 137: remote_pb.RemoteStorageLocation
|
||||
(*RestoreBlockSnapshotRequest)(nil), // 116: volume_server_pb.RestoreBlockSnapshotRequest
|
||||
(*RestoreBlockSnapshotResponse)(nil), // 117: volume_server_pb.RestoreBlockSnapshotResponse
|
||||
(*ListBlockSnapshotsRequest)(nil), // 118: volume_server_pb.ListBlockSnapshotsRequest
|
||||
(*ListBlockSnapshotsResponse)(nil), // 119: volume_server_pb.ListBlockSnapshotsResponse
|
||||
(*BlockSnapshotInfo)(nil), // 120: volume_server_pb.BlockSnapshotInfo
|
||||
(*ExpandBlockVolumeRequest)(nil), // 121: volume_server_pb.ExpandBlockVolumeRequest
|
||||
(*ExpandBlockVolumeResponse)(nil), // 122: volume_server_pb.ExpandBlockVolumeResponse
|
||||
(*PrepareExpandBlockVolumeRequest)(nil), // 123: volume_server_pb.PrepareExpandBlockVolumeRequest
|
||||
(*PrepareExpandBlockVolumeResponse)(nil), // 124: volume_server_pb.PrepareExpandBlockVolumeResponse
|
||||
(*CommitExpandBlockVolumeRequest)(nil), // 125: volume_server_pb.CommitExpandBlockVolumeRequest
|
||||
(*CommitExpandBlockVolumeResponse)(nil), // 126: volume_server_pb.CommitExpandBlockVolumeResponse
|
||||
(*CancelExpandBlockVolumeRequest)(nil), // 127: volume_server_pb.CancelExpandBlockVolumeRequest
|
||||
(*CancelExpandBlockVolumeResponse)(nil), // 128: volume_server_pb.CancelExpandBlockVolumeResponse
|
||||
(*FetchAndWriteNeedleRequest_Replica)(nil), // 129: volume_server_pb.FetchAndWriteNeedleRequest.Replica
|
||||
(*QueryRequest_Filter)(nil), // 130: volume_server_pb.QueryRequest.Filter
|
||||
(*QueryRequest_InputSerialization)(nil), // 131: volume_server_pb.QueryRequest.InputSerialization
|
||||
(*QueryRequest_OutputSerialization)(nil), // 132: volume_server_pb.QueryRequest.OutputSerialization
|
||||
(*QueryRequest_InputSerialization_CSVInput)(nil), // 133: volume_server_pb.QueryRequest.InputSerialization.CSVInput
|
||||
(*QueryRequest_InputSerialization_JSONInput)(nil), // 134: volume_server_pb.QueryRequest.InputSerialization.JSONInput
|
||||
(*QueryRequest_InputSerialization_ParquetInput)(nil), // 135: volume_server_pb.QueryRequest.InputSerialization.ParquetInput
|
||||
(*QueryRequest_OutputSerialization_CSVOutput)(nil), // 136: volume_server_pb.QueryRequest.OutputSerialization.CSVOutput
|
||||
(*QueryRequest_OutputSerialization_JSONOutput)(nil), // 137: volume_server_pb.QueryRequest.OutputSerialization.JSONOutput
|
||||
(*remote_pb.RemoteConf)(nil), // 138: remote_pb.RemoteConf
|
||||
(*remote_pb.RemoteStorageLocation)(nil), // 139: remote_pb.RemoteStorageLocation
|
||||
}
|
||||
var file_volume_server_proto_depIdxs = []int32{
|
||||
4, // 0: volume_server_pb.BatchDeleteResponse.results:type_name -> volume_server_pb.DeleteResult
|
||||
@@ -8492,21 +8588,21 @@ var file_volume_server_proto_depIdxs = []int32{
|
||||
82, // 10: volume_server_pb.VolumeServerStatusResponse.disk_statuses:type_name -> volume_server_pb.DiskStatus
|
||||
83, // 11: volume_server_pb.VolumeServerStatusResponse.memory_status:type_name -> volume_server_pb.MemStatus
|
||||
1, // 12: volume_server_pb.VolumeServerStatusResponse.state:type_name -> volume_server_pb.VolumeServerState
|
||||
127, // 13: volume_server_pb.FetchAndWriteNeedleRequest.replicas:type_name -> volume_server_pb.FetchAndWriteNeedleRequest.Replica
|
||||
136, // 14: volume_server_pb.FetchAndWriteNeedleRequest.remote_conf:type_name -> remote_pb.RemoteConf
|
||||
137, // 15: volume_server_pb.FetchAndWriteNeedleRequest.remote_location:type_name -> remote_pb.RemoteStorageLocation
|
||||
129, // 13: volume_server_pb.FetchAndWriteNeedleRequest.replicas:type_name -> volume_server_pb.FetchAndWriteNeedleRequest.Replica
|
||||
138, // 14: volume_server_pb.FetchAndWriteNeedleRequest.remote_conf:type_name -> remote_pb.RemoteConf
|
||||
139, // 15: volume_server_pb.FetchAndWriteNeedleRequest.remote_location:type_name -> remote_pb.RemoteStorageLocation
|
||||
0, // 16: volume_server_pb.ScrubVolumeRequest.mode:type_name -> volume_server_pb.VolumeScrubMode
|
||||
0, // 17: volume_server_pb.ScrubEcVolumeRequest.mode:type_name -> volume_server_pb.VolumeScrubMode
|
||||
79, // 18: volume_server_pb.ScrubEcVolumeResponse.broken_shard_infos:type_name -> volume_server_pb.EcShardInfo
|
||||
128, // 19: volume_server_pb.QueryRequest.filter:type_name -> volume_server_pb.QueryRequest.Filter
|
||||
129, // 20: volume_server_pb.QueryRequest.input_serialization:type_name -> volume_server_pb.QueryRequest.InputSerialization
|
||||
130, // 21: volume_server_pb.QueryRequest.output_serialization:type_name -> volume_server_pb.QueryRequest.OutputSerialization
|
||||
118, // 22: volume_server_pb.ListBlockSnapshotsResponse.snapshots:type_name -> volume_server_pb.BlockSnapshotInfo
|
||||
131, // 23: volume_server_pb.QueryRequest.InputSerialization.csv_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.CSVInput
|
||||
132, // 24: volume_server_pb.QueryRequest.InputSerialization.json_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.JSONInput
|
||||
133, // 25: volume_server_pb.QueryRequest.InputSerialization.parquet_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.ParquetInput
|
||||
134, // 26: volume_server_pb.QueryRequest.OutputSerialization.csv_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.CSVOutput
|
||||
135, // 27: volume_server_pb.QueryRequest.OutputSerialization.json_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.JSONOutput
|
||||
130, // 19: volume_server_pb.QueryRequest.filter:type_name -> volume_server_pb.QueryRequest.Filter
|
||||
131, // 20: volume_server_pb.QueryRequest.input_serialization:type_name -> volume_server_pb.QueryRequest.InputSerialization
|
||||
132, // 21: volume_server_pb.QueryRequest.output_serialization:type_name -> volume_server_pb.QueryRequest.OutputSerialization
|
||||
120, // 22: volume_server_pb.ListBlockSnapshotsResponse.snapshots:type_name -> volume_server_pb.BlockSnapshotInfo
|
||||
133, // 23: volume_server_pb.QueryRequest.InputSerialization.csv_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.CSVInput
|
||||
134, // 24: volume_server_pb.QueryRequest.InputSerialization.json_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.JSONInput
|
||||
135, // 25: volume_server_pb.QueryRequest.InputSerialization.parquet_input:type_name -> volume_server_pb.QueryRequest.InputSerialization.ParquetInput
|
||||
136, // 26: volume_server_pb.QueryRequest.OutputSerialization.csv_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.CSVOutput
|
||||
137, // 27: volume_server_pb.QueryRequest.OutputSerialization.json_output:type_name -> volume_server_pb.QueryRequest.OutputSerialization.JSONOutput
|
||||
2, // 28: volume_server_pb.VolumeServer.BatchDelete:input_type -> volume_server_pb.BatchDeleteRequest
|
||||
6, // 29: volume_server_pb.VolumeServer.VacuumVolumeCheck:input_type -> volume_server_pb.VacuumVolumeCheckRequest
|
||||
8, // 30: volume_server_pb.VolumeServer.VacuumVolumeCompact:input_type -> volume_server_pb.VacuumVolumeCompactRequest
|
||||
@@ -8559,70 +8655,72 @@ var file_volume_server_proto_depIdxs = []int32{
|
||||
110, // 77: volume_server_pb.VolumeServer.VolumeServerDeleteBlockVolume:input_type -> volume_server_pb.VolumeServerDeleteBlockVolumeRequest
|
||||
112, // 78: volume_server_pb.VolumeServer.SnapshotBlockVolume:input_type -> volume_server_pb.SnapshotBlockVolumeRequest
|
||||
114, // 79: volume_server_pb.VolumeServer.DeleteBlockSnapshot:input_type -> volume_server_pb.DeleteBlockSnapshotRequest
|
||||
116, // 80: volume_server_pb.VolumeServer.ListBlockSnapshots:input_type -> volume_server_pb.ListBlockSnapshotsRequest
|
||||
119, // 81: volume_server_pb.VolumeServer.ExpandBlockVolume:input_type -> volume_server_pb.ExpandBlockVolumeRequest
|
||||
121, // 82: volume_server_pb.VolumeServer.PrepareExpandBlockVolume:input_type -> volume_server_pb.PrepareExpandBlockVolumeRequest
|
||||
123, // 83: volume_server_pb.VolumeServer.CommitExpandBlockVolume:input_type -> volume_server_pb.CommitExpandBlockVolumeRequest
|
||||
125, // 84: volume_server_pb.VolumeServer.CancelExpandBlockVolume:input_type -> volume_server_pb.CancelExpandBlockVolumeRequest
|
||||
3, // 85: volume_server_pb.VolumeServer.BatchDelete:output_type -> volume_server_pb.BatchDeleteResponse
|
||||
7, // 86: volume_server_pb.VolumeServer.VacuumVolumeCheck:output_type -> volume_server_pb.VacuumVolumeCheckResponse
|
||||
9, // 87: volume_server_pb.VolumeServer.VacuumVolumeCompact:output_type -> volume_server_pb.VacuumVolumeCompactResponse
|
||||
11, // 88: volume_server_pb.VolumeServer.VacuumVolumeCommit:output_type -> volume_server_pb.VacuumVolumeCommitResponse
|
||||
13, // 89: volume_server_pb.VolumeServer.VacuumVolumeCleanup:output_type -> volume_server_pb.VacuumVolumeCleanupResponse
|
||||
15, // 90: volume_server_pb.VolumeServer.DeleteCollection:output_type -> volume_server_pb.DeleteCollectionResponse
|
||||
17, // 91: volume_server_pb.VolumeServer.AllocateVolume:output_type -> volume_server_pb.AllocateVolumeResponse
|
||||
19, // 92: volume_server_pb.VolumeServer.VolumeSyncStatus:output_type -> volume_server_pb.VolumeSyncStatusResponse
|
||||
21, // 93: volume_server_pb.VolumeServer.VolumeIncrementalCopy:output_type -> volume_server_pb.VolumeIncrementalCopyResponse
|
||||
23, // 94: volume_server_pb.VolumeServer.VolumeMount:output_type -> volume_server_pb.VolumeMountResponse
|
||||
25, // 95: volume_server_pb.VolumeServer.VolumeUnmount:output_type -> volume_server_pb.VolumeUnmountResponse
|
||||
27, // 96: volume_server_pb.VolumeServer.VolumeDelete:output_type -> volume_server_pb.VolumeDeleteResponse
|
||||
29, // 97: volume_server_pb.VolumeServer.VolumeMarkReadonly:output_type -> volume_server_pb.VolumeMarkReadonlyResponse
|
||||
31, // 98: volume_server_pb.VolumeServer.VolumeMarkWritable:output_type -> volume_server_pb.VolumeMarkWritableResponse
|
||||
33, // 99: volume_server_pb.VolumeServer.VolumeConfigure:output_type -> volume_server_pb.VolumeConfigureResponse
|
||||
35, // 100: volume_server_pb.VolumeServer.VolumeStatus:output_type -> volume_server_pb.VolumeStatusResponse
|
||||
37, // 101: volume_server_pb.VolumeServer.GetState:output_type -> volume_server_pb.GetStateResponse
|
||||
39, // 102: volume_server_pb.VolumeServer.SetState:output_type -> volume_server_pb.SetStateResponse
|
||||
41, // 103: volume_server_pb.VolumeServer.VolumeCopy:output_type -> volume_server_pb.VolumeCopyResponse
|
||||
81, // 104: volume_server_pb.VolumeServer.ReadVolumeFileStatus:output_type -> volume_server_pb.ReadVolumeFileStatusResponse
|
||||
43, // 105: volume_server_pb.VolumeServer.CopyFile:output_type -> volume_server_pb.CopyFileResponse
|
||||
46, // 106: volume_server_pb.VolumeServer.ReceiveFile:output_type -> volume_server_pb.ReceiveFileResponse
|
||||
48, // 107: volume_server_pb.VolumeServer.ReadNeedleBlob:output_type -> volume_server_pb.ReadNeedleBlobResponse
|
||||
50, // 108: volume_server_pb.VolumeServer.ReadNeedleMeta:output_type -> volume_server_pb.ReadNeedleMetaResponse
|
||||
52, // 109: volume_server_pb.VolumeServer.WriteNeedleBlob:output_type -> volume_server_pb.WriteNeedleBlobResponse
|
||||
54, // 110: volume_server_pb.VolumeServer.ReadAllNeedles:output_type -> volume_server_pb.ReadAllNeedlesResponse
|
||||
56, // 111: volume_server_pb.VolumeServer.VolumeTailSender:output_type -> volume_server_pb.VolumeTailSenderResponse
|
||||
58, // 112: volume_server_pb.VolumeServer.VolumeTailReceiver:output_type -> volume_server_pb.VolumeTailReceiverResponse
|
||||
60, // 113: volume_server_pb.VolumeServer.VolumeEcShardsGenerate:output_type -> volume_server_pb.VolumeEcShardsGenerateResponse
|
||||
62, // 114: volume_server_pb.VolumeServer.VolumeEcShardsRebuild:output_type -> volume_server_pb.VolumeEcShardsRebuildResponse
|
||||
64, // 115: volume_server_pb.VolumeServer.VolumeEcShardsCopy:output_type -> volume_server_pb.VolumeEcShardsCopyResponse
|
||||
66, // 116: volume_server_pb.VolumeServer.VolumeEcShardsDelete:output_type -> volume_server_pb.VolumeEcShardsDeleteResponse
|
||||
68, // 117: volume_server_pb.VolumeServer.VolumeEcShardsMount:output_type -> volume_server_pb.VolumeEcShardsMountResponse
|
||||
70, // 118: volume_server_pb.VolumeServer.VolumeEcShardsUnmount:output_type -> volume_server_pb.VolumeEcShardsUnmountResponse
|
||||
72, // 119: volume_server_pb.VolumeServer.VolumeEcShardRead:output_type -> volume_server_pb.VolumeEcShardReadResponse
|
||||
74, // 120: volume_server_pb.VolumeServer.VolumeEcBlobDelete:output_type -> volume_server_pb.VolumeEcBlobDeleteResponse
|
||||
76, // 121: volume_server_pb.VolumeServer.VolumeEcShardsToVolume:output_type -> volume_server_pb.VolumeEcShardsToVolumeResponse
|
||||
78, // 122: volume_server_pb.VolumeServer.VolumeEcShardsInfo:output_type -> volume_server_pb.VolumeEcShardsInfoResponse
|
||||
89, // 123: volume_server_pb.VolumeServer.VolumeTierMoveDatToRemote:output_type -> volume_server_pb.VolumeTierMoveDatToRemoteResponse
|
||||
91, // 124: volume_server_pb.VolumeServer.VolumeTierMoveDatFromRemote:output_type -> volume_server_pb.VolumeTierMoveDatFromRemoteResponse
|
||||
93, // 125: volume_server_pb.VolumeServer.VolumeServerStatus:output_type -> volume_server_pb.VolumeServerStatusResponse
|
||||
95, // 126: volume_server_pb.VolumeServer.VolumeServerLeave:output_type -> volume_server_pb.VolumeServerLeaveResponse
|
||||
97, // 127: volume_server_pb.VolumeServer.FetchAndWriteNeedle:output_type -> volume_server_pb.FetchAndWriteNeedleResponse
|
||||
99, // 128: volume_server_pb.VolumeServer.ScrubVolume:output_type -> volume_server_pb.ScrubVolumeResponse
|
||||
101, // 129: volume_server_pb.VolumeServer.ScrubEcVolume:output_type -> volume_server_pb.ScrubEcVolumeResponse
|
||||
103, // 130: volume_server_pb.VolumeServer.Query:output_type -> volume_server_pb.QueriedStripe
|
||||
105, // 131: volume_server_pb.VolumeServer.VolumeNeedleStatus:output_type -> volume_server_pb.VolumeNeedleStatusResponse
|
||||
107, // 132: volume_server_pb.VolumeServer.Ping:output_type -> volume_server_pb.PingResponse
|
||||
109, // 133: volume_server_pb.VolumeServer.AllocateBlockVolume:output_type -> volume_server_pb.AllocateBlockVolumeResponse
|
||||
111, // 134: volume_server_pb.VolumeServer.VolumeServerDeleteBlockVolume:output_type -> volume_server_pb.VolumeServerDeleteBlockVolumeResponse
|
||||
113, // 135: volume_server_pb.VolumeServer.SnapshotBlockVolume:output_type -> volume_server_pb.SnapshotBlockVolumeResponse
|
||||
115, // 136: volume_server_pb.VolumeServer.DeleteBlockSnapshot:output_type -> volume_server_pb.DeleteBlockSnapshotResponse
|
||||
117, // 137: volume_server_pb.VolumeServer.ListBlockSnapshots:output_type -> volume_server_pb.ListBlockSnapshotsResponse
|
||||
120, // 138: volume_server_pb.VolumeServer.ExpandBlockVolume:output_type -> volume_server_pb.ExpandBlockVolumeResponse
|
||||
122, // 139: volume_server_pb.VolumeServer.PrepareExpandBlockVolume:output_type -> volume_server_pb.PrepareExpandBlockVolumeResponse
|
||||
124, // 140: volume_server_pb.VolumeServer.CommitExpandBlockVolume:output_type -> volume_server_pb.CommitExpandBlockVolumeResponse
|
||||
126, // 141: volume_server_pb.VolumeServer.CancelExpandBlockVolume:output_type -> volume_server_pb.CancelExpandBlockVolumeResponse
|
||||
85, // [85:142] is the sub-list for method output_type
|
||||
28, // [28:85] is the sub-list for method input_type
|
||||
118, // 80: volume_server_pb.VolumeServer.ListBlockSnapshots:input_type -> volume_server_pb.ListBlockSnapshotsRequest
|
||||
116, // 81: volume_server_pb.VolumeServer.RestoreBlockSnapshot:input_type -> volume_server_pb.RestoreBlockSnapshotRequest
|
||||
121, // 82: volume_server_pb.VolumeServer.ExpandBlockVolume:input_type -> volume_server_pb.ExpandBlockVolumeRequest
|
||||
123, // 83: volume_server_pb.VolumeServer.PrepareExpandBlockVolume:input_type -> volume_server_pb.PrepareExpandBlockVolumeRequest
|
||||
125, // 84: volume_server_pb.VolumeServer.CommitExpandBlockVolume:input_type -> volume_server_pb.CommitExpandBlockVolumeRequest
|
||||
127, // 85: volume_server_pb.VolumeServer.CancelExpandBlockVolume:input_type -> volume_server_pb.CancelExpandBlockVolumeRequest
|
||||
3, // 86: volume_server_pb.VolumeServer.BatchDelete:output_type -> volume_server_pb.BatchDeleteResponse
|
||||
7, // 87: volume_server_pb.VolumeServer.VacuumVolumeCheck:output_type -> volume_server_pb.VacuumVolumeCheckResponse
|
||||
9, // 88: volume_server_pb.VolumeServer.VacuumVolumeCompact:output_type -> volume_server_pb.VacuumVolumeCompactResponse
|
||||
11, // 89: volume_server_pb.VolumeServer.VacuumVolumeCommit:output_type -> volume_server_pb.VacuumVolumeCommitResponse
|
||||
13, // 90: volume_server_pb.VolumeServer.VacuumVolumeCleanup:output_type -> volume_server_pb.VacuumVolumeCleanupResponse
|
||||
15, // 91: volume_server_pb.VolumeServer.DeleteCollection:output_type -> volume_server_pb.DeleteCollectionResponse
|
||||
17, // 92: volume_server_pb.VolumeServer.AllocateVolume:output_type -> volume_server_pb.AllocateVolumeResponse
|
||||
19, // 93: volume_server_pb.VolumeServer.VolumeSyncStatus:output_type -> volume_server_pb.VolumeSyncStatusResponse
|
||||
21, // 94: volume_server_pb.VolumeServer.VolumeIncrementalCopy:output_type -> volume_server_pb.VolumeIncrementalCopyResponse
|
||||
23, // 95: volume_server_pb.VolumeServer.VolumeMount:output_type -> volume_server_pb.VolumeMountResponse
|
||||
25, // 96: volume_server_pb.VolumeServer.VolumeUnmount:output_type -> volume_server_pb.VolumeUnmountResponse
|
||||
27, // 97: volume_server_pb.VolumeServer.VolumeDelete:output_type -> volume_server_pb.VolumeDeleteResponse
|
||||
29, // 98: volume_server_pb.VolumeServer.VolumeMarkReadonly:output_type -> volume_server_pb.VolumeMarkReadonlyResponse
|
||||
31, // 99: volume_server_pb.VolumeServer.VolumeMarkWritable:output_type -> volume_server_pb.VolumeMarkWritableResponse
|
||||
33, // 100: volume_server_pb.VolumeServer.VolumeConfigure:output_type -> volume_server_pb.VolumeConfigureResponse
|
||||
35, // 101: volume_server_pb.VolumeServer.VolumeStatus:output_type -> volume_server_pb.VolumeStatusResponse
|
||||
37, // 102: volume_server_pb.VolumeServer.GetState:output_type -> volume_server_pb.GetStateResponse
|
||||
39, // 103: volume_server_pb.VolumeServer.SetState:output_type -> volume_server_pb.SetStateResponse
|
||||
41, // 104: volume_server_pb.VolumeServer.VolumeCopy:output_type -> volume_server_pb.VolumeCopyResponse
|
||||
81, // 105: volume_server_pb.VolumeServer.ReadVolumeFileStatus:output_type -> volume_server_pb.ReadVolumeFileStatusResponse
|
||||
43, // 106: volume_server_pb.VolumeServer.CopyFile:output_type -> volume_server_pb.CopyFileResponse
|
||||
46, // 107: volume_server_pb.VolumeServer.ReceiveFile:output_type -> volume_server_pb.ReceiveFileResponse
|
||||
48, // 108: volume_server_pb.VolumeServer.ReadNeedleBlob:output_type -> volume_server_pb.ReadNeedleBlobResponse
|
||||
50, // 109: volume_server_pb.VolumeServer.ReadNeedleMeta:output_type -> volume_server_pb.ReadNeedleMetaResponse
|
||||
52, // 110: volume_server_pb.VolumeServer.WriteNeedleBlob:output_type -> volume_server_pb.WriteNeedleBlobResponse
|
||||
54, // 111: volume_server_pb.VolumeServer.ReadAllNeedles:output_type -> volume_server_pb.ReadAllNeedlesResponse
|
||||
56, // 112: volume_server_pb.VolumeServer.VolumeTailSender:output_type -> volume_server_pb.VolumeTailSenderResponse
|
||||
58, // 113: volume_server_pb.VolumeServer.VolumeTailReceiver:output_type -> volume_server_pb.VolumeTailReceiverResponse
|
||||
60, // 114: volume_server_pb.VolumeServer.VolumeEcShardsGenerate:output_type -> volume_server_pb.VolumeEcShardsGenerateResponse
|
||||
62, // 115: volume_server_pb.VolumeServer.VolumeEcShardsRebuild:output_type -> volume_server_pb.VolumeEcShardsRebuildResponse
|
||||
64, // 116: volume_server_pb.VolumeServer.VolumeEcShardsCopy:output_type -> volume_server_pb.VolumeEcShardsCopyResponse
|
||||
66, // 117: volume_server_pb.VolumeServer.VolumeEcShardsDelete:output_type -> volume_server_pb.VolumeEcShardsDeleteResponse
|
||||
68, // 118: volume_server_pb.VolumeServer.VolumeEcShardsMount:output_type -> volume_server_pb.VolumeEcShardsMountResponse
|
||||
70, // 119: volume_server_pb.VolumeServer.VolumeEcShardsUnmount:output_type -> volume_server_pb.VolumeEcShardsUnmountResponse
|
||||
72, // 120: volume_server_pb.VolumeServer.VolumeEcShardRead:output_type -> volume_server_pb.VolumeEcShardReadResponse
|
||||
74, // 121: volume_server_pb.VolumeServer.VolumeEcBlobDelete:output_type -> volume_server_pb.VolumeEcBlobDeleteResponse
|
||||
76, // 122: volume_server_pb.VolumeServer.VolumeEcShardsToVolume:output_type -> volume_server_pb.VolumeEcShardsToVolumeResponse
|
||||
78, // 123: volume_server_pb.VolumeServer.VolumeEcShardsInfo:output_type -> volume_server_pb.VolumeEcShardsInfoResponse
|
||||
89, // 124: volume_server_pb.VolumeServer.VolumeTierMoveDatToRemote:output_type -> volume_server_pb.VolumeTierMoveDatToRemoteResponse
|
||||
91, // 125: volume_server_pb.VolumeServer.VolumeTierMoveDatFromRemote:output_type -> volume_server_pb.VolumeTierMoveDatFromRemoteResponse
|
||||
93, // 126: volume_server_pb.VolumeServer.VolumeServerStatus:output_type -> volume_server_pb.VolumeServerStatusResponse
|
||||
95, // 127: volume_server_pb.VolumeServer.VolumeServerLeave:output_type -> volume_server_pb.VolumeServerLeaveResponse
|
||||
97, // 128: volume_server_pb.VolumeServer.FetchAndWriteNeedle:output_type -> volume_server_pb.FetchAndWriteNeedleResponse
|
||||
99, // 129: volume_server_pb.VolumeServer.ScrubVolume:output_type -> volume_server_pb.ScrubVolumeResponse
|
||||
101, // 130: volume_server_pb.VolumeServer.ScrubEcVolume:output_type -> volume_server_pb.ScrubEcVolumeResponse
|
||||
103, // 131: volume_server_pb.VolumeServer.Query:output_type -> volume_server_pb.QueriedStripe
|
||||
105, // 132: volume_server_pb.VolumeServer.VolumeNeedleStatus:output_type -> volume_server_pb.VolumeNeedleStatusResponse
|
||||
107, // 133: volume_server_pb.VolumeServer.Ping:output_type -> volume_server_pb.PingResponse
|
||||
109, // 134: volume_server_pb.VolumeServer.AllocateBlockVolume:output_type -> volume_server_pb.AllocateBlockVolumeResponse
|
||||
111, // 135: volume_server_pb.VolumeServer.VolumeServerDeleteBlockVolume:output_type -> volume_server_pb.VolumeServerDeleteBlockVolumeResponse
|
||||
113, // 136: volume_server_pb.VolumeServer.SnapshotBlockVolume:output_type -> volume_server_pb.SnapshotBlockVolumeResponse
|
||||
115, // 137: volume_server_pb.VolumeServer.DeleteBlockSnapshot:output_type -> volume_server_pb.DeleteBlockSnapshotResponse
|
||||
119, // 138: volume_server_pb.VolumeServer.ListBlockSnapshots:output_type -> volume_server_pb.ListBlockSnapshotsResponse
|
||||
117, // 139: volume_server_pb.VolumeServer.RestoreBlockSnapshot:output_type -> volume_server_pb.RestoreBlockSnapshotResponse
|
||||
122, // 140: volume_server_pb.VolumeServer.ExpandBlockVolume:output_type -> volume_server_pb.ExpandBlockVolumeResponse
|
||||
124, // 141: volume_server_pb.VolumeServer.PrepareExpandBlockVolume:output_type -> volume_server_pb.PrepareExpandBlockVolumeResponse
|
||||
126, // 142: volume_server_pb.VolumeServer.CommitExpandBlockVolume:output_type -> volume_server_pb.CommitExpandBlockVolumeResponse
|
||||
128, // 143: volume_server_pb.VolumeServer.CancelExpandBlockVolume:output_type -> volume_server_pb.CancelExpandBlockVolumeResponse
|
||||
86, // [86:144] is the sub-list for method output_type
|
||||
28, // [28:86] is the sub-list for method input_type
|
||||
28, // [28:28] is the sub-list for extension type_name
|
||||
28, // [28:28] is the sub-list for extension extendee
|
||||
0, // [0:28] is the sub-list for field type_name
|
||||
@@ -8643,7 +8741,7 @@ func file_volume_server_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_volume_server_proto_rawDesc), len(file_volume_server_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 135,
|
||||
NumMessages: 137,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc v5.29.5
|
||||
// - protoc v3.21.12
|
||||
// source: volume_server.proto
|
||||
|
||||
package volume_server_pb
|
||||
@@ -72,6 +72,7 @@ const (
|
||||
VolumeServer_SnapshotBlockVolume_FullMethodName = "/volume_server_pb.VolumeServer/SnapshotBlockVolume"
|
||||
VolumeServer_DeleteBlockSnapshot_FullMethodName = "/volume_server_pb.VolumeServer/DeleteBlockSnapshot"
|
||||
VolumeServer_ListBlockSnapshots_FullMethodName = "/volume_server_pb.VolumeServer/ListBlockSnapshots"
|
||||
VolumeServer_RestoreBlockSnapshot_FullMethodName = "/volume_server_pb.VolumeServer/RestoreBlockSnapshot"
|
||||
VolumeServer_ExpandBlockVolume_FullMethodName = "/volume_server_pb.VolumeServer/ExpandBlockVolume"
|
||||
VolumeServer_PrepareExpandBlockVolume_FullMethodName = "/volume_server_pb.VolumeServer/PrepareExpandBlockVolume"
|
||||
VolumeServer_CommitExpandBlockVolume_FullMethodName = "/volume_server_pb.VolumeServer/CommitExpandBlockVolume"
|
||||
@@ -82,7 +83,7 @@ const (
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type VolumeServerClient interface {
|
||||
// Experts only: takes multiple fid parameters. This function does not propagate deletes to replicas.
|
||||
//Experts only: takes multiple fid parameters. This function does not propagate deletes to replicas.
|
||||
BatchDelete(ctx context.Context, in *BatchDeleteRequest, opts ...grpc.CallOption) (*BatchDeleteResponse, error)
|
||||
VacuumVolumeCheck(ctx context.Context, in *VacuumVolumeCheckRequest, opts ...grpc.CallOption) (*VacuumVolumeCheckResponse, error)
|
||||
VacuumVolumeCompact(ctx context.Context, in *VacuumVolumeCompactRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[VacuumVolumeCompactResponse], error)
|
||||
@@ -143,6 +144,7 @@ type VolumeServerClient interface {
|
||||
SnapshotBlockVolume(ctx context.Context, in *SnapshotBlockVolumeRequest, opts ...grpc.CallOption) (*SnapshotBlockVolumeResponse, error)
|
||||
DeleteBlockSnapshot(ctx context.Context, in *DeleteBlockSnapshotRequest, opts ...grpc.CallOption) (*DeleteBlockSnapshotResponse, error)
|
||||
ListBlockSnapshots(ctx context.Context, in *ListBlockSnapshotsRequest, opts ...grpc.CallOption) (*ListBlockSnapshotsResponse, error)
|
||||
RestoreBlockSnapshot(ctx context.Context, in *RestoreBlockSnapshotRequest, opts ...grpc.CallOption) (*RestoreBlockSnapshotResponse, error)
|
||||
ExpandBlockVolume(ctx context.Context, in *ExpandBlockVolumeRequest, opts ...grpc.CallOption) (*ExpandBlockVolumeResponse, error)
|
||||
PrepareExpandBlockVolume(ctx context.Context, in *PrepareExpandBlockVolumeRequest, opts ...grpc.CallOption) (*PrepareExpandBlockVolumeResponse, error)
|
||||
CommitExpandBlockVolume(ctx context.Context, in *CommitExpandBlockVolumeRequest, opts ...grpc.CallOption) (*CommitExpandBlockVolumeResponse, error)
|
||||
@@ -780,6 +782,16 @@ func (c *volumeServerClient) ListBlockSnapshots(ctx context.Context, in *ListBlo
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *volumeServerClient) RestoreBlockSnapshot(ctx context.Context, in *RestoreBlockSnapshotRequest, opts ...grpc.CallOption) (*RestoreBlockSnapshotResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RestoreBlockSnapshotResponse)
|
||||
err := c.cc.Invoke(ctx, VolumeServer_RestoreBlockSnapshot_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *volumeServerClient) ExpandBlockVolume(ctx context.Context, in *ExpandBlockVolumeRequest, opts ...grpc.CallOption) (*ExpandBlockVolumeResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ExpandBlockVolumeResponse)
|
||||
@@ -824,7 +836,7 @@ func (c *volumeServerClient) CancelExpandBlockVolume(ctx context.Context, in *Ca
|
||||
// All implementations must embed UnimplementedVolumeServerServer
|
||||
// for forward compatibility.
|
||||
type VolumeServerServer interface {
|
||||
// Experts only: takes multiple fid parameters. This function does not propagate deletes to replicas.
|
||||
//Experts only: takes multiple fid parameters. This function does not propagate deletes to replicas.
|
||||
BatchDelete(context.Context, *BatchDeleteRequest) (*BatchDeleteResponse, error)
|
||||
VacuumVolumeCheck(context.Context, *VacuumVolumeCheckRequest) (*VacuumVolumeCheckResponse, error)
|
||||
VacuumVolumeCompact(*VacuumVolumeCompactRequest, grpc.ServerStreamingServer[VacuumVolumeCompactResponse]) error
|
||||
@@ -885,6 +897,7 @@ type VolumeServerServer interface {
|
||||
SnapshotBlockVolume(context.Context, *SnapshotBlockVolumeRequest) (*SnapshotBlockVolumeResponse, error)
|
||||
DeleteBlockSnapshot(context.Context, *DeleteBlockSnapshotRequest) (*DeleteBlockSnapshotResponse, error)
|
||||
ListBlockSnapshots(context.Context, *ListBlockSnapshotsRequest) (*ListBlockSnapshotsResponse, error)
|
||||
RestoreBlockSnapshot(context.Context, *RestoreBlockSnapshotRequest) (*RestoreBlockSnapshotResponse, error)
|
||||
ExpandBlockVolume(context.Context, *ExpandBlockVolumeRequest) (*ExpandBlockVolumeResponse, error)
|
||||
PrepareExpandBlockVolume(context.Context, *PrepareExpandBlockVolumeRequest) (*PrepareExpandBlockVolumeResponse, error)
|
||||
CommitExpandBlockVolume(context.Context, *CommitExpandBlockVolumeRequest) (*CommitExpandBlockVolumeResponse, error)
|
||||
@@ -1058,6 +1071,9 @@ func (UnimplementedVolumeServerServer) DeleteBlockSnapshot(context.Context, *Del
|
||||
func (UnimplementedVolumeServerServer) ListBlockSnapshots(context.Context, *ListBlockSnapshotsRequest) (*ListBlockSnapshotsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListBlockSnapshots not implemented")
|
||||
}
|
||||
func (UnimplementedVolumeServerServer) RestoreBlockSnapshot(context.Context, *RestoreBlockSnapshotRequest) (*RestoreBlockSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RestoreBlockSnapshot not implemented")
|
||||
}
|
||||
func (UnimplementedVolumeServerServer) ExpandBlockVolume(context.Context, *ExpandBlockVolumeRequest) (*ExpandBlockVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ExpandBlockVolume not implemented")
|
||||
}
|
||||
@@ -1964,6 +1980,24 @@ func _VolumeServer_ListBlockSnapshots_Handler(srv interface{}, ctx context.Conte
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _VolumeServer_RestoreBlockSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RestoreBlockSnapshotRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(VolumeServerServer).RestoreBlockSnapshot(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: VolumeServer_RestoreBlockSnapshot_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(VolumeServerServer).RestoreBlockSnapshot(ctx, req.(*RestoreBlockSnapshotRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _VolumeServer_ExpandBlockVolume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ExpandBlockVolumeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -2211,6 +2245,10 @@ var VolumeServer_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListBlockSnapshots",
|
||||
Handler: _VolumeServer_ListBlockSnapshots_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RestoreBlockSnapshot",
|
||||
Handler: _VolumeServer_RestoreBlockSnapshot_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ExpandBlockVolume",
|
||||
Handler: _VolumeServer_ExpandBlockVolume_Handler,
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 09 P4: Adversarial tests for RecoveryManager
|
||||
// ============================================================
|
||||
|
||||
// --- Adversarial 1: Rapid triple supersede ---
|
||||
|
||||
func TestAdversarial_P4_RapidTripleSupersede(t *testing.T) {
|
||||
bs, volPath := createTestBlockServiceWithVol(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
// Write data so recovery has work.
|
||||
bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 5; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
var executionCount atomic.Int32
|
||||
|
||||
rm.OnBeforeExecute = func(replicaID string) {
|
||||
executionCount.Add(1)
|
||||
// Simulate slow execution — each goroutine takes 100ms.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
makeAssignment := func(epoch uint64) []blockvol.BlockVolumeAssignment {
|
||||
return []blockvol.BlockVolumeAssignment{
|
||||
{Path: volPath, Epoch: epoch, Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2", ReplicaDataAddr: "10.0.0.2:9333", ReplicaCtrlAddr: "10.0.0.2:9334"},
|
||||
}
|
||||
}
|
||||
|
||||
// Rapid fire: epoch 1, 2, 3 in quick succession.
|
||||
// Each ProcessAssignments must drain the previous before starting the next.
|
||||
bs.ProcessAssignments(makeAssignment(1))
|
||||
time.Sleep(20 * time.Millisecond) // let goroutine start
|
||||
|
||||
bs.ProcessAssignments(makeAssignment(2))
|
||||
bs.ProcessAssignments(makeAssignment(3))
|
||||
|
||||
// Wait for all to settle.
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Must have at most 1 active task (the final one, or 0 if it completed).
|
||||
count := rm.ActiveTaskCount()
|
||||
if count > 1 {
|
||||
t.Fatalf("goroutine leak: %d active tasks after triple supersede", count)
|
||||
}
|
||||
|
||||
t.Logf("adversarial 1: triple supersede — %d executions, %d active tasks, no leak",
|
||||
executionCount.Load(), count)
|
||||
}
|
||||
|
||||
// --- Adversarial 2: Shutdown during slow goroutine (not permanently stuck) ---
|
||||
|
||||
func TestAdversarial_P4_ShutdownDuringSlow(t *testing.T) {
|
||||
bs, volPath := createTestBlockServiceWithVol(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 5; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
started := make(chan struct{}, 1)
|
||||
rm.OnBeforeExecute = func(replicaID string) {
|
||||
started <- struct{}{}
|
||||
// Simulate slow execution — 500ms, not permanently stuck.
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{Path: volPath, Epoch: 1, Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2", ReplicaDataAddr: "10.0.0.2:9333", ReplicaCtrlAddr: "10.0.0.2:9334"},
|
||||
})
|
||||
|
||||
select {
|
||||
case <-started:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("goroutine did not start")
|
||||
}
|
||||
|
||||
// Shutdown while goroutine is in slow execution.
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
rm.Shutdown()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
t.Log("adversarial 2: Shutdown completed after slow goroutine finished")
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Shutdown did not complete within 5 seconds")
|
||||
}
|
||||
|
||||
if rm.ActiveTaskCount() != 0 {
|
||||
t.Fatalf("tasks after shutdown: %d", rm.ActiveTaskCount())
|
||||
}
|
||||
}
|
||||
|
||||
// --- Adversarial 3: Two replicas, no cross-interference ---
|
||||
|
||||
func TestAdversarial_P4_TwoReplicas_NoInterference(t *testing.T) {
|
||||
bs, volPath := createTestBlockServiceWithVol(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 5; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
var execIDs []string
|
||||
var execMu = make(chan struct{}, 1)
|
||||
rm.OnBeforeExecute = func(replicaID string) {
|
||||
execMu <- struct{}{}
|
||||
execIDs = append(execIDs, replicaID)
|
||||
<-execMu
|
||||
}
|
||||
|
||||
// Two different replicas in one assignment batch.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{Path: volPath, Epoch: 1, Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2", ReplicaDataAddr: "10.0.0.2:9333", ReplicaCtrlAddr: "10.0.0.2:9334",
|
||||
ReplicaAddrs: []blockvol.ReplicaAddr{
|
||||
{DataAddr: "10.0.0.2:9333", CtrlAddr: "10.0.0.2:9334", ServerID: "vs2"},
|
||||
{DataAddr: "10.0.0.3:9333", CtrlAddr: "10.0.0.3:9334", ServerID: "vs3"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Both replicas should have gotten recovery goroutines.
|
||||
// No cross-interference: each has its own task with its own done channel.
|
||||
replicaID1 := volPath + "/vs2"
|
||||
replicaID2 := volPath + "/vs3"
|
||||
|
||||
s1 := bs.v2Orchestrator.Registry.Sender(replicaID1)
|
||||
s2 := bs.v2Orchestrator.Registry.Sender(replicaID2)
|
||||
|
||||
if s1 == nil {
|
||||
t.Fatal("sender for vs2 not created")
|
||||
}
|
||||
if s2 == nil {
|
||||
t.Fatal("sender for vs3 not created")
|
||||
}
|
||||
|
||||
t.Logf("adversarial 3: two replicas — vs2=%s vs3=%s, no cross-interference",
|
||||
s1.State(), s2.State())
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 09 P4: Stronger live runtime ownership
|
||||
//
|
||||
// Proofs:
|
||||
// 1. Live-path with real vol: ProcessAssignments → plan → executor
|
||||
// 2. Serialized replacement: old drained before new starts
|
||||
// 3. Shutdown drains all tasks
|
||||
// 4. Rebuild address scoped
|
||||
// 5. No split ownership under replacement
|
||||
// ============================================================
|
||||
|
||||
func createTestBlockServiceWithVol(t *testing.T) (*BlockService, string) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
|
||||
// Create a real blockvol.
|
||||
volPath := filepath.Join(dir, "vol1.blk")
|
||||
vol, err := blockvol.CreateBlockVol(volPath, blockvol.CreateOptions{
|
||||
VolumeSize: 1 * 1024 * 1024,
|
||||
BlockSize: 4096,
|
||||
WALSize: 256 * 1024,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVol: %v", err)
|
||||
}
|
||||
vol.Close()
|
||||
|
||||
// Build BlockService with a real BlockVolumeStore.
|
||||
store := storage.NewBlockVolumeStore()
|
||||
if _, err := store.AddBlockVolume(volPath, ""); err != nil {
|
||||
t.Fatalf("AddBlockVolume: %v", err)
|
||||
}
|
||||
|
||||
bs := &BlockService{
|
||||
blockStore: store,
|
||||
blockDir: dir,
|
||||
listenAddr: "127.0.0.1:3260",
|
||||
localServerID: "test-server-1",
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
}
|
||||
bs.v2Recovery = NewRecoveryManager(bs)
|
||||
|
||||
t.Cleanup(func() {
|
||||
bs.v2Recovery.Shutdown()
|
||||
store.Close()
|
||||
})
|
||||
|
||||
return bs, volPath
|
||||
}
|
||||
|
||||
// --- Live-path with real vol: reaches planning ---
|
||||
|
||||
func TestP4_LivePath_RealVol_ReachesPlan(t *testing.T) {
|
||||
bs, volPath := createTestBlockServiceWithVol(t)
|
||||
|
||||
// Write data to the vol so recovery has something to plan against.
|
||||
if err := bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 5; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
// Process assignment through real path.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2",
|
||||
ReplicaDataAddr: "10.0.0.2:9333",
|
||||
ReplicaCtrlAddr: "10.0.0.2:9334",
|
||||
},
|
||||
})
|
||||
|
||||
// Give recovery goroutine time to reach planning.
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify: sender exists and engine processed it.
|
||||
replicaID := volPath + "/vs2"
|
||||
s := bs.v2Orchestrator.Registry.Sender(replicaID)
|
||||
if s == nil {
|
||||
t.Fatal("sender not created")
|
||||
}
|
||||
|
||||
// Assert the full chain completed: plan → executor → in_sync.
|
||||
events := bs.v2Orchestrator.Log.EventsFor(replicaID)
|
||||
required := map[string]bool{
|
||||
"plan_catchup": false,
|
||||
"exec_catchup_started": false,
|
||||
"exec_completed": false,
|
||||
}
|
||||
for _, ev := range events {
|
||||
if _, ok := required[ev.Event]; ok {
|
||||
required[ev.Event] = true
|
||||
}
|
||||
}
|
||||
for event, found := range required {
|
||||
if !found {
|
||||
t.Fatalf("missing required event: %s (events=%d)", event, len(events))
|
||||
}
|
||||
}
|
||||
|
||||
// Assert final sender state.
|
||||
if s.State() != engine.StateInSync {
|
||||
t.Fatalf("sender state=%s, want in_sync", s.State())
|
||||
}
|
||||
|
||||
t.Log("P4 live-path: ProcessAssignments → plan_catchup → exec_catchup_started → exec_completed → in_sync")
|
||||
}
|
||||
|
||||
// --- Serialized replacement: old drained before new starts ---
|
||||
|
||||
func TestP4_SerializedReplacement_DrainsBeforeStart(t *testing.T) {
|
||||
bs, volPath := createTestBlockServiceWithVol(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
// Write data so recovery has work to do.
|
||||
if err := bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 10; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
// Hook: block the first recovery goroutine so it is DEFINITELY still
|
||||
// alive when the supersede arrives. Release it via channel.
|
||||
holdFirst := make(chan struct{})
|
||||
firstReached := make(chan struct{}, 1)
|
||||
callCount := 0
|
||||
rm.OnBeforeExecute = func(replicaID string) {
|
||||
callCount++
|
||||
if callCount == 1 {
|
||||
// First call: signal that we're alive, then block.
|
||||
firstReached <- struct{}{}
|
||||
<-holdFirst // block until test releases
|
||||
}
|
||||
// Second call (replacement): proceed immediately.
|
||||
}
|
||||
|
||||
replicaID := volPath + "/vs2"
|
||||
|
||||
// Epoch 1: start recovery goroutine (will block at OnBeforeExecute).
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{Path: volPath, Epoch: 1, Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2", ReplicaDataAddr: "10.0.0.2:9333", ReplicaCtrlAddr: "10.0.0.2:9334"},
|
||||
})
|
||||
|
||||
// Wait for the first goroutine to reach the hook (still alive).
|
||||
select {
|
||||
case <-firstReached:
|
||||
t.Log("first recovery goroutine is alive and blocked at OnBeforeExecute")
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("first goroutine did not reach OnBeforeExecute")
|
||||
}
|
||||
|
||||
// Capture the old task's done channel WHILE it is still running.
|
||||
rm.mu.Lock()
|
||||
oldTask := rm.tasks[replicaID]
|
||||
rm.mu.Unlock()
|
||||
if oldTask == nil {
|
||||
t.Fatal("old task must exist while goroutine is blocked")
|
||||
}
|
||||
oldDone := oldTask.done
|
||||
|
||||
// Verify: old done channel is NOT closed yet.
|
||||
select {
|
||||
case <-oldDone:
|
||||
t.Fatal("old task done should NOT be closed yet")
|
||||
default:
|
||||
t.Log("confirmed: old task still running (done channel open)")
|
||||
}
|
||||
|
||||
// Epoch 2: supersede. cancelAndDrain will cancel the old context and
|
||||
// block on oldDone. We release the hold from another goroutine so
|
||||
// cancelAndDrain can complete.
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
close(holdFirst) // release the blocked first goroutine
|
||||
}()
|
||||
|
||||
// This call blocks inside cancelAndDrain until the old goroutine exits.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{Path: volPath, Epoch: 2, Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2", ReplicaDataAddr: "10.0.0.2:9333", ReplicaCtrlAddr: "10.0.0.2:9334"},
|
||||
})
|
||||
|
||||
// After ProcessAssignments returns, the old goroutine MUST be drained.
|
||||
select {
|
||||
case <-oldDone:
|
||||
t.Log("confirmed: old task drained (done channel closed) before replacement started")
|
||||
default:
|
||||
t.Fatal("old task done channel still open after ProcessAssignments returned")
|
||||
}
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// At most 1 active task.
|
||||
count := rm.ActiveTaskCount()
|
||||
if count > 1 {
|
||||
t.Fatalf("overlap: %d tasks after replacement", count)
|
||||
}
|
||||
|
||||
t.Log("P4 serialized: old goroutine blocked → supersede → drain waited → old exited → replacement started")
|
||||
}
|
||||
|
||||
// --- Shutdown drains all ---
|
||||
|
||||
func TestP4_ShutdownDrain(t *testing.T) {
|
||||
bs, volPath := createTestBlockServiceWithVol(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
// Hook: block the goroutine so it's alive when shutdown arrives.
|
||||
holdTask := make(chan struct{})
|
||||
taskReached := make(chan struct{}, 1)
|
||||
rm.OnBeforeExecute = func(replicaID string) {
|
||||
taskReached <- struct{}{}
|
||||
<-holdTask
|
||||
}
|
||||
|
||||
// Write data so recovery has work.
|
||||
if err := bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 5; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{Path: volPath, Epoch: 1, Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "vs2", ReplicaDataAddr: "10.0.0.2:9333", ReplicaCtrlAddr: "10.0.0.2:9334"},
|
||||
})
|
||||
|
||||
// Wait for task to be alive.
|
||||
select {
|
||||
case <-taskReached:
|
||||
t.Log("task is alive and blocked before shutdown")
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("task did not reach hook")
|
||||
}
|
||||
|
||||
// Assert: a live task exists.
|
||||
if rm.ActiveTaskCount() == 0 {
|
||||
t.Fatal("expected live task before shutdown")
|
||||
}
|
||||
|
||||
// Release the blocked goroutine from another goroutine so Shutdown can drain.
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
close(holdTask)
|
||||
}()
|
||||
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
rm.Shutdown()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Shutdown did not complete within 5 seconds")
|
||||
}
|
||||
|
||||
if rm.ActiveTaskCount() != 0 {
|
||||
t.Fatalf("expected 0 active tasks, got %d", rm.ActiveTaskCount())
|
||||
}
|
||||
|
||||
t.Log("P4 shutdown: live task existed → shutdown drained it → 0 active")
|
||||
}
|
||||
|
||||
// --- Rebuild address scoped ---
|
||||
|
||||
func TestP4_RebuildAddrScoped(t *testing.T) {
|
||||
bs, _ := createTestBlockServiceWithVol(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
assignments := []blockvol.BlockVolumeAssignment{
|
||||
{Path: "/data/vol1.blk", RebuildAddr: "10.0.0.1:5000"},
|
||||
{Path: "/data/vol2.blk", RebuildAddr: "10.0.0.2:5000"},
|
||||
}
|
||||
|
||||
if addr := rm.deriveRebuildAddr("/data/vol1.blk/vs2", assignments); addr != "10.0.0.1:5000" {
|
||||
t.Fatalf("vol1 addr=%s", addr)
|
||||
}
|
||||
if addr := rm.deriveRebuildAddr("/data/vol2.blk/vs3", assignments); addr != "10.0.0.2:5000" {
|
||||
t.Fatalf("vol2 addr=%s", addr)
|
||||
}
|
||||
if addr := rm.deriveRebuildAddr("/data/vol3.blk/vs4", assignments); addr != "" {
|
||||
t.Fatalf("vol3 addr=%s", addr)
|
||||
}
|
||||
}
|
||||
@@ -1127,6 +1127,12 @@ func (r *BlockVolumeRegistry) evaluatePromotionLocked(entry *BlockVolumeEntry) P
|
||||
freshnessCutoff = 60 * time.Second
|
||||
}
|
||||
primaryLSN := entry.WALHeadLSN
|
||||
// EC-6 fix: when the primary is dead, its last-reported WALHeadLSN
|
||||
// includes entries that were fsync'd locally but never shipped.
|
||||
// The replica can never catch up because the primary is gone.
|
||||
// Skip the WAL LSN gate so the best available replica is promoted,
|
||||
// accepting that the last few unshipped entries may be lost.
|
||||
primaryAlive := r.blockServers[entry.VolumeServer] != nil
|
||||
|
||||
bestIdx := -1
|
||||
for i := range entry.Replicas {
|
||||
@@ -1149,8 +1155,11 @@ func (r *BlockVolumeRegistry) evaluatePromotionLocked(entry *BlockVolumeEntry) P
|
||||
})
|
||||
continue
|
||||
}
|
||||
// Gate 2: WAL LSN recency (skip if primary LSN is 0 — no data yet, all eligible).
|
||||
if primaryLSN > 0 && ri.WALHeadLSN+r.promotionLSNTolerance < primaryLSN {
|
||||
// Gate 2: WAL LSN recency.
|
||||
// Skip if primary LSN is 0 (no data yet — all eligible).
|
||||
// EC-6 fix: also skip if primary is dead — its LSN is stale and
|
||||
// the replica can never catch up. Promote the best available.
|
||||
if primaryAlive && primaryLSN > 0 && ri.WALHeadLSN+r.promotionLSNTolerance < primaryLSN {
|
||||
result.Rejections = append(result.Rejections, PromotionRejection{
|
||||
Server: ri.Server,
|
||||
Reason: "wal_lag",
|
||||
|
||||
@@ -870,9 +870,12 @@ func TestRegistry_PromoteBestReplica_StaleHeartbeatIneligible(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fix #2: Replica with WAL lag too large is not eligible.
|
||||
// Fix #2: Replica with WAL lag too large is not eligible (when primary alive).
|
||||
// EC-6 fix: WAL lag gate only applies when primary is alive.
|
||||
func TestRegistry_PromoteBestReplica_WALLagIneligible(t *testing.T) {
|
||||
r := NewBlockVolumeRegistry()
|
||||
r.MarkBlockCapable("primary") // primary must be alive for WAL lag gate
|
||||
r.MarkBlockCapable("lagging") // replica must be alive for other gates to pass
|
||||
r.Register(&BlockVolumeEntry{
|
||||
Name: "vol1",
|
||||
VolumeServer: "primary",
|
||||
@@ -968,8 +971,10 @@ func TestRegistry_PromoteBestReplica_EligibilityFiltersCorrectly(t *testing.T) {
|
||||
}
|
||||
|
||||
// Configurable tolerance: widen tolerance to allow lagging replicas.
|
||||
// EC-6 fix: WAL lag gate only applies when primary is alive.
|
||||
func TestRegistry_PromoteBestReplica_ConfigurableTolerance(t *testing.T) {
|
||||
r := NewBlockVolumeRegistry()
|
||||
r.MarkBlockCapable("primary") // primary must be alive for WAL lag gate to apply
|
||||
r.MarkBlockCapable("lagging")
|
||||
r.Register(&BlockVolumeEntry{
|
||||
Name: "vol1",
|
||||
|
||||
@@ -167,16 +167,19 @@ func (ms *MasterServer) CreateBlockVolume(ctx context.Context, req *master_pb.Cr
|
||||
LeaseTtlMs: leaseTTLMs,
|
||||
}
|
||||
// CP8-2: populate ReplicaAddrs for multi-replica.
|
||||
// V2 P10-1: include stable ServerID from registry.
|
||||
for _, ri := range entry.Replicas {
|
||||
primaryAssignment.ReplicaAddrs = append(primaryAssignment.ReplicaAddrs, blockvol.ReplicaAddr{
|
||||
DataAddr: ri.DataAddr,
|
||||
CtrlAddr: ri.CtrlAddr,
|
||||
ServerID: ri.Server, // V2: stable identity = registry VS address
|
||||
})
|
||||
}
|
||||
// Backward compat: also set scalar fields if exactly 1 replica.
|
||||
if len(entry.Replicas) == 1 {
|
||||
primaryAssignment.ReplicaDataAddr = entry.Replicas[0].DataAddr
|
||||
primaryAssignment.ReplicaCtrlAddr = entry.Replicas[0].CtrlAddr
|
||||
primaryAssignment.ReplicaServerID = entry.Replicas[0].Server // V2: scalar stable ID
|
||||
}
|
||||
ms.blockAssignmentQueue.Enqueue(server, primaryAssignment)
|
||||
|
||||
@@ -368,6 +371,25 @@ func (ms *MasterServer) ListBlockSnapshots(ctx context.Context, req *master_pb.L
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// RestoreBlockSnapshot restores a block volume to the specified snapshot.
|
||||
// This is a destructive operation: all writes after the snapshot are lost.
|
||||
func (ms *MasterServer) RestoreBlockSnapshot(ctx context.Context, req *master_pb.RestoreBlockSnapshotRequest) (*master_pb.RestoreBlockSnapshotResponse, error) {
|
||||
if req.VolumeName == "" {
|
||||
return nil, fmt.Errorf("volume_name is required")
|
||||
}
|
||||
|
||||
entry, ok := ms.blockRegistry.Lookup(req.VolumeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("block volume %q not found", req.VolumeName)
|
||||
}
|
||||
|
||||
if err := ms.blockVSRestore(ctx, entry.VolumeServer, req.VolumeName, req.SnapshotId); err != nil {
|
||||
return nil, fmt.Errorf("restore snapshot on %s: %w", entry.VolumeServer, err)
|
||||
}
|
||||
|
||||
return &master_pb.RestoreBlockSnapshotResponse{}, nil
|
||||
}
|
||||
|
||||
// ExpandBlockVolume expands a block volume. For standalone volumes (no replicas),
|
||||
// uses direct expand. For replicated volumes, uses coordinated prepare/commit/cancel.
|
||||
func (ms *MasterServer) ExpandBlockVolume(ctx context.Context, req *master_pb.ExpandBlockVolumeRequest) (*master_pb.ExpandBlockVolumeResponse, error) {
|
||||
|
||||
@@ -104,6 +104,7 @@ type MasterServer struct {
|
||||
blockVSSnapshot func(ctx context.Context, server string, name string, snapID uint32) (int64, uint64, error)
|
||||
blockVSDeleteSnap func(ctx context.Context, server string, name string, snapID uint32) error
|
||||
blockVSListSnaps func(ctx context.Context, server string, name string) ([]*volume_server_pb.BlockSnapshotInfo, error)
|
||||
blockVSRestore func(ctx context.Context, server string, name string, snapID uint32) error
|
||||
blockVSExpand func(ctx context.Context, server string, name string, newSize uint64) (uint64, error)
|
||||
blockVSPrepareExpand func(ctx context.Context, server string, name string, newSize, expandEpoch uint64) error
|
||||
blockVSCommitExpand func(ctx context.Context, server string, name string, expandEpoch uint64) (uint64, error)
|
||||
@@ -172,6 +173,7 @@ func NewMasterServer(r *mux.Router, option *MasterOption, peers map[string]pb.Se
|
||||
ms.blockVSSnapshot = ms.defaultBlockVSSnapshot
|
||||
ms.blockVSDeleteSnap = ms.defaultBlockVSDeleteSnap
|
||||
ms.blockVSListSnaps = ms.defaultBlockVSListSnaps
|
||||
ms.blockVSRestore = ms.defaultBlockVSRestore
|
||||
ms.blockVSExpand = ms.defaultBlockVSExpand
|
||||
ms.blockVSPrepareExpand = ms.defaultBlockVSPrepareExpand
|
||||
ms.blockVSCommitExpand = ms.defaultBlockVSCommitExpand
|
||||
@@ -650,6 +652,16 @@ func (ms *MasterServer) defaultBlockVSListSnaps(ctx context.Context, server stri
|
||||
return infos, err
|
||||
}
|
||||
|
||||
func (ms *MasterServer) defaultBlockVSRestore(ctx context.Context, server string, name string, snapID uint32) error {
|
||||
return operation.WithVolumeServerClient(false, pb.ServerAddress(server), ms.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error {
|
||||
_, err := client.RestoreBlockSnapshot(ctx, &volume_server_pb.RestoreBlockSnapshotRequest{
|
||||
Name: name,
|
||||
SnapshotId: snapID,
|
||||
})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (ms *MasterServer) defaultBlockVSExpand(ctx context.Context, server string, name string, newSize uint64) (uint64, error) {
|
||||
var capacity uint64
|
||||
err := operation.WithVolumeServerClient(false, pb.ServerAddress(server), ms.grpcDialOption, func(client volume_server_pb.VolumeServerClient) error {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 10 P4: Adversarial — stale epoch on PROMOTED volume
|
||||
//
|
||||
// Corrected: targets the promoted BS + promoted volume path,
|
||||
// matching the real failover test shape.
|
||||
// ============================================================
|
||||
|
||||
func TestAdversarial_P10P4_StaleEpochOnPromotedVolume(t *testing.T) {
|
||||
s := newP4Setup(t)
|
||||
|
||||
// Step 1: create volume at epoch 1 (primary=vs1, replica=vs2).
|
||||
ctx := context.Background()
|
||||
createResp, err := s.ms.CreateBlockVolume(ctx, &master_pb.CreateBlockVolumeRequest{
|
||||
Name: "pvc-stale-2",
|
||||
SizeBytes: 1 << 20,
|
||||
ReplicaFactor: 2,
|
||||
DurabilityMode: "sync_all",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
primaryVS := createResp.VolumeServer
|
||||
t.Logf("created: primary=%s", primaryVS)
|
||||
|
||||
// Deliver epoch 1 to primary BS.
|
||||
s.deliverAssignments(primaryVS)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Step 2: failover → epoch 2, new primary is the old replica.
|
||||
s.ms.blockRegistry.UpdateEntry("pvc-stale-2", func(e *BlockVolumeEntry) {
|
||||
e.LastLeaseGrant = time.Now().Add(-1 * time.Minute)
|
||||
})
|
||||
s.ms.failoverBlockVolumes(primaryVS)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
entryAfter, ok := s.ms.blockRegistry.Lookup("pvc-stale-2")
|
||||
if !ok {
|
||||
t.Fatal("volume not found after failover")
|
||||
}
|
||||
if entryAfter.Epoch != 2 {
|
||||
t.Fatalf("registry epoch: %d, want 2", entryAfter.Epoch)
|
||||
}
|
||||
newPrimary := entryAfter.VolumeServer
|
||||
t.Logf("after failover: new primary=%s epoch=2", newPrimary)
|
||||
|
||||
// Step 3: create a SEPARATE promoted BlockService (same as P4 failover test).
|
||||
// This is the promoted VS — it has its own store with the promoted volume.
|
||||
sanitized := strings.ReplaceAll(newPrimary, ":", "_")
|
||||
promotedDir := filepath.Join(s.dir, sanitized+"_promoted")
|
||||
promotedStore := storage.NewBlockVolumeStore()
|
||||
volPath := filepath.Join(s.dir, sanitized, "pvc-stale-2.blk")
|
||||
if _, err := promotedStore.AddBlockVolume(volPath, ""); err != nil {
|
||||
t.Fatalf("add promoted vol: %v", err)
|
||||
}
|
||||
promotedBS := &BlockService{
|
||||
blockStore: promotedStore,
|
||||
blockDir: promotedDir,
|
||||
listenAddr: "127.0.0.1:3260",
|
||||
localServerID: newPrimary,
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
replStates: make(map[string]*volReplState),
|
||||
}
|
||||
promotedBS.v2Recovery = NewRecoveryManager(promotedBS)
|
||||
t.Cleanup(func() {
|
||||
promotedBS.v2Recovery.Shutdown()
|
||||
promotedStore.Close()
|
||||
})
|
||||
|
||||
// Deliver epoch 2 assignment to promoted BS (not s.bs).
|
||||
pending := s.ms.blockAssignmentQueue.Peek(newPrimary)
|
||||
if len(pending) > 0 {
|
||||
protoAssignments := blockvol.AssignmentsToProto(pending)
|
||||
goAssignments := blockvol.AssignmentsFromProto(protoAssignments)
|
||||
promotedBS.ProcessAssignments(goAssignments)
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify: promoted vol has epoch 2.
|
||||
var promotedEpoch uint64
|
||||
promotedStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
promotedEpoch = vol.Epoch()
|
||||
return nil
|
||||
})
|
||||
if promotedEpoch != 2 {
|
||||
t.Fatalf("promoted vol epoch: %d, want 2", promotedEpoch)
|
||||
}
|
||||
|
||||
// Step 4: deliver STALE epoch 1 to the PROMOTED BS.
|
||||
staleAssignment := blockvol.BlockVolumeAssignment{
|
||||
Path: volPath,
|
||||
Epoch: 1, // STALE — lower than current epoch 2
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
}
|
||||
promotedBS.ProcessAssignments([]blockvol.BlockVolumeAssignment{staleAssignment})
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Step 5: verify NOT reverted on the PROMOTED volume.
|
||||
var afterStale uint64
|
||||
promotedStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
afterStale = vol.Epoch()
|
||||
return nil
|
||||
})
|
||||
if afterStale < 2 {
|
||||
t.Fatalf("BUG: promoted vol epoch reverted from 2 to %d after stale delivery", afterStale)
|
||||
}
|
||||
|
||||
t.Logf("adversarial: stale epoch 1 on promoted vol → epoch stays at %d (HandleAssignment rejects regression)", afterStale)
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
||||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 10 P4: Master-driven control-loop closure
|
||||
//
|
||||
// These tests use REAL master assignment production (CreateBlockVolume,
|
||||
// failoverBlockVolumes) with the allocator wired to produce paths that
|
||||
// match REAL registered block volumes in the test BlockService.
|
||||
// ============================================================
|
||||
|
||||
// p4Setup creates a master + BlockService where the master's allocator
|
||||
// produces paths matching real volumes registered in the BlockService's store.
|
||||
type p4Setup struct {
|
||||
ms *MasterServer
|
||||
bs *BlockService
|
||||
store *storage.BlockVolumeStore
|
||||
dir string
|
||||
volSeq int
|
||||
}
|
||||
|
||||
func newP4Setup(t *testing.T) *p4Setup {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
store := storage.NewBlockVolumeStore()
|
||||
|
||||
bs := &BlockService{
|
||||
blockStore: store,
|
||||
blockDir: dir,
|
||||
listenAddr: "127.0.0.1:3260",
|
||||
localServerID: "vs1:9333",
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
replStates: make(map[string]*volReplState),
|
||||
}
|
||||
bs.v2Recovery = NewRecoveryManager(bs)
|
||||
|
||||
ms := &MasterServer{
|
||||
blockRegistry: NewBlockVolumeRegistry(),
|
||||
blockAssignmentQueue: NewBlockAssignmentQueue(),
|
||||
blockFailover: newBlockFailoverState(),
|
||||
}
|
||||
ms.blockRegistry.MarkBlockCapable("vs1:9333")
|
||||
ms.blockRegistry.MarkBlockCapable("vs2:9333")
|
||||
|
||||
setup := &p4Setup{ms: ms, bs: bs, store: store, dir: dir}
|
||||
|
||||
// Wire allocator to create REAL block volumes at per-server paths.
|
||||
ms.blockVSAllocate = func(ctx context.Context, server string, name string, sizeBytes uint64, diskType string, durabilityMode string) (*blockAllocResult, error) {
|
||||
// Per-server subdir (sanitize colons for Windows).
|
||||
sanitized := strings.ReplaceAll(server, ":", "_")
|
||||
serverDir := filepath.Join(dir, sanitized)
|
||||
if err := os.MkdirAll(serverDir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
volPath := filepath.Join(serverDir, fmt.Sprintf("%s.blk", name))
|
||||
vol, err := blockvol.CreateBlockVol(volPath, blockvol.CreateOptions{
|
||||
VolumeSize: 1 * 1024 * 1024,
|
||||
BlockSize: 4096,
|
||||
WALSize: 256 * 1024,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.Close()
|
||||
// Register ALL volumes in the shared store (single-process test
|
||||
// simulates both primary and promoted-replica VS).
|
||||
if _, err := store.AddBlockVolume(volPath, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blockAllocResult{
|
||||
Path: volPath,
|
||||
IQN: fmt.Sprintf("iqn.2024.test:%s", name),
|
||||
ISCSIAddr: server + ":3260",
|
||||
ReplicaDataAddr: server + ":14260",
|
||||
ReplicaCtrlAddr: server + ":14261",
|
||||
RebuildListenAddr: server + ":15000",
|
||||
}, nil
|
||||
}
|
||||
ms.blockVSDelete = func(ctx context.Context, server string, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
bs.v2Recovery.Shutdown()
|
||||
store.Close()
|
||||
})
|
||||
|
||||
return setup
|
||||
}
|
||||
|
||||
// deliverAssignments simulates heartbeat delivery: queue → proto → decode → ProcessAssignments.
|
||||
func (s *p4Setup) deliverAssignments(server string) int {
|
||||
pending := s.ms.blockAssignmentQueue.Peek(server)
|
||||
if len(pending) == 0 {
|
||||
return 0
|
||||
}
|
||||
protoAssignments := blockvol.AssignmentsToProto(pending)
|
||||
goAssignments := blockvol.AssignmentsFromProto(protoAssignments)
|
||||
s.bs.ProcessAssignments(goAssignments)
|
||||
return len(goAssignments)
|
||||
}
|
||||
|
||||
// --- 1. Real master create → full delivery with real volume ---
|
||||
|
||||
func TestP10P4_MasterCreate_FullDelivery(t *testing.T) {
|
||||
s := newP4Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
createResp, err := s.ms.CreateBlockVolume(ctx, &master_pb.CreateBlockVolumeRequest{
|
||||
Name: "pvc-data-1",
|
||||
SizeBytes: 1 << 30,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVolume: %v", err)
|
||||
}
|
||||
primaryVS := createResp.VolumeServer
|
||||
t.Logf("master created: primary=%s replica=%s", primaryVS, createResp.ReplicaServer)
|
||||
|
||||
// Deliver master-produced assignments.
|
||||
n := s.deliverAssignments(primaryVS)
|
||||
if n == 0 {
|
||||
t.Fatal("no assignments delivered")
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify: engine has sender with stable ReplicaID.
|
||||
entry, _ := s.ms.blockRegistry.Lookup("pvc-data-1")
|
||||
expectedID := entry.Path + "/" + entry.Replicas[0].Server
|
||||
sender := s.bs.v2Orchestrator.Registry.Sender(expectedID)
|
||||
if sender == nil {
|
||||
t.Fatalf("sender not found: %s", expectedID)
|
||||
}
|
||||
|
||||
// Verify: V1 HandleAssignment succeeded (vol exists in store).
|
||||
var volEpoch uint64
|
||||
if err := s.store.WithVolume(entry.Path, func(vol *blockvol.BlockVol) error {
|
||||
volEpoch = vol.Epoch()
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("volume not accessible in store: %v", err)
|
||||
}
|
||||
if volEpoch == 0 {
|
||||
t.Fatal("vol epoch should be set after HandleAssignment")
|
||||
}
|
||||
|
||||
// Verify: heartbeat reports the assigned volume.
|
||||
msgs := s.bs.CollectBlockVolumeHeartbeat()
|
||||
found := false
|
||||
for _, m := range msgs {
|
||||
if m.Path == entry.Path {
|
||||
found = true
|
||||
if m.Epoch != entry.Epoch {
|
||||
t.Fatalf("heartbeat epoch=%d, want %d", m.Epoch, entry.Epoch)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("assigned volume %s not in heartbeat", entry.Path)
|
||||
}
|
||||
|
||||
t.Logf("P10P4 create: master → queue → proto → VS → sender(%s) + vol(epoch=%d) + heartbeat", expectedID, volEpoch)
|
||||
}
|
||||
|
||||
// --- 2. Real master failover → delivery → convergence ---
|
||||
|
||||
func TestP10P4_MasterFailover_Convergence(t *testing.T) {
|
||||
s := newP4Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
createResp, err := s.ms.CreateBlockVolume(ctx, &master_pb.CreateBlockVolumeRequest{
|
||||
Name: "pvc-data-2",
|
||||
SizeBytes: 1 << 30,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVolume: %v", err)
|
||||
}
|
||||
primaryVS := createResp.VolumeServer
|
||||
replicaVS := createResp.ReplicaServer
|
||||
t.Logf("created: primary=%s replica=%s", primaryVS, replicaVS)
|
||||
|
||||
// Deliver initial assignment to primary BS.
|
||||
n1 := s.deliverAssignments(primaryVS)
|
||||
if n1 == 0 {
|
||||
t.Fatal("no initial assignments delivered")
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
entry, _ := s.ms.blockRegistry.Lookup("pvc-data-2")
|
||||
|
||||
// Expire lease, then failover.
|
||||
s.ms.blockRegistry.UpdateEntry("pvc-data-2", func(e *BlockVolumeEntry) {
|
||||
e.LastLeaseGrant = time.Now().Add(-1 * time.Minute)
|
||||
})
|
||||
s.ms.failoverBlockVolumes(primaryVS)
|
||||
|
||||
entryAfter, _ := s.ms.blockRegistry.Lookup("pvc-data-2")
|
||||
if entryAfter.Epoch != 2 {
|
||||
t.Fatalf("epoch=%d, want 2", entryAfter.Epoch)
|
||||
}
|
||||
newPrimary := entryAfter.VolumeServer
|
||||
t.Logf("after failover: new primary=%s epoch=%d", newPrimary, entryAfter.Epoch)
|
||||
|
||||
// Build a SEPARATE BlockService for the promoted replica VS.
|
||||
// This simulates the promoted VS having its own store and heartbeat.
|
||||
replicaStore := storage.NewBlockVolumeStore()
|
||||
// Find and register the replica's vol path.
|
||||
sanitized := strings.ReplaceAll(replicaVS, ":", "_")
|
||||
replicaVolPath := filepath.Join(s.dir, sanitized, "pvc-data-2.blk")
|
||||
if _, err := replicaStore.AddBlockVolume(replicaVolPath, ""); err != nil {
|
||||
t.Fatalf("register replica vol: %v", err)
|
||||
}
|
||||
promotedBS := &BlockService{
|
||||
blockStore: replicaStore,
|
||||
blockDir: s.dir,
|
||||
listenAddr: "127.0.0.1:3261",
|
||||
localServerID: newPrimary,
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
replStates: make(map[string]*volReplState),
|
||||
}
|
||||
promotedBS.v2Recovery = NewRecoveryManager(promotedBS)
|
||||
t.Cleanup(func() {
|
||||
promotedBS.v2Recovery.Shutdown()
|
||||
replicaStore.Close()
|
||||
})
|
||||
|
||||
// Deliver failover assignment to the promoted BS through proto path.
|
||||
failoverPending := s.ms.blockAssignmentQueue.Peek(newPrimary)
|
||||
if len(failoverPending) == 0 {
|
||||
t.Fatal("no failover assignment in queue")
|
||||
}
|
||||
fa := failoverPending[0]
|
||||
if fa.Epoch != 2 {
|
||||
t.Fatalf("failover assignment epoch=%d, want 2", fa.Epoch)
|
||||
}
|
||||
|
||||
protoAssignments := blockvol.AssignmentsToProto(failoverPending)
|
||||
goAssignments := blockvol.AssignmentsFromProto(protoAssignments)
|
||||
promotedBS.ProcessAssignments(goAssignments)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify: HandleAssignment succeeded on the promoted vol (epoch=2).
|
||||
var promotedEpoch uint64
|
||||
if err := replicaStore.WithVolume(fa.Path, func(vol *blockvol.BlockVol) error {
|
||||
promotedEpoch = vol.Epoch()
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("promoted vol not accessible: %v", err)
|
||||
}
|
||||
if promotedEpoch != 2 {
|
||||
t.Fatalf("promoted vol epoch=%d, want 2", promotedEpoch)
|
||||
}
|
||||
|
||||
// Verify: promoted VS's OWN heartbeat reports the vol at epoch 2.
|
||||
// This is the promoted VS's independent heartbeat, not the shared store.
|
||||
promotedMsgs := promotedBS.CollectBlockVolumeHeartbeat()
|
||||
foundPromoted := false
|
||||
for _, m := range promotedMsgs {
|
||||
if m.Path == fa.Path && m.Epoch == 2 {
|
||||
foundPromoted = true
|
||||
}
|
||||
}
|
||||
if !foundPromoted {
|
||||
t.Fatal("promoted VS heartbeat should show vol at epoch 2")
|
||||
}
|
||||
|
||||
// Verify: promoted VS heartbeat does NOT contain the old primary's vol.
|
||||
_ = entry // reference to initial entry
|
||||
for _, m := range promotedMsgs {
|
||||
if m.Path == entry.Path && m.Path != fa.Path {
|
||||
t.Fatalf("promoted VS heartbeat should NOT contain old primary vol %s", entry.Path)
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("P10P4 failover: create → failover(epoch=2) → separate promoted BS → vol(epoch=2) + own heartbeat")
|
||||
}
|
||||
|
||||
// --- 3. Identity preservation through master-produced path ---
|
||||
|
||||
func TestP10P4_IdentityPreservation(t *testing.T) {
|
||||
s := newP4Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := s.ms.CreateBlockVolume(ctx, &master_pb.CreateBlockVolumeRequest{
|
||||
Name: "pvc-data-3",
|
||||
SizeBytes: 1 << 30,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVolume: %v", err)
|
||||
}
|
||||
|
||||
entry, _ := s.ms.blockRegistry.Lookup("pvc-data-3")
|
||||
|
||||
// Verify master-produced assignment carries stable ServerID.
|
||||
pending := s.ms.blockAssignmentQueue.Peek(entry.VolumeServer)
|
||||
hasStableID := false
|
||||
for _, a := range pending {
|
||||
if a.ReplicaServerID != "" {
|
||||
hasStableID = true
|
||||
}
|
||||
for _, ra := range a.ReplicaAddrs {
|
||||
if ra.ServerID != "" {
|
||||
hasStableID = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasStableID {
|
||||
t.Fatal("master assignment missing stable ServerID")
|
||||
}
|
||||
|
||||
// Deliver and verify engine uses stable ID, not address.
|
||||
s.deliverAssignments(entry.VolumeServer)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
for _, ri := range entry.Replicas {
|
||||
stableID := entry.Path + "/" + ri.Server
|
||||
addressID := entry.Path + "/" + ri.DataAddr
|
||||
if s.bs.v2Orchestrator.Registry.Sender(stableID) == nil {
|
||||
t.Fatalf("stable sender %s not found", stableID)
|
||||
}
|
||||
if s.bs.v2Orchestrator.Registry.Sender(addressID) != nil {
|
||||
t.Fatalf("address-derived sender %s should NOT exist", addressID)
|
||||
}
|
||||
}
|
||||
|
||||
t.Log("P10P4 identity: master-produced stable ID preserved through full delivery")
|
||||
}
|
||||
|
||||
// --- 4. Repeated delivery idempotence ---
|
||||
|
||||
func TestP10P4_RepeatedDelivery_Idempotence(t *testing.T) {
|
||||
s := newP4Setup(t)
|
||||
ctx := context.Background()
|
||||
|
||||
_, err := s.ms.CreateBlockVolume(ctx, &master_pb.CreateBlockVolumeRequest{
|
||||
Name: "pvc-data-4",
|
||||
SizeBytes: 1 << 30,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVolume: %v", err)
|
||||
}
|
||||
|
||||
entry, _ := s.ms.blockRegistry.Lookup("pvc-data-4")
|
||||
primaryVS := entry.VolumeServer
|
||||
replicaID := entry.Path + "/" + entry.Replicas[0].Server
|
||||
|
||||
// First delivery.
|
||||
s.deliverAssignments(primaryVS)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
eventsAfterFirst := len(s.bs.v2Orchestrator.Log.EventsFor(replicaID))
|
||||
if eventsAfterFirst == 0 {
|
||||
t.Fatal("first delivery must create engine events (guard against vacuous pass)")
|
||||
}
|
||||
t.Logf("first delivery: %d events", eventsAfterFirst)
|
||||
|
||||
// Second delivery (Peek returns same assignments).
|
||||
s.deliverAssignments(primaryVS)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
eventsAfterSecond := len(s.bs.v2Orchestrator.Log.EventsFor(replicaID))
|
||||
if eventsAfterSecond != eventsAfterFirst {
|
||||
t.Fatalf("idempotence broken: events %d → %d", eventsAfterFirst, eventsAfterSecond)
|
||||
}
|
||||
|
||||
t.Logf("P10P4 idempotent: repeated delivery → events stable at %d (guard: >0)", eventsAfterFirst)
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 10 P2: Adversarial convergence tests
|
||||
// ============================================================
|
||||
|
||||
// --- Adversarial 1: Rapid triple reassignment (vs2 → vs3 → vs4) ---
|
||||
|
||||
func TestAdversarial_P10P2_RapidTripleReassignment(t *testing.T) {
|
||||
bs, volPath := createP2BlockService(t)
|
||||
|
||||
makeAssignment := func(epoch uint64, serverID, dataAddr string) []blockvol.BlockVolumeAssignment {
|
||||
return []blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: epoch,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: serverID,
|
||||
ReplicaDataAddr: dataAddr,
|
||||
ReplicaCtrlAddr: dataAddr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Three rapid reassignments.
|
||||
bs.ProcessAssignments(makeAssignment(1, "vs2-node:18080", "10.0.0.2:14260"))
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
bs.ProcessAssignments(makeAssignment(2, "vs3-node:18080", "10.0.0.3:14260"))
|
||||
bs.ProcessAssignments(makeAssignment(3, "vs4-node:18080", "10.0.0.4:14260"))
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Only vs4 should exist in engine.
|
||||
vs2ID := volPath + "/vs2-node:18080"
|
||||
vs3ID := volPath + "/vs3-node:18080"
|
||||
vs4ID := volPath + "/vs4-node:18080"
|
||||
|
||||
if bs.v2Orchestrator.Registry.Sender(vs2ID) != nil {
|
||||
t.Fatal("vs2 sender should be removed")
|
||||
}
|
||||
if bs.v2Orchestrator.Registry.Sender(vs3ID) != nil {
|
||||
t.Fatal("vs3 sender should be removed")
|
||||
}
|
||||
if bs.v2Orchestrator.Registry.Sender(vs4ID) == nil {
|
||||
t.Fatal("vs4 sender should exist")
|
||||
}
|
||||
|
||||
// Runtime: no stale tasks for vs2 or vs3.
|
||||
bs.v2Recovery.mu.Lock()
|
||||
_, hasVs2 := bs.v2Recovery.tasks[vs2ID]
|
||||
_, hasVs3 := bs.v2Recovery.tasks[vs3ID]
|
||||
bs.v2Recovery.mu.Unlock()
|
||||
|
||||
if hasVs2 || hasVs3 {
|
||||
t.Fatalf("stale runtime tasks: vs2=%v vs3=%v", hasVs2, hasVs3)
|
||||
}
|
||||
|
||||
// Heartbeat: must report vs4 address.
|
||||
msgs := bs.CollectBlockVolumeHeartbeat()
|
||||
hb := findHeartbeatMsg(msgs, volPath)
|
||||
if hb == nil {
|
||||
t.Fatal("volume not in heartbeat")
|
||||
}
|
||||
if hb.ReplicaDataAddr != "10.0.0.4:14260" {
|
||||
t.Fatalf("heartbeat addr=%s, want 10.0.0.4:14260", hb.ReplicaDataAddr)
|
||||
}
|
||||
|
||||
t.Log("adversarial 1: rapid triple reassignment → only vs4 in all truth surfaces")
|
||||
}
|
||||
|
||||
// --- Adversarial 2: Same replica, address change (epoch bump) ---
|
||||
|
||||
func TestAdversarial_P10P2_SameReplica_AddressChange(t *testing.T) {
|
||||
bs, volPath := createP2BlockService(t)
|
||||
|
||||
replicaID := volPath + "/vs2-node:18080"
|
||||
|
||||
// Epoch 1: vs2 at address 10.0.0.2.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
senderBefore := bs.v2Orchestrator.Registry.Sender(replicaID)
|
||||
if senderBefore == nil {
|
||||
t.Fatal("sender should exist at epoch 1")
|
||||
}
|
||||
|
||||
// Epoch 2: SAME ServerID, DIFFERENT address (replica restarted on new IP).
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 2,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.99:14260", // different IP
|
||||
ReplicaCtrlAddr: "10.0.0.99:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Same sender identity (pointer).
|
||||
senderAfter := bs.v2Orchestrator.Registry.Sender(replicaID)
|
||||
if senderAfter == nil {
|
||||
t.Fatal("sender should still exist")
|
||||
}
|
||||
if senderAfter != senderBefore {
|
||||
t.Fatal("sender identity must be preserved (same pointer) across address change")
|
||||
}
|
||||
|
||||
// Endpoint updated.
|
||||
if senderAfter.Endpoint().DataAddr != "10.0.0.99:14260" {
|
||||
t.Fatalf("endpoint not updated: %s", senderAfter.Endpoint().DataAddr)
|
||||
}
|
||||
|
||||
// Heartbeat: new address.
|
||||
msgs := bs.CollectBlockVolumeHeartbeat()
|
||||
hb := findHeartbeatMsg(msgs, volPath)
|
||||
if hb == nil {
|
||||
t.Fatal("volume not in heartbeat")
|
||||
}
|
||||
if hb.ReplicaDataAddr != "10.0.0.99:14260" {
|
||||
t.Fatalf("heartbeat addr=%s, want 10.0.0.99:14260", hb.ReplicaDataAddr)
|
||||
}
|
||||
|
||||
// No duplicate sender.
|
||||
count := 0
|
||||
for _, s := range bs.v2Orchestrator.Registry.All() {
|
||||
if s.ReplicaID() == replicaID {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("duplicate senders: %d for %s", count, replicaID)
|
||||
}
|
||||
|
||||
t.Log("adversarial 2: same replica, address change → identity preserved, endpoint updated, no duplicate")
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 10 P2: Reassignment / result convergence
|
||||
//
|
||||
// Proofs:
|
||||
// 1. Reassignment convergence: epoch bump updates control truth
|
||||
// 2. Stale owner removal: old sender/session gone after reassignment
|
||||
// 3. Reported truth: heartbeat reflects new replica assignment
|
||||
// 4. No split truth: ingress + runtime + reported all agree
|
||||
// ============================================================
|
||||
|
||||
func createP2BlockService(t *testing.T) (*BlockService, string) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
|
||||
volPath := filepath.Join(dir, "vol1.blk")
|
||||
vol, err := blockvol.CreateBlockVol(volPath, blockvol.CreateOptions{
|
||||
VolumeSize: 1 * 1024 * 1024,
|
||||
BlockSize: 4096,
|
||||
WALSize: 256 * 1024,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVol: %v", err)
|
||||
}
|
||||
vol.Close()
|
||||
|
||||
store := storage.NewBlockVolumeStore()
|
||||
if _, err := store.AddBlockVolume(volPath, ""); err != nil {
|
||||
t.Fatalf("AddBlockVolume: %v", err)
|
||||
}
|
||||
|
||||
bs := &BlockService{
|
||||
blockStore: store,
|
||||
blockDir: dir,
|
||||
listenAddr: "127.0.0.1:3260",
|
||||
localServerID: "vs1-node:18080",
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
replStates: make(map[string]*volReplState),
|
||||
}
|
||||
bs.v2Recovery = NewRecoveryManager(bs)
|
||||
|
||||
t.Cleanup(func() {
|
||||
bs.v2Recovery.Shutdown()
|
||||
store.Close()
|
||||
})
|
||||
|
||||
return bs, volPath
|
||||
}
|
||||
|
||||
// --- 1. Reassignment convergence: epoch bump updates control truth ---
|
||||
|
||||
func TestP10P2_ReassignmentConvergence_EpochBump(t *testing.T) {
|
||||
bs, volPath := createP2BlockService(t)
|
||||
|
||||
// Epoch 1: primary with replica vs2.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
replicaID1 := volPath + "/vs2-node:18080"
|
||||
s1 := bs.v2Orchestrator.Registry.Sender(replicaID1)
|
||||
if s1 == nil {
|
||||
t.Fatal("epoch 1: sender for vs2 not created")
|
||||
}
|
||||
epoch1Session := s1.SessionID()
|
||||
t.Logf("epoch 1: sender=%s sessionID=%d", replicaID1, epoch1Session)
|
||||
|
||||
// Epoch 2: primary with DIFFERENT replica vs3.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 2,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs3-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.3:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.3:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// New replica sender should exist.
|
||||
replicaID2 := volPath + "/vs3-node:18080"
|
||||
s2 := bs.v2Orchestrator.Registry.Sender(replicaID2)
|
||||
if s2 == nil {
|
||||
t.Fatal("epoch 2: sender for vs3 not created")
|
||||
}
|
||||
t.Logf("epoch 2: sender=%s sessionID=%d", replicaID2, s2.SessionID())
|
||||
|
||||
// Old replica sender should be gone (removed by Reconcile).
|
||||
s1After := bs.v2Orchestrator.Registry.Sender(replicaID1)
|
||||
if s1After != nil {
|
||||
t.Fatalf("epoch 2: old sender %s should be removed", replicaID1)
|
||||
}
|
||||
|
||||
t.Log("P10P2 convergence: epoch bump → old replica removed, new replica active")
|
||||
}
|
||||
|
||||
// --- 2. Stale owner removal: live old recovery cancelled during reassignment ---
|
||||
|
||||
func TestP10P2_StaleOwnerRemoval(t *testing.T) {
|
||||
bs, volPath := createP2BlockService(t)
|
||||
rm := bs.v2Recovery
|
||||
|
||||
// Write data so recovery has real work (not zero-gap auto-complete).
|
||||
if err := bs.blockStore.WithVolume(volPath, func(vol *blockvol.BlockVol) error {
|
||||
for i := 0; i < 5; i++ {
|
||||
vol.WriteLBA(uint64(i), make([]byte, 4096))
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
// Hook: block the first recovery goroutine so it's alive during reassignment.
|
||||
holdFirst := make(chan struct{})
|
||||
firstReached := make(chan struct{}, 1)
|
||||
callCount := 0
|
||||
rm.OnBeforeExecute = func(replicaID string) {
|
||||
callCount++
|
||||
if callCount == 1 {
|
||||
firstReached <- struct{}{}
|
||||
<-holdFirst // block until released
|
||||
}
|
||||
}
|
||||
|
||||
// Epoch 1: start recovery for vs2 (will block at hook).
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
},
|
||||
})
|
||||
|
||||
// Wait for old goroutine to be alive.
|
||||
select {
|
||||
case <-firstReached:
|
||||
t.Log("old recovery goroutine for vs2 is alive and blocked")
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("old goroutine did not reach hook")
|
||||
}
|
||||
|
||||
// Capture old task's done channel.
|
||||
oldReplicaID := volPath + "/vs2-node:18080"
|
||||
rm.mu.Lock()
|
||||
oldTask := rm.tasks[oldReplicaID]
|
||||
rm.mu.Unlock()
|
||||
if oldTask == nil {
|
||||
t.Fatal("old task must exist while goroutine is blocked")
|
||||
}
|
||||
oldDone := oldTask.done
|
||||
|
||||
// Release old goroutine from another goroutine.
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
close(holdFirst)
|
||||
}()
|
||||
|
||||
// Epoch 2: reassignment to vs3. cancelAndDrain waits for old to exit.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 2,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs3-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.3:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.3:14261",
|
||||
},
|
||||
})
|
||||
|
||||
// Old goroutine must be drained.
|
||||
select {
|
||||
case <-oldDone:
|
||||
t.Log("confirmed: old recovery goroutine drained during reassignment")
|
||||
default:
|
||||
t.Fatal("old done channel still open after reassignment")
|
||||
}
|
||||
|
||||
// Old task must be gone.
|
||||
rm.mu.Lock()
|
||||
_, hasOld := rm.tasks[oldReplicaID]
|
||||
rm.mu.Unlock()
|
||||
if hasOld {
|
||||
t.Fatal("stale recovery task should be gone")
|
||||
}
|
||||
|
||||
t.Log("P10P2 stale removal: live old owner cancelled + drained during reassignment")
|
||||
}
|
||||
|
||||
// --- 3. Reported truth: actual heartbeat output reflects new replica ---
|
||||
|
||||
func TestP10P2_ReportedTruth_HeartbeatConverges(t *testing.T) {
|
||||
bs, volPath := createP2BlockService(t)
|
||||
|
||||
// Epoch 1: primary with vs2.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Verify actual heartbeat output reports vs2.
|
||||
msgs1 := bs.CollectBlockVolumeHeartbeat()
|
||||
found1 := findHeartbeatMsg(msgs1, volPath)
|
||||
if found1 == nil {
|
||||
t.Fatal("epoch 1: volume not in heartbeat output")
|
||||
}
|
||||
if found1.ReplicaDataAddr != "10.0.0.2:14260" {
|
||||
t.Fatalf("epoch 1 heartbeat: ReplicaDataAddr=%q, want 10.0.0.2:14260", found1.ReplicaDataAddr)
|
||||
}
|
||||
t.Logf("epoch 1 heartbeat: ReplicaDataAddr=%s ✓", found1.ReplicaDataAddr)
|
||||
|
||||
// Epoch 2: primary with vs3.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 2,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs3-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.3:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.3:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Actual heartbeat output should report vs3, NOT stale vs2.
|
||||
msgs2 := bs.CollectBlockVolumeHeartbeat()
|
||||
found2 := findHeartbeatMsg(msgs2, volPath)
|
||||
if found2 == nil {
|
||||
t.Fatal("epoch 2: volume not in heartbeat output")
|
||||
}
|
||||
if found2.ReplicaDataAddr != "10.0.0.3:14260" {
|
||||
t.Fatalf("epoch 2 heartbeat: ReplicaDataAddr=%q, want 10.0.0.3:14260 (stale vs2)", found2.ReplicaDataAddr)
|
||||
}
|
||||
if found2.ReplicaCtrlAddr != "10.0.0.3:14261" {
|
||||
t.Fatalf("epoch 2 heartbeat: ReplicaCtrlAddr=%q, want 10.0.0.3:14261", found2.ReplicaCtrlAddr)
|
||||
}
|
||||
|
||||
t.Logf("P10P2 reported truth: CollectBlockVolumeHeartbeat converged from vs2 → vs3")
|
||||
}
|
||||
|
||||
func findHeartbeatMsg(msgs []blockvol.BlockVolumeInfoMessage, path string) *blockvol.BlockVolumeInfoMessage {
|
||||
for i := range msgs {
|
||||
if msgs[i].Path == path {
|
||||
return &msgs[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- 4. No split truth: ingress + runtime + reported all agree ---
|
||||
|
||||
func TestP10P2_NoSplitTruth(t *testing.T) {
|
||||
bs, volPath := createP2BlockService(t)
|
||||
|
||||
// Epoch 1: primary with vs2.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Epoch 2: primary with vs3.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 2,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs3-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.3:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.3:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Check all three truth surfaces agree on vs3:
|
||||
|
||||
// 1. Engine (assignment ingress): sender for vs3 exists, vs2 gone.
|
||||
vs3ID := volPath + "/vs3-node:18080"
|
||||
vs2ID := volPath + "/vs2-node:18080"
|
||||
|
||||
if bs.v2Orchestrator.Registry.Sender(vs3ID) == nil {
|
||||
t.Fatal("engine: vs3 sender missing")
|
||||
}
|
||||
if bs.v2Orchestrator.Registry.Sender(vs2ID) != nil {
|
||||
t.Fatal("engine: vs2 sender should be removed")
|
||||
}
|
||||
|
||||
// 2. Runtime (recovery manager): no task for vs2.
|
||||
bs.v2Recovery.mu.Lock()
|
||||
_, hasVs2Task := bs.v2Recovery.tasks[vs2ID]
|
||||
bs.v2Recovery.mu.Unlock()
|
||||
if hasVs2Task {
|
||||
t.Fatal("runtime: stale vs2 recovery task exists")
|
||||
}
|
||||
|
||||
// 3. Reported (heartbeat output): addresses match vs3.
|
||||
msgs := bs.CollectBlockVolumeHeartbeat()
|
||||
hb := findHeartbeatMsg(msgs, volPath)
|
||||
if hb == nil {
|
||||
t.Fatal("reported: volume not in heartbeat")
|
||||
}
|
||||
if hb.ReplicaDataAddr != "10.0.0.3:14260" {
|
||||
t.Fatalf("reported: ReplicaDataAddr=%q, want 10.0.0.3:14260", hb.ReplicaDataAddr)
|
||||
}
|
||||
|
||||
t.Log("P10P2 no-split-truth: engine(vs3) + runtime(no vs2 task) + heartbeat(vs3 addr) — all converged")
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 10 P3: Bounded repeated-assignment idempotence
|
||||
//
|
||||
// Proofs:
|
||||
// 1. Unchanged repeated assignment is idempotent (no rebuild-server relisten)
|
||||
// 2. Changed assignment still triggers replacement (P2 guard)
|
||||
// 3. No duplicate runtime side effects after repeated assignment
|
||||
// ============================================================
|
||||
|
||||
func createP3BlockService(t *testing.T) (*BlockService, string) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
|
||||
volPath := filepath.Join(dir, "vol1.blk")
|
||||
vol, err := blockvol.CreateBlockVol(volPath, blockvol.CreateOptions{
|
||||
VolumeSize: 1 * 1024 * 1024,
|
||||
BlockSize: 4096,
|
||||
WALSize: 256 * 1024,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateBlockVol: %v", err)
|
||||
}
|
||||
vol.Close()
|
||||
|
||||
store := storage.NewBlockVolumeStore()
|
||||
if _, err := store.AddBlockVolume(volPath, ""); err != nil {
|
||||
t.Fatalf("AddBlockVolume: %v", err)
|
||||
}
|
||||
|
||||
bs := &BlockService{
|
||||
blockStore: store,
|
||||
blockDir: dir,
|
||||
listenAddr: "127.0.0.1:3260",
|
||||
localServerID: "vs1-node:18080",
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
replStates: make(map[string]*volReplState),
|
||||
}
|
||||
bs.v2Recovery = NewRecoveryManager(bs)
|
||||
|
||||
t.Cleanup(func() {
|
||||
bs.v2Recovery.Shutdown()
|
||||
store.Close()
|
||||
})
|
||||
|
||||
return bs, volPath
|
||||
}
|
||||
|
||||
// --- 1. Unchanged repeated assignment is idempotent ---
|
||||
|
||||
func TestP10P3_RepeatedAssignment_Idempotent(t *testing.T) {
|
||||
bs, volPath := createP3BlockService(t)
|
||||
|
||||
assignment := blockvol.BlockVolumeAssignment{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
}
|
||||
|
||||
// First assignment: sets up replication + engine session + recovery.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{assignment})
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Capture V2 engine event count after first assignment.
|
||||
replicaID := volPath + "/vs2-node:18080"
|
||||
eventsAfterFirst := len(bs.v2Orchestrator.Log.EventsFor(replicaID))
|
||||
t.Logf("first assignment: %d engine events", eventsAfterFirst)
|
||||
|
||||
// Second assignment: identical. Must be fully idempotent — no V2 engine
|
||||
// processing, no new sessions, no recovery starts.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{assignment})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
eventsAfterSecond := len(bs.v2Orchestrator.Log.EventsFor(replicaID))
|
||||
if eventsAfterSecond != eventsAfterFirst {
|
||||
t.Fatalf("V2 engine events grew: %d → %d (repeated assignment not idempotent)",
|
||||
eventsAfterFirst, eventsAfterSecond)
|
||||
}
|
||||
|
||||
// Third assignment: same again. Still no new events.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{assignment})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
eventsAfterThird := len(bs.v2Orchestrator.Log.EventsFor(replicaID))
|
||||
if eventsAfterThird != eventsAfterFirst {
|
||||
t.Fatalf("V2 engine events grew on third: %d → %d", eventsAfterFirst, eventsAfterThird)
|
||||
}
|
||||
|
||||
// V1 state also unchanged.
|
||||
dataAddr, _ := bs.GetReplState(volPath)
|
||||
if dataAddr != "10.0.0.2:14260" {
|
||||
t.Fatalf("replState: %q", dataAddr)
|
||||
}
|
||||
|
||||
t.Logf("P10P3 idempotent: 3x same → V2 events stable at %d, V1 state unchanged", eventsAfterFirst)
|
||||
}
|
||||
|
||||
// --- 2. Changed assignment triggers replacement (P2 guard) ---
|
||||
|
||||
func TestP10P3_ChangedAssignment_StillReplaces(t *testing.T) {
|
||||
bs, volPath := createP3BlockService(t)
|
||||
|
||||
// First assignment: vs2.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
dataAddr1, _ := bs.GetReplState(volPath)
|
||||
if dataAddr1 != "10.0.0.2:14260" {
|
||||
t.Fatalf("epoch 1: dataAddr=%q", dataAddr1)
|
||||
}
|
||||
|
||||
// Second assignment: CHANGED replica (vs3). Must NOT be idempotent.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: volPath,
|
||||
Epoch: 2,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs3-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.3:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.3:14261",
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Verify: state updated to vs3.
|
||||
dataAddr2, _ := bs.GetReplState(volPath)
|
||||
if dataAddr2 != "10.0.0.3:14260" {
|
||||
t.Fatalf("epoch 2: dataAddr=%q, want 10.0.0.3:14260 (should have replaced)", dataAddr2)
|
||||
}
|
||||
|
||||
// Engine should have vs3, not vs2.
|
||||
vs3ID := volPath + "/vs3-node:18080"
|
||||
vs2ID := volPath + "/vs2-node:18080"
|
||||
if bs.v2Orchestrator.Registry.Sender(vs3ID) == nil {
|
||||
t.Fatal("vs3 sender should exist")
|
||||
}
|
||||
if bs.v2Orchestrator.Registry.Sender(vs2ID) != nil {
|
||||
t.Fatal("vs2 sender should be removed")
|
||||
}
|
||||
|
||||
t.Log("P10P3 guard: changed assignment still triggers full replacement")
|
||||
}
|
||||
|
||||
// --- 3. Heartbeat coherent after repeated assignment ---
|
||||
|
||||
func TestP10P3_HeartbeatCoherent_AfterRepeated(t *testing.T) {
|
||||
bs, volPath := createP3BlockService(t)
|
||||
|
||||
assignment := blockvol.BlockVolumeAssignment{
|
||||
Path: volPath,
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerID: "vs2-node:18080",
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
}
|
||||
|
||||
// Apply 3 times.
|
||||
for i := 0; i < 3; i++ {
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{assignment})
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Heartbeat should report vs2 exactly once (no duplicates in output).
|
||||
msgs := bs.CollectBlockVolumeHeartbeat()
|
||||
count := 0
|
||||
for _, m := range msgs {
|
||||
if m.Path == volPath {
|
||||
count++
|
||||
if m.ReplicaDataAddr != "10.0.0.2:14260" {
|
||||
t.Fatalf("heartbeat: ReplicaDataAddr=%q", m.ReplicaDataAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("heartbeat: volume appeared %d times, want 1", count)
|
||||
}
|
||||
|
||||
t.Log("P10P3 heartbeat: coherent after 3x repeated assignment")
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package weed_server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
||||
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/v2bridge"
|
||||
|
||||
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// Phase 10 P1: Stable identity on the real control wire
|
||||
//
|
||||
// Proofs:
|
||||
// 1. Real ingress: proto wire → decode → ProcessAssignments → ControlBridge → ReplicaID
|
||||
// 2. Canonical local identity: non-default serverID flows through block/control path
|
||||
// 3. Fail-closed: missing ServerID on proto wire → replica skipped
|
||||
// ============================================================
|
||||
|
||||
// --- Real ingress proof: proto wire → engine ReplicaID ---
|
||||
|
||||
func TestP10P1_RealIngress_ProtoToReplicaID(t *testing.T) {
|
||||
// Simulate the real control ingress chain:
|
||||
// 1. Master builds proto assignment with stable ServerID
|
||||
// 2. Proto is encoded (as it would be over gRPC wire)
|
||||
// 3. Volume server decodes proto → Go assignment
|
||||
// 4. ProcessAssignments → ControlBridge → engine
|
||||
// 5. Engine has sender with correct ReplicaID = <path>/<ServerID>
|
||||
|
||||
// Step 1: Master builds proto (simulates master_grpc_server_block.go).
|
||||
protoAssignment := &master_pb.BlockVolumeAssignment{
|
||||
Path: "pvc-vol-1",
|
||||
Epoch: 5,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
LeaseTtlMs: 30000,
|
||||
ReplicaServerId: "vs2-node.cluster:18080", // stable ID from registry
|
||||
ReplicaDataAddr: "10.0.0.2:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.2:14261",
|
||||
ReplicaAddrs: []*master_pb.ReplicaAddrMessage{
|
||||
{
|
||||
DataAddr: "10.0.0.2:14260",
|
||||
CtrlAddr: "10.0.0.2:14261",
|
||||
ServerId: "vs2-node.cluster:18080",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Step 2: Proto round-trip (simulates gRPC wire encode/decode).
|
||||
// In real gRPC, this is automatic. Here we verify the Go struct
|
||||
// carries the fields after proto-to-Go conversion.
|
||||
goAssignment := blockvol.AssignmentFromProto(protoAssignment)
|
||||
|
||||
// Verify: Go wire type preserves the stable ID.
|
||||
if goAssignment.ReplicaServerID != "vs2-node.cluster:18080" {
|
||||
t.Fatalf("scalar ReplicaServerID=%q, want vs2-node.cluster:18080", goAssignment.ReplicaServerID)
|
||||
}
|
||||
if len(goAssignment.ReplicaAddrs) != 1 || goAssignment.ReplicaAddrs[0].ServerID != "vs2-node.cluster:18080" {
|
||||
t.Fatalf("multi-replica ServerID not preserved")
|
||||
}
|
||||
|
||||
// Step 3-4: ProcessAssignments → ControlBridge → engine.
|
||||
bs := &BlockService{
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
localServerID: "vs1-node.cluster:18080",
|
||||
}
|
||||
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{goAssignment})
|
||||
|
||||
// Step 5: Verify engine sender has the correct stable ReplicaID.
|
||||
expectedReplicaID := "pvc-vol-1/vs2-node.cluster:18080"
|
||||
sender := bs.v2Orchestrator.Registry.Sender(expectedReplicaID)
|
||||
if sender == nil {
|
||||
t.Fatalf("engine sender not found for %s", expectedReplicaID)
|
||||
}
|
||||
if !sender.HasActiveSession() {
|
||||
t.Fatal("sender should have active session")
|
||||
}
|
||||
|
||||
t.Logf("P10P1 real ingress: proto(ServerId=%s) → decode → ProcessAssignments → sender(%s) with session",
|
||||
"vs2-node.cluster:18080", expectedReplicaID)
|
||||
}
|
||||
|
||||
// --- Canonical local identity: non-default serverID ---
|
||||
|
||||
func TestP10P1_CanonicalLocalIdentity(t *testing.T) {
|
||||
// Prove that when the volume server has a canonical ID different from
|
||||
// plain ip:port, the block/control path uses that canonical ID.
|
||||
|
||||
canonicalID := "my-custom-volume-server-id"
|
||||
|
||||
bs := &BlockService{
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
localServerID: canonicalID, // explicitly NOT ip:port shaped
|
||||
}
|
||||
|
||||
// Process a replica assignment that targets THIS volume server.
|
||||
// The ControlBridge uses localServerID for replica/rebuild assignments.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: "pvc-vol-2",
|
||||
Epoch: 3,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaServerID: "other-server-id",
|
||||
ReplicaDataAddr: "10.0.0.2:9333",
|
||||
ReplicaCtrlAddr: "10.0.0.2:9334",
|
||||
},
|
||||
})
|
||||
|
||||
// The sender should use the ServerID from the assignment, not localServerID.
|
||||
sender := bs.v2Orchestrator.Registry.Sender("pvc-vol-2/other-server-id")
|
||||
if sender == nil {
|
||||
t.Fatal("sender should exist with ServerID from assignment, not from localServerID")
|
||||
}
|
||||
|
||||
// Now process a replica assignment where THIS server is the replica.
|
||||
// The ControlBridge uses localServerID for the local ReplicaID.
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: "pvc-vol-3",
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RoleReplica),
|
||||
ReplicaDataAddr: "10.0.0.1:14260",
|
||||
ReplicaCtrlAddr: "10.0.0.1:14261",
|
||||
},
|
||||
})
|
||||
|
||||
// For a replica assignment, ControlBridge builds ReplicaID as <path>/<localServerID>.
|
||||
expectedLocalReplicaID := "pvc-vol-3/" + canonicalID
|
||||
localSender := bs.v2Orchestrator.Registry.Sender(expectedLocalReplicaID)
|
||||
if localSender == nil {
|
||||
t.Fatalf("local replica sender not found for %s — canonical ID not used", expectedLocalReplicaID)
|
||||
}
|
||||
|
||||
t.Logf("P10P1 canonical identity: localServerID=%q → local ReplicaID=%s", canonicalID, expectedLocalReplicaID)
|
||||
}
|
||||
|
||||
// --- Fail-closed: missing ServerID on proto wire ---
|
||||
|
||||
func TestP10P1_FailClosed_MissingServerID(t *testing.T) {
|
||||
// When the proto assignment has addresses but no ServerID,
|
||||
// the ControlBridge must skip that replica (fail closed).
|
||||
|
||||
bs := &BlockService{
|
||||
v2Bridge: v2bridge.NewControlBridge(),
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
localServerID: "vs1:18080",
|
||||
}
|
||||
|
||||
bs.ProcessAssignments([]blockvol.BlockVolumeAssignment{
|
||||
{
|
||||
Path: "pvc-vol-4",
|
||||
Epoch: 1,
|
||||
Role: uint32(blockvol.RolePrimary),
|
||||
ReplicaDataAddr: "10.0.0.2:9333",
|
||||
ReplicaCtrlAddr: "10.0.0.2:9334",
|
||||
// ReplicaServerID intentionally empty — should be skipped.
|
||||
},
|
||||
})
|
||||
|
||||
// No sender should exist — the replica was skipped due to missing ServerID.
|
||||
// The ControlBridge logs "scalar replica assignment without ServerID" and skips.
|
||||
// Check that no sender exists with an address-derived ID.
|
||||
addressDerivedID := "pvc-vol-4/10.0.0.2:9333"
|
||||
if bs.v2Orchestrator.Registry.Sender(addressDerivedID) != nil {
|
||||
t.Fatal("address-derived sender should NOT exist — missing ServerID must fail closed")
|
||||
}
|
||||
|
||||
t.Log("P10P1 fail-closed: missing ServerID → replica skipped, no address-derived fallback")
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
type volReplState struct {
|
||||
replicaDataAddr string
|
||||
replicaCtrlAddr string
|
||||
// allReplicas stores the full replica set for multi-replica idempotence.
|
||||
allReplicas []blockvol.ReplicaAddr
|
||||
}
|
||||
|
||||
// NVMeConfig holds NVMe/TCP target configuration passed from CLI flags.
|
||||
@@ -51,6 +53,11 @@ type BlockService struct {
|
||||
// V2 engine bridge (Phase 08 P1).
|
||||
v2Bridge *v2bridge.ControlBridge
|
||||
v2Orchestrator *engine.RecoveryOrchestrator
|
||||
v2Recovery *RecoveryManager
|
||||
|
||||
// P3: last-applied assignment per volume path for idempotence.
|
||||
lastAssignMu sync.RWMutex
|
||||
lastAssign map[string]lastAppliedAssignment
|
||||
// localServerID: stable identity for this volume server.
|
||||
// INTERIM: uses listenAddr (transport-shaped). Should be replaced
|
||||
// with a registry-assigned stable server ID in a later hardening pass.
|
||||
@@ -62,6 +69,13 @@ func (bs *BlockService) V2Orchestrator() *engine.RecoveryOrchestrator {
|
||||
return bs.v2Orchestrator
|
||||
}
|
||||
|
||||
// SetServerID sets the stable server identity for V2 control semantics.
|
||||
// Should be called with the gRPC address that the master knows this VS by,
|
||||
// replacing the interim listenAddr-based identity.
|
||||
func (bs *BlockService) SetServerID(id string) {
|
||||
bs.localServerID = id
|
||||
}
|
||||
|
||||
// WireStateChangeNotify sets up shipper state change callbacks on all
|
||||
// registered volumes so that degradation/recovery triggers an immediate
|
||||
// heartbeat via the provided channel. Non-blocking send (buffered chan 1).
|
||||
@@ -103,6 +117,7 @@ func StartBlockService(listenAddr, blockDir, iqnPrefix, portalAddr string, nvmeC
|
||||
v2Orchestrator: engine.NewRecoveryOrchestrator(),
|
||||
localServerID: listenAddr, // INTERIM: transport-shaped, see field doc
|
||||
}
|
||||
bs.v2Recovery = NewRecoveryManager(bs)
|
||||
|
||||
// iSCSI target setup.
|
||||
logger := log.New(os.Stderr, "iscsi: ", log.LstdFlags)
|
||||
@@ -347,13 +362,27 @@ func (bs *BlockService) DeleteBlockVol(name string) error {
|
||||
// V2 bridge: also delivers each assignment to the V2 engine for recovery ownership.
|
||||
func (bs *BlockService) ProcessAssignments(assignments []blockvol.BlockVolumeAssignment) {
|
||||
// V2 bridge: convert and deliver to engine orchestrator (Phase 08 P1).
|
||||
// P3: skip V2 processing for repeated unchanged assignments.
|
||||
// P4: RecoveryManager starts/cancels recovery goroutines based on results.
|
||||
if bs.v2Bridge != nil && bs.v2Orchestrator != nil {
|
||||
for _, a := range assignments {
|
||||
// P3 idempotence: skip V2 processing if this assignment is
|
||||
// materially unchanged from the last one applied for this path.
|
||||
if bs.isAssignmentUnchanged(a) {
|
||||
continue
|
||||
}
|
||||
bs.recordAppliedAssignment(a)
|
||||
|
||||
intent := bs.v2Bridge.ConvertAssignment(a, bs.localServerID)
|
||||
result := bs.v2Orchestrator.ProcessAssignment(intent)
|
||||
glog.V(1).Infof("v2bridge: assignment %s epoch=%d → added=%d removed=%d sessions=%d",
|
||||
a.Path, a.Epoch, len(result.Added), len(result.Removed),
|
||||
len(result.SessionsCreated)+len(result.SessionsSuperseded))
|
||||
|
||||
// P4: drive live recovery execution based on engine result.
|
||||
if bs.v2Recovery != nil && (len(result.SessionsCreated) > 0 || len(result.SessionsSuperseded) > 0 || len(result.Removed) > 0) {
|
||||
bs.v2Recovery.HandleAssignmentResult(result, assignments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +426,15 @@ func (bs *BlockService) ProcessAssignments(assignments []blockvol.BlockVolumeAss
|
||||
// setupPrimaryReplication configures WAL shipping from primary to replica
|
||||
// and starts the rebuild server (R1-2).
|
||||
func (bs *BlockService) setupPrimaryReplication(path, replicaDataAddr, replicaCtrlAddr string) {
|
||||
// P3 idempotence: skip if replica state is unchanged.
|
||||
bs.replMu.RLock()
|
||||
existing := bs.replStates[path]
|
||||
bs.replMu.RUnlock()
|
||||
if existing != nil && existing.replicaDataAddr == replicaDataAddr && existing.replicaCtrlAddr == replicaCtrlAddr {
|
||||
// Unchanged repeated assignment — idempotent, no side effects.
|
||||
return
|
||||
}
|
||||
|
||||
// Compute deterministic rebuild listen address.
|
||||
_, _, rebuildPort := bs.ReplicationPorts(path)
|
||||
host := bs.listenAddr
|
||||
@@ -436,6 +474,17 @@ func (bs *BlockService) setupPrimaryReplication(path, replicaDataAddr, replicaCt
|
||||
// setupPrimaryReplicationMulti configures WAL shipping from primary to N replicas
|
||||
// using SetReplicaAddrs (CP8-2: multi-replica support).
|
||||
func (bs *BlockService) setupPrimaryReplicationMulti(path string, addrs []blockvol.ReplicaAddr) {
|
||||
// P3 idempotence: skip if ALL replica addresses unchanged.
|
||||
// Compare full replica set, not just the first entry.
|
||||
if len(addrs) > 0 {
|
||||
bs.replMu.RLock()
|
||||
existing := bs.replStates[path]
|
||||
bs.replMu.RUnlock()
|
||||
if existing != nil && bs.multiReplicaUnchanged(path, addrs) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Compute deterministic rebuild listen address.
|
||||
_, _, rebuildPort := bs.ReplicationPorts(path)
|
||||
host := bs.listenAddr
|
||||
@@ -459,11 +508,15 @@ func (bs *BlockService) setupPrimaryReplicationMulti(path string, addrs []blockv
|
||||
if bs.replStates == nil {
|
||||
bs.replStates = make(map[string]*volReplState)
|
||||
}
|
||||
// Store first replica in replState for backward compat heartbeat reporting.
|
||||
// Store full replica set + first replica for backward compat heartbeat.
|
||||
if len(addrs) > 0 {
|
||||
// Copy the addrs slice to avoid aliasing.
|
||||
copied := make([]blockvol.ReplicaAddr, len(addrs))
|
||||
copy(copied, addrs)
|
||||
bs.replStates[path] = &volReplState{
|
||||
replicaDataAddr: addrs[0].DataAddr,
|
||||
replicaCtrlAddr: addrs[0].CtrlAddr,
|
||||
allReplicas: copied,
|
||||
}
|
||||
}
|
||||
bs.replMu.Unlock()
|
||||
@@ -560,6 +613,15 @@ func (bs *BlockService) DeleteBlockSnapshot(name string, snapID uint32) error {
|
||||
})
|
||||
}
|
||||
|
||||
// RestoreBlockSnapshot restores the named volume to the specified snapshot.
|
||||
// This is a destructive operation: all writes after the snapshot are lost.
|
||||
func (bs *BlockService) RestoreBlockSnapshot(name string, snapID uint32) error {
|
||||
path := bs.volumePath(name)
|
||||
return bs.blockStore.WithVolume(path, func(vol *blockvol.BlockVol) error {
|
||||
return vol.RestoreSnapshot(snapID)
|
||||
})
|
||||
}
|
||||
|
||||
// ListBlockSnapshots lists all snapshots on the named volume.
|
||||
func (bs *BlockService) ListBlockSnapshots(name string) ([]blockvol.SnapshotInfo, uint64, error) {
|
||||
path := bs.volumePath(name)
|
||||
@@ -656,6 +718,101 @@ func (bs *BlockService) CollectBlockVolumeHeartbeat() []blockvol.BlockVolumeInfo
|
||||
return msgs
|
||||
}
|
||||
|
||||
// multiReplicaUnchanged checks if the full replica set is unchanged.
|
||||
func (bs *BlockService) multiReplicaUnchanged(path string, addrs []blockvol.ReplicaAddr) bool {
|
||||
bs.replMu.RLock()
|
||||
defer bs.replMu.RUnlock()
|
||||
existing, ok := bs.replStates[path]
|
||||
if !ok || existing == nil {
|
||||
return false
|
||||
}
|
||||
if len(existing.allReplicas) != len(addrs) {
|
||||
return false
|
||||
}
|
||||
for i := range addrs {
|
||||
if existing.allReplicas[i].DataAddr != addrs[i].DataAddr ||
|
||||
existing.allReplicas[i].CtrlAddr != addrs[i].CtrlAddr ||
|
||||
existing.allReplicas[i].ServerID != addrs[i].ServerID {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// --- P3: Assignment idempotence ---
|
||||
|
||||
// lastAppliedAssignment stores the full assignment for idempotence comparison.
|
||||
// Keyed by volume path.
|
||||
type lastAppliedAssignment struct {
|
||||
Path string
|
||||
Epoch uint64
|
||||
Role uint32
|
||||
ReplicaServerID string
|
||||
ReplicaDataAddr string
|
||||
ReplicaCtrlAddr string
|
||||
ReplicaAddrs []blockvol.ReplicaAddr
|
||||
}
|
||||
|
||||
func lastAppliedFrom(a blockvol.BlockVolumeAssignment) lastAppliedAssignment {
|
||||
// Copy the slice to avoid aliasing.
|
||||
var addrs []blockvol.ReplicaAddr
|
||||
if len(a.ReplicaAddrs) > 0 {
|
||||
addrs = make([]blockvol.ReplicaAddr, len(a.ReplicaAddrs))
|
||||
copy(addrs, a.ReplicaAddrs)
|
||||
}
|
||||
return lastAppliedAssignment{
|
||||
Path: a.Path,
|
||||
Epoch: a.Epoch,
|
||||
Role: a.Role,
|
||||
ReplicaServerID: a.ReplicaServerID,
|
||||
ReplicaDataAddr: a.ReplicaDataAddr,
|
||||
ReplicaCtrlAddr: a.ReplicaCtrlAddr,
|
||||
ReplicaAddrs: addrs,
|
||||
}
|
||||
}
|
||||
|
||||
func (la lastAppliedAssignment) equals(a blockvol.BlockVolumeAssignment) bool {
|
||||
if la.Path != a.Path || la.Epoch != a.Epoch || la.Role != a.Role {
|
||||
return false
|
||||
}
|
||||
if la.ReplicaServerID != a.ReplicaServerID || la.ReplicaDataAddr != a.ReplicaDataAddr || la.ReplicaCtrlAddr != a.ReplicaCtrlAddr {
|
||||
return false
|
||||
}
|
||||
if len(la.ReplicaAddrs) != len(a.ReplicaAddrs) {
|
||||
return false
|
||||
}
|
||||
for i := range la.ReplicaAddrs {
|
||||
if la.ReplicaAddrs[i].DataAddr != a.ReplicaAddrs[i].DataAddr ||
|
||||
la.ReplicaAddrs[i].CtrlAddr != a.ReplicaAddrs[i].CtrlAddr ||
|
||||
la.ReplicaAddrs[i].ServerID != a.ReplicaAddrs[i].ServerID {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (bs *BlockService) isAssignmentUnchanged(a blockvol.BlockVolumeAssignment) bool {
|
||||
bs.lastAssignMu.RLock()
|
||||
defer bs.lastAssignMu.RUnlock()
|
||||
if bs.lastAssign == nil {
|
||||
return false
|
||||
}
|
||||
last, ok := bs.lastAssign[a.Path]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return last.equals(a)
|
||||
}
|
||||
|
||||
func (bs *BlockService) recordAppliedAssignment(a blockvol.BlockVolumeAssignment) {
|
||||
bs.lastAssignMu.Lock()
|
||||
defer bs.lastAssignMu.Unlock()
|
||||
if bs.lastAssign == nil {
|
||||
bs.lastAssign = make(map[string]lastAppliedAssignment)
|
||||
}
|
||||
bs.lastAssign[a.Path] = lastAppliedFrom(a)
|
||||
}
|
||||
|
||||
// ReplicationPorts computes deterministic replication ports for a volume.
|
||||
// Ports are derived from a hash of the volume path offset from the iSCSI base port.
|
||||
func (bs *BlockService) ReplicationPorts(volPath string) (dataPort, ctrlPort, rebuildPort int) {
|
||||
@@ -681,6 +838,10 @@ func (bs *BlockService) Shutdown() {
|
||||
return
|
||||
}
|
||||
glog.V(0).Infof("block service: shutting down...")
|
||||
// P4: drain active recovery goroutines before closing volumes.
|
||||
if bs.v2Recovery != nil {
|
||||
bs.v2Recovery.Shutdown()
|
||||
}
|
||||
if bs.nvmeServer != nil {
|
||||
bs.nvmeServer.Close()
|
||||
}
|
||||
|
||||
@@ -106,11 +106,13 @@ func AssignmentToProto(a BlockVolumeAssignment) *master_pb.BlockVolumeAssignment
|
||||
ReplicaDataAddr: a.ReplicaDataAddr,
|
||||
ReplicaCtrlAddr: a.ReplicaCtrlAddr,
|
||||
RebuildAddr: a.RebuildAddr,
|
||||
ReplicaServerId: a.ReplicaServerID, // V2: stable identity
|
||||
}
|
||||
for _, ra := range a.ReplicaAddrs {
|
||||
pb.ReplicaAddrs = append(pb.ReplicaAddrs, &master_pb.ReplicaAddrMessage{
|
||||
DataAddr: ra.DataAddr,
|
||||
CtrlAddr: ra.CtrlAddr,
|
||||
ServerId: ra.ServerID, // V2: stable identity
|
||||
})
|
||||
}
|
||||
return pb
|
||||
@@ -123,11 +125,12 @@ func AssignmentFromProto(p *master_pb.BlockVolumeAssignment) BlockVolumeAssignme
|
||||
return BlockVolumeAssignment{}
|
||||
}
|
||||
a := BlockVolumeAssignment{
|
||||
Path: p.Path,
|
||||
Epoch: p.Epoch,
|
||||
Role: p.Role,
|
||||
LeaseTtlMs: p.LeaseTtlMs,
|
||||
RebuildAddr: p.RebuildAddr,
|
||||
Path: p.Path,
|
||||
Epoch: p.Epoch,
|
||||
Role: p.Role,
|
||||
LeaseTtlMs: p.LeaseTtlMs,
|
||||
RebuildAddr: p.RebuildAddr,
|
||||
ReplicaServerID: p.ReplicaServerId, // V2: stable identity
|
||||
}
|
||||
if len(p.ReplicaAddrs) > 0 {
|
||||
// Multi-replica: populate ReplicaAddrs, leave scalar fields empty.
|
||||
@@ -135,6 +138,7 @@ func AssignmentFromProto(p *master_pb.BlockVolumeAssignment) BlockVolumeAssignme
|
||||
a.ReplicaAddrs = append(a.ReplicaAddrs, ReplicaAddr{
|
||||
DataAddr: ra.DataAddr,
|
||||
CtrlAddr: ra.CtrlAddr,
|
||||
ServerID: ra.ServerId, // V2: stable identity
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -464,3 +464,76 @@ func TestInfoMessage_HealthFieldsZeroDefault(t *testing.T) {
|
||||
t.Fatal("ReplicaDegraded zero default should be false")
|
||||
}
|
||||
}
|
||||
|
||||
// --- Phase 10 P1: Stable server identity on the proto wire ---
|
||||
|
||||
func TestP10P1_ProtoRoundTrip_ScalarServerID(t *testing.T) {
|
||||
a := BlockVolumeAssignment{
|
||||
Path: "/data/vol1.blk",
|
||||
Epoch: 5,
|
||||
Role: RoleToWire(RolePrimary),
|
||||
ReplicaDataAddr: "10.0.0.2:9333",
|
||||
ReplicaCtrlAddr: "10.0.0.2:9334",
|
||||
ReplicaServerID: "vs2-grpc:18080",
|
||||
RebuildAddr: "10.0.0.1:5000",
|
||||
}
|
||||
|
||||
pb := AssignmentToProto(a)
|
||||
if pb.ReplicaServerId != "vs2-grpc:18080" {
|
||||
t.Fatalf("proto encode: ReplicaServerId=%q, want vs2-grpc:18080", pb.ReplicaServerId)
|
||||
}
|
||||
|
||||
decoded := AssignmentFromProto(pb)
|
||||
if decoded.ReplicaServerID != "vs2-grpc:18080" {
|
||||
t.Fatalf("proto decode: ReplicaServerID=%q, want vs2-grpc:18080", decoded.ReplicaServerID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestP10P1_ProtoRoundTrip_MultiReplicaServerID(t *testing.T) {
|
||||
a := BlockVolumeAssignment{
|
||||
Path: "/data/vol1.blk",
|
||||
Epoch: 5,
|
||||
Role: RoleToWire(RolePrimary),
|
||||
ReplicaAddrs: []ReplicaAddr{
|
||||
{DataAddr: "10.0.0.2:9333", CtrlAddr: "10.0.0.2:9334", ServerID: "vs2-grpc:18080"},
|
||||
{DataAddr: "10.0.0.3:9333", CtrlAddr: "10.0.0.3:9334", ServerID: "vs3-grpc:18080"},
|
||||
},
|
||||
}
|
||||
|
||||
pb := AssignmentToProto(a)
|
||||
if pb.ReplicaAddrs[0].ServerId != "vs2-grpc:18080" {
|
||||
t.Fatalf("encode[0]: ServerId=%q", pb.ReplicaAddrs[0].ServerId)
|
||||
}
|
||||
if pb.ReplicaAddrs[1].ServerId != "vs3-grpc:18080" {
|
||||
t.Fatalf("encode[1]: ServerId=%q", pb.ReplicaAddrs[1].ServerId)
|
||||
}
|
||||
|
||||
decoded := AssignmentFromProto(pb)
|
||||
if decoded.ReplicaAddrs[0].ServerID != "vs2-grpc:18080" {
|
||||
t.Fatalf("decode[0]: ServerID=%q", decoded.ReplicaAddrs[0].ServerID)
|
||||
}
|
||||
if decoded.ReplicaAddrs[1].ServerID != "vs3-grpc:18080" {
|
||||
t.Fatalf("decode[1]: ServerID=%q", decoded.ReplicaAddrs[1].ServerID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestP10P1_ProtoRoundTrip_MissingServerID_NotSynthesized(t *testing.T) {
|
||||
a := BlockVolumeAssignment{
|
||||
Path: "/data/vol1.blk",
|
||||
Epoch: 5,
|
||||
Role: RoleToWire(RolePrimary),
|
||||
ReplicaDataAddr: "10.0.0.2:9333",
|
||||
ReplicaCtrlAddr: "10.0.0.2:9334",
|
||||
// ReplicaServerID intentionally empty.
|
||||
}
|
||||
|
||||
pb := AssignmentToProto(a)
|
||||
if pb.ReplicaServerId != "" {
|
||||
t.Fatalf("encode: ReplicaServerId=%q, want empty", pb.ReplicaServerId)
|
||||
}
|
||||
|
||||
decoded := AssignmentFromProto(pb)
|
||||
if decoded.ReplicaServerID != "" {
|
||||
t.Fatalf("decode: ReplicaServerID=%q, want empty (not synthesized)", decoded.ReplicaServerID)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user