mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
E5 handoff contract (contract.go): - BlockVolReader: ReadState() → BlockVolState from real blockvol - BlockVolPinner: HoldWALRetention/HoldSnapshot/HoldFullBase → release func - BlockVolExecutor: StreamWALEntries/TransferSnapshot/TransferFullBase/TruncateWAL - Clear import direction: weed-side imports sw-block, not reverse StorageAdapter refactored: - Consumes BlockVolReader + BlockVolPinner interfaces - Pin/release uses release-func pattern (not map-based tracking) - PushStorageAdapter for tests (push-based, no blockvol dependency) 10 bridge tests: - 4 control adapter (identity, address change, role mapping, primary) - 4 storage adapter (retained history, WAL pin reject, snapshot reject, symmetry) - 1 E2E (assignment → adapter → engine → plan → execute → InSync) - 1 contract interface verification Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
165 lines
4.9 KiB
Go
165 lines
4.9 KiB
Go
package blockvol
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
engine "github.com/seaweedfs/seaweedfs/sw-block/engine/replication"
|
|
)
|
|
|
|
// StorageAdapter implements engine.StorageAdapter by consuming
|
|
// BlockVolReader and BlockVolPinner interfaces. When backed by
|
|
// real implementations from weed/storage/blockvol/v2bridge/,
|
|
// all fields come from actual blockvol state.
|
|
//
|
|
// For testing, use PushStorageAdapter (push-based, no blockvol dependency).
|
|
type StorageAdapter struct {
|
|
reader BlockVolReader
|
|
pinner BlockVolPinner
|
|
|
|
mu sync.Mutex
|
|
nextPinID atomic.Uint64
|
|
|
|
// Release functions keyed by pin ID.
|
|
releaseFuncs map[uint64]func()
|
|
}
|
|
|
|
// NewStorageAdapter creates a storage adapter backed by real blockvol
|
|
// reader and pinner interfaces.
|
|
func NewStorageAdapter(reader BlockVolReader, pinner BlockVolPinner) *StorageAdapter {
|
|
return &StorageAdapter{
|
|
reader: reader,
|
|
pinner: pinner,
|
|
releaseFuncs: map[uint64]func(){},
|
|
}
|
|
}
|
|
|
|
// GetRetainedHistory reads real blockvol state via BlockVolReader.
|
|
func (sa *StorageAdapter) GetRetainedHistory() engine.RetainedHistory {
|
|
state := sa.reader.ReadState()
|
|
return engine.RetainedHistory{
|
|
HeadLSN: state.WALHeadLSN,
|
|
TailLSN: state.WALTailLSN,
|
|
CommittedLSN: state.CommittedLSN,
|
|
CheckpointLSN: state.CheckpointLSN,
|
|
CheckpointTrusted: state.CheckpointTrusted,
|
|
}
|
|
}
|
|
|
|
// PinSnapshot delegates to BlockVolPinner.HoldSnapshot.
|
|
func (sa *StorageAdapter) PinSnapshot(checkpointLSN uint64) (engine.SnapshotPin, error) {
|
|
release, err := sa.pinner.HoldSnapshot(checkpointLSN)
|
|
if err != nil {
|
|
return engine.SnapshotPin{}, fmt.Errorf("snapshot pin at LSN %d: %w", checkpointLSN, err)
|
|
}
|
|
id := sa.nextPinID.Add(1)
|
|
sa.mu.Lock()
|
|
sa.releaseFuncs[id] = release
|
|
sa.mu.Unlock()
|
|
return engine.SnapshotPin{LSN: checkpointLSN, PinID: id, Valid: true}, nil
|
|
}
|
|
|
|
// ReleaseSnapshot calls the held release function.
|
|
func (sa *StorageAdapter) ReleaseSnapshot(pin engine.SnapshotPin) {
|
|
sa.mu.Lock()
|
|
release := sa.releaseFuncs[pin.PinID]
|
|
delete(sa.releaseFuncs, pin.PinID)
|
|
sa.mu.Unlock()
|
|
if release != nil {
|
|
release()
|
|
}
|
|
}
|
|
|
|
// PinWALRetention delegates to BlockVolPinner.HoldWALRetention.
|
|
func (sa *StorageAdapter) PinWALRetention(startLSN uint64) (engine.RetentionPin, error) {
|
|
release, err := sa.pinner.HoldWALRetention(startLSN)
|
|
if err != nil {
|
|
return engine.RetentionPin{}, fmt.Errorf("WAL retention pin at LSN %d: %w", startLSN, err)
|
|
}
|
|
id := sa.nextPinID.Add(1)
|
|
sa.mu.Lock()
|
|
sa.releaseFuncs[id] = release
|
|
sa.mu.Unlock()
|
|
return engine.RetentionPin{StartLSN: startLSN, PinID: id, Valid: true}, nil
|
|
}
|
|
|
|
// ReleaseWALRetention calls the held release function.
|
|
func (sa *StorageAdapter) ReleaseWALRetention(pin engine.RetentionPin) {
|
|
sa.mu.Lock()
|
|
release := sa.releaseFuncs[pin.PinID]
|
|
delete(sa.releaseFuncs, pin.PinID)
|
|
sa.mu.Unlock()
|
|
if release != nil {
|
|
release()
|
|
}
|
|
}
|
|
|
|
// PinFullBase delegates to BlockVolPinner.HoldFullBase.
|
|
func (sa *StorageAdapter) PinFullBase(committedLSN uint64) (engine.FullBasePin, error) {
|
|
release, err := sa.pinner.HoldFullBase(committedLSN)
|
|
if err != nil {
|
|
return engine.FullBasePin{}, fmt.Errorf("full base pin at LSN %d: %w", committedLSN, err)
|
|
}
|
|
id := sa.nextPinID.Add(1)
|
|
sa.mu.Lock()
|
|
sa.releaseFuncs[id] = release
|
|
sa.mu.Unlock()
|
|
return engine.FullBasePin{CommittedLSN: committedLSN, PinID: id, Valid: true}, nil
|
|
}
|
|
|
|
// ReleaseFullBase calls the held release function.
|
|
func (sa *StorageAdapter) ReleaseFullBase(pin engine.FullBasePin) {
|
|
sa.mu.Lock()
|
|
release := sa.releaseFuncs[pin.PinID]
|
|
delete(sa.releaseFuncs, pin.PinID)
|
|
sa.mu.Unlock()
|
|
if release != nil {
|
|
release()
|
|
}
|
|
}
|
|
|
|
// PushStorageAdapter is a test-only adapter that uses push-based state
|
|
// updates instead of pulling from a BlockVolReader. For use in tests
|
|
// that don't have real blockvol instances.
|
|
type PushStorageAdapter struct {
|
|
*StorageAdapter
|
|
state BlockVolState
|
|
}
|
|
|
|
// NewPushStorageAdapter creates a push-based adapter for tests.
|
|
func NewPushStorageAdapter() *PushStorageAdapter {
|
|
psa := &PushStorageAdapter{}
|
|
psa.StorageAdapter = NewStorageAdapter(&pushReader{psa: psa}, &pushPinner{psa: psa})
|
|
return psa
|
|
}
|
|
|
|
// UpdateState sets the adapter's state (push model for tests).
|
|
func (psa *PushStorageAdapter) UpdateState(state BlockVolState) {
|
|
psa.state = state
|
|
}
|
|
|
|
type pushReader struct{ psa *PushStorageAdapter }
|
|
|
|
func (pr *pushReader) ReadState() BlockVolState { return pr.psa.state }
|
|
|
|
type pushPinner struct{ psa *PushStorageAdapter }
|
|
|
|
func (pp *pushPinner) HoldWALRetention(startLSN uint64) (func(), error) {
|
|
if startLSN < pp.psa.state.WALTailLSN {
|
|
return nil, fmt.Errorf("WAL recycled past %d (tail=%d)", startLSN, pp.psa.state.WALTailLSN)
|
|
}
|
|
return func() {}, nil
|
|
}
|
|
|
|
func (pp *pushPinner) HoldSnapshot(checkpointLSN uint64) (func(), error) {
|
|
if !pp.psa.state.CheckpointTrusted || pp.psa.state.CheckpointLSN != checkpointLSN {
|
|
return nil, fmt.Errorf("no trusted checkpoint at %d", checkpointLSN)
|
|
}
|
|
return func() {}, nil
|
|
}
|
|
|
|
func (pp *pushPinner) HoldFullBase(_ uint64) (func(), error) {
|
|
return func() {}, nil
|
|
}
|