refactor: extract host effects adapter and define Phase 17 stop line

Move dispatcher-facing host effects out of volume_server_block.go into
blockcmd while keeping server-owned cache/state semantics in weed/server.
Document Batch 10 delivery and Batch 11 stop-line review so the
separation line closes without over-extracting readiness-state mutation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
pingqiuandClaude Opus 4.6 committed 2026-04-04 08:43:21 -07:00
1 parent 38b5042997
commit 1453274988
6 files changed
+940 -43

No files matched your search

+371
View File
@@ -0,0 +1,371 @@
Purpose: append-only technical pack and delivery log for `Phase 17`
post-`Phase 16` separation tracking.
---
### `Phase 17` Start Note
Date: 2026-04-04
Intent: restore a continuous engineering log for the migration batches that
followed `Phase 16` runtime closure
This phase is intentionally a tracking phase.
It records the code-separation line that ran after `Phase 16` but before a new
single semantic/runtime claim had replaced it.
It exists so that:
1. `Batch 1-9` have one durable phase home
2. reviews can reference a stable migration timeline
3. the next seam can be chosen from a clear current-state snapshot
---
### Batch 1 Delivery Note
Date: 2026-04-04
Scope: canonical translation and contract ownership check
What changed:
1. canonical replica identity and recovery-target translation were confirmed to
belong to `sw-block/bridge/blockvol`
2. adapter-side inline mapping was removed from `weed/storage/blockvol/v2bridge/control.go`
3. the remaining Batch 1 ports (`reader`, `pinner`, `executor`) were reviewed
and found already aligned with the intended ownership split
Proof / evidence:
1. commit `a38e04c03`
2. no further code changes required for `Task B/C/D`
Conclusion:
1. Batch 1 closed semantic drift first, without forcing unnecessary code motion
---
### Batch 2 Delivery Note
Date: 2026-04-04
Scope: backend-binding shim reduction
What changed:
1. `v2bridge.Reader` now returns `bridge.BlockVolState` directly
2. `pinnerShimForRecovery` was removed from `weed/server/block_recovery.go`
3. executor binding was rechecked and kept as the already-correct thin binding
Proof / evidence:
1. commit `680b53031`
2. commit `519c84994`
Conclusion:
1. the backend-binding layer became thinner without changing semantic ownership
---
### Batch 3 Delivery Note
Date: 2026-04-04
Scope: reusable recovery coordination extraction
What changed:
1. `sw-block/engine/replication/runtime/pending.go` introduced
`PendingCoordinator`
2. `sw-block/engine/replication/runtime/executor.go` introduced reusable
recovery execution helpers
3. `weed/server/block_recovery.go` was later rewired so production code uses the
runtime helpers directly
4. no-core execution was split into explicit legacy helpers instead of remaining
implicit inline branches
Proof / evidence:
1. commit `6fea93e82`
2. commit `e200df779`
3. commit `e075d7761`
4. commit `3a5fbbfde`
Conclusion:
1. Batch 3 only became complete after the wiring fix; the final state removes
helper duplication from the production path
---
### Batch 4 Delivery Note
Date: 2026-04-04
Scope: typed runtime boundary and host-shell reduction
What changed:
1. `PendingExecution` became fully typed
2. type assertions and `interface{}` drift were removed from the production
recovery path
3. rebuild completion shaping moved into a dedicated runtime helper
4. recovery bundle assembly inside `block_recovery.go` was further reduced into
a bounded helper
Proof / evidence:
1. commit `0bcfc678d`
2. commit `ded84b25e`
Conclusion:
1. Batch 4 made the recovery host shell easier to reason about and safer to
test
---
### Batch 5 Delivery Note
Date: 2026-04-04
Scope: recovery binding factory extraction
What changed:
1. concrete construction of `Reader`, `Pinner`, `StorageAdapter`, and
`Executor` moved behind `v2bridge.BuildRecoveryBundle()`
2. `weed/server/block_recovery.go` stopped assembling those concrete bindings
directly
Proof / evidence:
1. commit `263611004`
Conclusion:
1. the backend-binding layer now owns its own assembly seam
---
### Batch 6 Delivery Note
Date: 2026-04-04
Scope: recovery context resolver extraction
What changed:
1. `resolveRecoveryContext()` consolidated host-side context assembly
2. inline derivation of `rebuildAddr` and related runtime inputs was removed
3. `runCatchUp()` and `runRebuild()` now follow a simple:
- resolve
- plan
- branch
structure
Proof / evidence:
1. commit `a48da0f67`
2. commit `41082bf92`
Conclusion:
1. the recovery host path became structurally thin enough to review by shape,
not only by behavior
---
### Batch 7 Delivery Note
Date: 2026-04-04
Scope: command dispatch extraction
What changed:
1. the `engine.Command` switch moved out of `weed/server/volume_server_block.go`
2. new package `weed/server/blockcmd` became the server-adapter command
dispatcher
3. host effects remained intentionally on the server side instead of being
pushed into `v2bridge`
Proof / evidence:
1. commit `11c6aaf31`
Conclusion:
1. Batch 7 established the correct ownership seam:
- dispatch in server adapter
- backend bindings elsewhere
- host effects still local
---
### Batch 8 Delivery Note
Date: 2026-04-04
Scope: `BlockVol` command-binding extraction
What changed:
1. concrete `BlockVol` command operations moved into
`weed/storage/blockvol/v2bridge/command_bindings.go`
2. direct `WithVolume` execution for role apply / receiver startup / primary
replication setup stopped living in `volume_server_block.go`
Proof / evidence:
1. commit `38b504299`
Conclusion:
1. `v2bridge` now owns concrete backend command bindings, which is the correct
side of the seam
---
### Batch 9 Delivery Note
Date: 2026-04-04
Scope: non-`BlockVol` command-op extraction
What changed:
1. non-backend command operations moved into `weed/server/blockcmd/service_ops.go`
2. dispatcher rebinding no longer requires `volume_server_block.go` to own the
full command-op surface
3. nil-safe service-op construction was added so typed nil pointers are not
smuggled through interfaces
Proof / evidence:
1. commit `38b504299`
2. focused server proofs remained green after rebinding
Conclusion:
1. after Batch 9, `volume_server_block.go` is much closer to a host shell than
a command-runtime implementation file
---
### Batch 10 Start Note
Date: 2026-04-04
Scope: `10A` host-effects adapter extraction
Execution rule:
1. extract only command-completion host effects
2. keep the slice bounded to:
- `RecordCommand`
- `EmitCoreEvent`
- `PublishProjection`
- projection-cache write routing
3. do not mix backend readiness mutation into the same cut unless the code
proves it is already inseparable
Acceptance target:
1. `coreCommandEffects` disappears from `volume_server_block.go`
2. publish-projection cache writes stop being inline there
3. dispatcher keeps consuming a server-side host-effects object
4. focused proofs stay green
Why this is the next seam:
1. dispatch is already extracted
2. backend bindings are already extracted
3. the largest remaining concentrated non-shell logic in
`volume_server_block.go` is now host effects
---
### Batch 10 Delivery Note
Date: 2026-04-04
Scope: `10A` host-effects adapter extraction
What changed:
1. concrete dispatcher-facing host effects moved into
`weed/server/blockcmd/host_effects.go`
2. `volume_server_block.go` now wires host-effect callbacks and projection cache
storage into that adapter instead of defining `coreCommandEffects` locally
3. server-owned projection cache writes moved behind
`BlockService.StoreProjection()`
Proof / evidence:
1. `go test ./weed/server/blockcmd -count=1 -timeout 60s`
2. `go test ./weed/server -count=1 -timeout 120s -run "TestBlockService_(ApplyAssignments|DebugInfoForVolume|CollectBlockVolumeHeartbeat|ReadinessSnapshot|HeartbeatReplicaDegraded)"`
3. result: `PASS`
Conclusion:
1. `volume_server_block.go` is thinner again, but host-effect semantics still
remain explicitly on the server side
2. backend readiness mutation was intentionally left untouched in this slice
---
### Batch 11 Delivery Note
Date: 2026-04-04
Scope: stop-line review for remaining readiness-state mutation
What was reviewed:
1. `noteRoleApplied`
2. `markPrimaryTransportConfigured`
3. `markReceiverReady`
4. `ReadinessSnapshot` as the read-side consumer of the same local state
Decision:
1. do not extract these methods into `weed/server/blockcmd`
Why:
1. they are direct mutations of `BlockService.replStates`
2. they belong to adapter-local host state, not dispatcher-side orchestration
3. a further extraction would mostly replace direct method calls with callback
plumbing while leaving ownership unchanged
Accepted stop line:
1. `blockcmd` keeps dispatch / service ops / host-effects adapter
2. `v2bridge` keeps concrete backend bindings
3. `weed/server` keeps local readiness/cache state and its mutation paths
Conclusion:
1. the current boundary is the correct stopping point for the separation line
2. the next meaningful work item should be cleanup within that boundary or a new
semantic/runtime step, not another package shuffle
---
### Current State Snapshot
Date: 2026-04-04
State after Batch 10:
1. `sw-block` owns:
- canonical bridge helpers
- reusable recovery runtime helpers
2. `weed/storage/blockvol/v2bridge` owns:
- concrete `BlockVol` recovery bundle assembly
- concrete `BlockVol` command bindings
3. `weed/server/blockcmd` owns:
- command dispatch
- service-side command operations
- host-effects adapter
4. `weed/server` still owns:
- assignment ingress
- projection/publication cache writes
- host-owned cache/state fields
- product-facing integration state
Open next seam:
1. no additional ownership move is currently justified on the readiness-state
path
+411
View File
@@ -0,0 +1,411 @@
# Phase 17
Date: 2026-04-04
Status: active
Purpose: track post-`Phase 16` code separation so adapter/runtime ownership in
`weed/server` and backend-binding ownership in `weed/storage/blockvol/v2bridge`
continue to converge without losing engineering continuity
## Why This Phase Exists
`Phase 14-16` established and widened one bounded `V2`-owned runtime path:
1. `Phase 14` made the explicit core real
2. `Phase 15` rebound bounded integrated surfaces to consume core-owned truth
3. `Phase 16` closed one bounded runtime path where the core owns command and
observation semantics
That semantic/runtime line is now real, but a separate engineering line has
been running in parallel:
1. move canonical rules toward `sw-block`
2. thin `weed/server` into a host shell
3. keep `v2bridge` limited to backend-binding responsibilities
4. avoid creating a new mixed-ownership layer while the migration proceeds
This phase exists to track that separation line explicitly so commits, reviews,
and next steps do not drift away from the phase log.
## Relationship To Phase 16
`Phase 16` remains the semantic/runtime-ownership phase.
`Phase 17` is different:
1. it does not redefine the bounded `V2` runtime claims from `Phase 16`
2. it tracks code movement and ownership cleanup after those claims became real
3. it records which adapter/binding responsibilities moved where
In short:
1. `Phase 16` answers: who owns runtime semantics?
2. `Phase 17` answers: where does the code now live, and what is left to move?
## Phase Goal
Track and complete the post-`Phase 16` separation of command/runtime/binding
responsibilities so the codebase approaches this split:
1. `sw-block`
- owns canonical contracts, core/runtime helpers, and reusable semantics
2. `weed/storage/blockvol/v2bridge`
- owns concrete `BlockVol` and wire/backend bindings
3. `weed/server`
- owns host-shell orchestration, host effects, and product integration only
## Scope
### In scope
1. migration batches that thin `weed/server`
2. migration batches that keep backend binding in `v2bridge`
3. migration batches that move reusable coordination toward `sw-block`
4. explicit tracking of delivered / partial / next separation seams
### Out of scope
1. changing the semantic claims already owned by `Phase 14-16`
2. broad protocol redesign
3. full removal of legacy compatibility paths without replacement criteria
4. changing `blockvol` into a `sw-block` package
## Batch Tracking
### Batch 1: Contracts And Canonical Translation
Goal:
1. stabilize contract ownership and canonical identity/recovery-target mapping
Status:
1. delivered
Delivered result:
1. `sw-block/bridge/blockvol` owns the canonical contract/helper layer
2. `Task A` removed duplicate identity/recovery-target derivation from adapter
code
3. `Task B/C/D` were confirmed already clean at the acceptance bar
Evidence:
1. `a38e04c03`
2. design notes:
- `sw-block/design/v2-first-migration-batch.md`
- `sw-block/design/v2-first-migration-task-pack.md`
### Batch 2: Backend-Binding Shim Reduction
Goal:
1. remove redundant reader/pinner shims and confirm thin backend bindings
Status:
1. delivered
Delivered result:
1. `v2bridge.Reader` returns bridge contract state directly
2. `pinnerShimForRecovery` was removed
3. `Executor` was confirmed already clean as a direct engine-IO implementer
Evidence:
1. `680b53031`
2. `519c84994`
3. design notes:
- `sw-block/design/v2-second-migration-batch.md`
- `sw-block/design/v2-second-migration-task-pack.md`
### Batch 3: Recovery Runtime Helper Extraction
Goal:
1. move reusable recovery coordination out of `weed/server/block_recovery.go`
Status:
1. delivered
Delivered result:
1. `sw-block/engine/replication/runtime` owns pending coordination and reusable
execution helpers
2. `block_recovery.go` uses those helpers on the production path
3. no-core behavior is structurally isolated behind explicit legacy helpers
Evidence:
1. `6fea93e82`
2. `e200df779`
3. `e075d7761`
4. `3a5fbbfde`
### Batch 4: Typed Runtime Boundary And Host-Shell Reduction
Goal:
1. remove `interface{}` drift and simplify recovery host-shell logic
Status:
1. delivered
Delivered result:
1. `PendingExecution` is fully typed
2. rebuild completion shaping moved into runtime helper
3. `buildRecoveryBundle()` removed repeated host-side assembly
Evidence:
1. `0bcfc678d`
2. `ded84b25e`
### Batch 5: Recovery Binding Factory Extraction
Goal:
1. move recovery bundle assembly into the backend-binding layer
Status:
1. delivered
Delivered result:
1. `block_recovery.go` no longer constructs `Reader/Pinner/StorageAdapter/Executor`
directly
2. `v2bridge.BuildRecoveryBundle()` became the concrete backend-binding seam
Evidence:
1. `263611004`
### Batch 6: Recovery Context Resolver Extraction
Goal:
1. make one resolver own the host-side recovery context
Status:
1. delivered
Delivered result:
1. `resolveRecoveryContext()` owns:
- `volPath`
- `rebuildAddr`
- bundle construction
- replica flushed progress lookup
2. `runCatchUp()` / `runRebuild()` now read as:
- resolve
- plan
- branch
Evidence:
1. `a48da0f67`
2. `41082bf92`
### Batch 7: Command Dispatch Split
Goal:
1. move command-switch orchestration out of `volume_server_block.go`
2. keep it out of `v2bridge`
Status:
1. delivered
Delivered result:
1. `weed/server/blockcmd` owns command dispatch
2. `weed/server` keeps host effects
3. `v2bridge` remains free of `engine.Command` switch / event-emission
semantics
Evidence:
1. `11c6aaf31`
### Batch 8: BlockVol Command-Binding Extraction
Goal:
1. move concrete `BlockVol`-backed command operations out of
`volume_server_block.go`
Status:
1. delivered
Delivered result:
1. `v2bridge.CommandBindings` owns:
- role apply binding
- receiver startup binding
- primary shipper configuration binding
- shipper-connected probe
2. `weed/server` still owns readiness-state updates and event semantics
Evidence:
1. `38b504299`
### Batch 9: Non-BlockVol Service-Ops Extraction
Goal:
1. move the remaining non-`BlockVol` command operations out of
`volume_server_block.go` into the server-adapter layer
Status:
1. delivered
Delivered result:
1. `weed/server/blockcmd.ServiceOps` owns:
- recovery-task startup
- pending catch-up/rebuild execution
- sender invalidation through projection + resolver
2. `volume_server_block.go` now keeps:
- host effects
- backend-facing thin wrappers
- assignment/event ingress
Evidence:
1. `38b504299`
### Batch 10: Host-Effects Adapter Extraction
Goal:
1. move the remaining command-completion host effects out of
`volume_server_block.go` while keeping them on the `weed/server` side
Status:
1. delivered
Chosen slice:
1. `10A`: extract a dedicated server-side host-effects adapter into
`weed/server/blockcmd`
2. keep this slice bounded to:
- `RecordCommand`
- `EmitCoreEvent`
- `PublishProjection`
- projection-cache write routing
3. do not fold backend-side readiness mutation into this first cut
Acceptance:
1. `coreCommandEffects` is no longer defined in `volume_server_block.go`
2. `PublishProjection` cache-write logic is no longer inline in
`volume_server_block.go`
3. `weed/server` still owns host-effect semantics, but exposes them through a
thinner adapter object
4. focused proofs remain green
Delivered result:
1. `weed/server/blockcmd/host_effects.go` now owns the concrete dispatcher-side
host-effects adapter
2. `volume_server_block.go` now only wires recorder/event/cache dependencies
into that adapter
3. server-owned projection cache writing moved behind `BlockService.StoreProjection()`
Evidence:
1. working tree change on top of `38b504299`
### Batch 11: Stop-Line Review For Readiness Mutation
Goal:
1. decide whether the remaining adapter-local readiness mutation should move out
of `volume_server_block.go`
Status:
1. delivered
Decision:
1. stop at the current package boundary
2. do not extract readiness-state mutation into `weed/server/blockcmd`
Reason:
1. the remaining methods are not dispatcher-side host effects
2. they are direct writes to `BlockService`-owned `replStates`
3. they encode adapter-local cache/state semantics, not reusable command
orchestration logic
4. moving them into `blockcmd` would mostly add callback indirection without
clarifying ownership
Covered methods:
1. `noteRoleApplied`
2. `markPrimaryTransportConfigured`
3. `markReceiverReady`
4. `ReadinessSnapshot` remains the read-side consumer of the same local state
Accepted stop line:
1. `weed/server/blockcmd`
- owns dispatch, service ops, and host-effects adapter
2. `weed/server`
- owns host state fields and adapter-local readiness/cache mutation
3. `weed/storage/blockvol/v2bridge`
- owns concrete backend bindings only
## Current Position
The separation line is now in the late-stage thinning phase.
Current split:
1. `sw-block`
- canonical contracts / helpers
- runtime coordination helpers
2. `weed/storage/blockvol/v2bridge`
- `BlockVol`-backed recovery and command bindings
3. `weed/server/blockcmd`
- command dispatch, service-side command operations, and host-effects adapter
4. `weed/server`
- assignment ingress, product integration, and adapter-local readiness/cache state
## Non-Claims
`Phase 17` does not yet claim:
1. every remaining host-side helper must move
2. full deletion of compatibility-only paths
3. that `weed/server` becomes empty or trivial
The stop line is practical, not ideological:
1. keep real host/product integration in `weed/server`
2. keep backend binding in `v2bridge`
3. keep reusable semantics/runtime helpers out of both
## Immediate Next Step
The current separation stop line is now explicit:
1. keep dispatcher-side logic in `weed/server/blockcmd`
2. keep backend bindings in `weed/storage/blockvol/v2bridge`
3. keep `BlockService` local state mutation in `weed/server`
Any next task should now be one of two kinds:
1. cleanup inside the accepted stop line
2. a new semantic/runtime phase, not another ownership-shuffle task
+17
View File
@@ -0,0 +1,17 @@
package weed_server
import engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
// StoreProjection keeps the latest published projection in the server-owned
// adapter cache used by outward/product-facing surfaces.
func (bs *BlockService) StoreProjection(volumeID string, projection engine.PublicationProjection) {
if bs == nil {
return
}
bs.coreProjMu.Lock()
if bs.coreProj == nil {
bs.coreProj = make(map[string]engine.PublicationProjection)
}
bs.coreProj[volumeID] = projection
bs.coreProjMu.Unlock()
}
+65 -4
View File
@@ -189,11 +189,11 @@ type fakeRecoveryCoordinator struct {
startedReplica string
startedAssigns []blockvol.BlockVolumeAssignment
catchUpCalls []struct {
volumeID string
volumeID string
targetLSN uint64
}
rebuildCalls []struct {
volumeID string
volumeID string
targetLSN uint64
}
}
@@ -205,7 +205,7 @@ func (f *fakeRecoveryCoordinator) StartRecoveryTask(replicaID string, assignment
func (f *fakeRecoveryCoordinator) ExecutePendingCatchUp(volumeID string, targetLSN uint64) error {
f.catchUpCalls = append(f.catchUpCalls, struct {
volumeID string
volumeID string
targetLSN uint64
}{volumeID: volumeID, targetLSN: targetLSN})
return nil
@@ -213,7 +213,7 @@ func (f *fakeRecoveryCoordinator) ExecutePendingCatchUp(volumeID string, targetL
func (f *fakeRecoveryCoordinator) ExecutePendingRebuild(volumeID string, targetLSN uint64) error {
f.rebuildCalls = append(f.rebuildCalls, struct {
volumeID string
volumeID string
targetLSN uint64
}{volumeID: volumeID, targetLSN: targetLSN})
return nil
@@ -231,6 +231,17 @@ func (f fakeProjectionReader) Projection(volumeID string) (engine.PublicationPro
return f.proj, true
}
type fakeProjectionCache struct {
published map[string]engine.PublicationProjection
}
func (f *fakeProjectionCache) StoreProjection(volumeID string, projection engine.PublicationProjection) {
if f.published == nil {
f.published = make(map[string]engine.PublicationProjection)
}
f.published[volumeID] = projection
}
type fakeSessionInvalidator struct {
reasons []string
states []engine.ReplicaState
@@ -257,6 +268,56 @@ func TestServiceOps_StartRecoveryTaskUsesRecoveryCoordinator(t *testing.T) {
}
}
func TestHostEffects_PublishProjectionPrefersLatestCoreProjection(t *testing.T) {
cache := &fakeProjectionCache{}
effects := NewHostEffects(
nil,
nil,
fakeProjectionReader{
proj: engine.PublicationProjection{VolumeID: "vol1", Role: engine.RoleReplica},
ok: true,
},
cache,
)
err := effects.PublishProjection("vol1", engine.PublicationProjection{VolumeID: "vol1", Role: engine.RolePrimary})
if err != nil {
t.Fatal(err)
}
got, ok := cache.published["vol1"]
if !ok {
t.Fatal("expected cached projection")
}
if got.Role != engine.RoleReplica {
t.Fatalf("role=%v", got.Role)
}
}
func TestHostEffects_RecordAndEmitUseCallbacks(t *testing.T) {
var recorded []string
var events []engine.Event
effects := NewHostEffects(
func(volumeID, name string) {
recorded = append(recorded, volumeID+":"+name)
},
func(ev engine.Event) {
events = append(events, ev)
},
nil,
nil,
)
effects.RecordCommand("vol1", "apply_role")
effects.EmitCoreEvent(engine.RoleApplied{ID: "vol1"})
if !reflect.DeepEqual(recorded, []string{"vol1:apply_role"}) {
t.Fatalf("recorded=%v", recorded)
}
if len(events) != 1 {
t.Fatalf("events=%d", len(events))
}
if _, ok := events[0].(engine.RoleApplied); !ok {
t.Fatalf("event=%T", events[0])
}
}
func TestServiceOps_InvalidateSessionUsesProjectionAndSenderResolver(t *testing.T) {
s1 := &fakeSessionInvalidator{}
s2 := &fakeSessionInvalidator{}
+66
View File
@@ -0,0 +1,66 @@
package blockcmd
import engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
// CommandRecorder records one executed command on the host side.
type CommandRecorder func(volumeID, name string)
// CoreEventEmitter feeds one runtime observation back into the core.
type CoreEventEmitter func(ev engine.Event)
// ProjectionCacheWriter stores the latest published projection in a host-local
// cache owned by the server adapter.
type ProjectionCacheWriter interface {
StoreProjection(volumeID string, projection engine.PublicationProjection)
}
type hostEffects struct {
recordCommand CommandRecorder
emitCoreEvent CoreEventEmitter
projection ProjectionReader
cache ProjectionCacheWriter
}
// NewHostEffects keeps host-side command completion effects in the server
// adapter layer while exposing only the narrow Dispatcher-facing surface.
func NewHostEffects(
recordCommand CommandRecorder,
emitCoreEvent CoreEventEmitter,
projection ProjectionReader,
cache ProjectionCacheWriter,
) HostEffects {
return &hostEffects{
recordCommand: recordCommand,
emitCoreEvent: emitCoreEvent,
projection: projection,
cache: cache,
}
}
func (effects *hostEffects) RecordCommand(volumeID, name string) {
if effects == nil || effects.recordCommand == nil {
return
}
effects.recordCommand(volumeID, name)
}
func (effects *hostEffects) EmitCoreEvent(ev engine.Event) {
if effects == nil || effects.emitCoreEvent == nil {
return
}
effects.emitCoreEvent(ev)
}
func (effects *hostEffects) PublishProjection(volumeID string, projection engine.PublicationProjection) error {
if effects == nil || effects.cache == nil {
return nil
}
proj := projection
if effects.projection != nil {
if latest, ok := effects.projection.Projection(volumeID); ok {
proj = latest
}
}
effects.cache.StoreProjection(volumeID, proj)
return nil
}
+10 -39
View File
@@ -11,8 +11,8 @@ import (
bridgeblockvol "github.com/seaweedfs/seaweedfs/sw-block/bridge/blockvol"
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
"github.com/seaweedfs/seaweedfs/weed/server/blockcmd"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/server/blockcmd"
"github.com/seaweedfs/seaweedfs/weed/storage"
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol/iscsi"
@@ -580,6 +580,14 @@ func (bs *BlockService) coreCommandDispatcher() *blockcmd.Dispatcher {
if bs != nil && bs.v2Core != nil {
projection = bs.v2Core
}
var recordCommand blockcmd.CommandRecorder
var emitCoreEvent blockcmd.CoreEventEmitter
var projectionCache blockcmd.ProjectionCacheWriter
if bs != nil {
recordCommand = bs.recordExecutedCoreCommand
emitCoreEvent = bs.applyCoreEvent
projectionCache = bs
}
return blockcmd.NewDispatcher(
blockcmd.NewServiceOps(
coreCommandBackend{bs: bs},
@@ -592,7 +600,7 @@ func (bs *BlockService) coreCommandDispatcher() *blockcmd.Dispatcher {
return bs.v2Orchestrator.Registry.Sender(replicaID)
},
),
coreCommandEffects{bs: bs},
blockcmd.NewHostEffects(recordCommand, emitCoreEvent, projection, projectionCache),
)
}
@@ -660,43 +668,6 @@ func (ops coreCommandBackend) ConfigureShipper(volumeID string, replicas []engin
return true, ops.bs.isPrimaryShipperConnected(volumeID), nil
}
type coreCommandEffects struct {
bs *BlockService
}
func (effects coreCommandEffects) RecordCommand(volumeID, name string) {
if effects.bs == nil {
return
}
effects.bs.recordExecutedCoreCommand(volumeID, name)
}
func (effects coreCommandEffects) EmitCoreEvent(ev engine.Event) {
if effects.bs == nil {
return
}
effects.bs.applyCoreEvent(ev)
}
func (effects coreCommandEffects) PublishProjection(volumeID string, projection engine.PublicationProjection) error {
if effects.bs == nil {
return nil
}
proj := projection
if core := effects.bs.V2Core(); core != nil {
if latest, ok := core.Projection(volumeID); ok {
proj = latest
}
}
effects.bs.coreProjMu.Lock()
if effects.bs.coreProj == nil {
effects.bs.coreProj = make(map[string]engine.PublicationProjection)
}
effects.bs.coreProj[volumeID] = proj
effects.bs.coreProjMu.Unlock()
return nil
}
func (bs *BlockService) applyRoleAssignment(a blockvol.BlockVolumeAssignment) error {
if bs == nil || bs.blockStore == nil {
return nil