mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-01 12:16:07 +00:00
V2 runtime packages: - sw-block/runtime/masterv2: identity authority (desired state, heartbeat handling, promotion arbitration via SelectPromotionCandidate) - sw-block/runtime/volumev2: per-volume micro-cluster shell (node, orchestrator, control session, iSCSI frontend, takeover gate, failover session + driver, replica summary reconstruction) - sw-block/runtime/purev2: RF1 execution shell (engine + store + dispatcher + local boundary observations) - sw-block/runtime/protocolv2: three-channel separation (heartbeat/assignment/query + replica summary) V2 binaries: - sw-block/cmd/v2singleblock: single-node RF1 block server - sw-block/cmd/purev2rf1: minimal RF1 runtime binary Milestone capabilities: - RF1 write/read/sync with engine-driven mode projection - masterv2 ↔ volumev2 heartbeat convergence + assignment reissue - Promotion query with fresh CommittedLSN/WALHeadLSN evidence - Replica summary for bounded takeover reconstruction - Primary-loss reconstruction from peer summaries (fail-closed gate) - In-process failover driver with session observability - Local boundary observations feed engine (Committed/Durable/Checkpoint) Design docs: - v2-two-loop-protocol.md: identity vs data-control separation - v2-automata-ownership-map.md: event/command ownership split - v2-loop1-surface-draft.md: heartbeat/query/assignment field spec - v2-volumev2-single-node-mvp.md: target layering - v2-kernel-closure-review.md: per-volume micro-cluster principle - v2-pure-runtime-rf1-bootstrap.md, v2-capability-map.md, v2-proof-and-retest-pyramid.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/sw-block/runtime/purev2"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/blockvol"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
usage()
|
|
os.Exit(2)
|
|
}
|
|
|
|
switch os.Args[1] {
|
|
case "bootstrap":
|
|
if err := runBootstrap(os.Args[2:]); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
case "status":
|
|
if err := runStatus(os.Args[2:]); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
default:
|
|
usage()
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
|
|
func runBootstrap(args []string) error {
|
|
fs := flag.NewFlagSet("bootstrap", flag.ContinueOnError)
|
|
path := fs.String("path", "", "block volume path")
|
|
sizeBytes := fs.Uint64("size-bytes", 1*1024*1024, "logical volume size in bytes")
|
|
blockSize := fs.Uint("block-size", 4096, "block size in bytes")
|
|
walSize := fs.Uint64("wal-size", 256*1024, "wal size in bytes")
|
|
epoch := fs.Uint64("epoch", 1, "assignment epoch")
|
|
leaseMs := fs.Int("lease-ms", 30000, "lease ttl in milliseconds")
|
|
if err := fs.Parse(args); err != nil {
|
|
return err
|
|
}
|
|
if *path == "" {
|
|
return fmt.Errorf("bootstrap: --path is required")
|
|
}
|
|
|
|
rt := purev2.New(purev2.Config{})
|
|
defer rt.Close()
|
|
|
|
opts := blockvol.CreateOptions{
|
|
VolumeSize: *sizeBytes,
|
|
BlockSize: uint32(*blockSize),
|
|
WALSize: *walSize,
|
|
}
|
|
if err := rt.BootstrapPrimary(*path, opts, *epoch, time.Duration(*leaseMs)*time.Millisecond); err != nil {
|
|
return err
|
|
}
|
|
snap, err := rt.Snapshot(*path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return printJSON(snap)
|
|
}
|
|
|
|
func runStatus(args []string) error {
|
|
fs := flag.NewFlagSet("status", flag.ContinueOnError)
|
|
path := fs.String("path", "", "block volume path")
|
|
epoch := fs.Uint64("epoch", 0, "optional epoch to rebuild core projection")
|
|
leaseMs := fs.Int("lease-ms", 30000, "lease ttl in milliseconds")
|
|
if err := fs.Parse(args); err != nil {
|
|
return err
|
|
}
|
|
if *path == "" {
|
|
return fmt.Errorf("status: --path is required")
|
|
}
|
|
|
|
rt := purev2.New(purev2.Config{})
|
|
defer rt.Close()
|
|
|
|
if err := rt.OpenVolume(*path); err != nil {
|
|
return err
|
|
}
|
|
if *epoch > 0 {
|
|
if err := rt.ApplyPrimaryAssignment(*path, *epoch, time.Duration(*leaseMs)*time.Millisecond); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
snap, err := rt.Snapshot(*path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return printJSON(snap)
|
|
}
|
|
|
|
func printJSON(v any) error {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
return enc.Encode(v)
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Fprintln(os.Stderr, "usage:")
|
|
fmt.Fprintln(os.Stderr, " purev2rf1 bootstrap --path <file> [--size-bytes N --block-size N --wal-size N --epoch N]")
|
|
fmt.Fprintln(os.Stderr, " purev2rf1 status --path <file> [--epoch N]")
|
|
}
|