VOLUME_SERVER_RUST_PLAN.md
| Component | Go Source | Lines (non-test) | Description |
|---|---|---|---|
| CLI & startup | weed/command/volume.go | 476 | ~40 CLI flags, server bootstrap |
| HTTP server + handlers | weed/server/volume_server*.go | 1,517 | Struct, routes, read/write/delete handlers |
| gRPC handlers | weed/server/volume_grpc_*.go | 3,073 | 40 RPC method implementations |
| Storage engine | weed/storage/ | 15,271 | Volumes, needles, index, compaction, EC, backend |
| Protobuf definitions | weed/pb/volume_server.proto | 759 | Service + message definitions |
| Shared utilities | weed/security/, weed/stats/, weed/util/ | ~2,000+ | JWT, TLS, metrics, helpers |
| Total | ~23,000+ |
seaweed-volume/
├── Cargo.toml
├── build.rs # protobuf codegen
├── proto/
│ ├── volume_server.proto # copied from Go, adapted
│ └── remote.proto
├── src/
│ ├── main.rs # CLI entry point
│ ├── config.rs # CLI flags + config
│ ├── server/
│ │ ├── mod.rs
│ │ ├── volume_server.rs # VolumeServer struct + lifecycle
│ │ ├── http_handlers.rs # HTTP route dispatch
│ │ ├── http_read.rs # GET/HEAD handlers
│ │ ├── http_write.rs # POST/PUT handlers
│ │ ├── http_delete.rs # DELETE handler
│ │ ├── http_admin.rs # /status, /healthz, /ui
│ │ ├── grpc_service.rs # gRPC trait impl dispatch
│ │ ├── grpc_vacuum.rs
│ │ ├── grpc_copy.rs
│ │ ├── grpc_erasure_coding.rs
│ │ ├── grpc_tail.rs
│ │ ├── grpc_admin.rs
│ │ ├── grpc_read_write.rs
│ │ ├── grpc_batch_delete.rs
│ │ ├── grpc_scrub.rs
│ │ ├── grpc_tier.rs
│ │ ├── grpc_remote.rs
│ │ ├── grpc_query.rs
│ │ ├── grpc_state.rs
│ │ └── grpc_client_to_master.rs # heartbeat
│ ├── storage/
│ │ ├── mod.rs
│ │ ├── store.rs # Store (multi-disk manager)
│ │ ├── volume.rs # Volume struct + lifecycle
│ │ ├── volume_read.rs
│ │ ├── volume_write.rs
│ │ ├── volume_compact.rs
│ │ ├── volume_info.rs
│ │ ├── needle/
│ │ │ ├── mod.rs
│ │ │ ├── needle.rs # Needle struct + serialization
│ │ │ ├── needle_read.rs
│ │ │ ├── needle_write.rs
│ │ │ ├── needle_map.rs # in-memory NeedleMap
│ │ │ ├── needle_value.rs
│ │ │ └── crc.rs
│ │ ├── super_block.rs
│ │ ├── idx/
│ │ │ ├── mod.rs
│ │ │ └── idx.rs # .idx file format read/write
│ │ ├── needle_map_leveldb.rs
│ │ ├── types.rs # NeedleId, Offset, Size, DiskType
│ │ ├── disk_location.rs # DiskLocation per-directory
│ │ ├── erasure_coding/
│ │ │ ├── mod.rs
│ │ │ ├── ec_volume.rs
│ │ │ ├── ec_shard.rs
│ │ │ ├── ec_encoder.rs # Reed-Solomon encoding
│ │ │ └── ec_decoder.rs
│ │ └── backend/
│ │ ├── mod.rs
│ │ ├── disk.rs
│ │ └── s3_backend.rs # tiered storage to S3
│ ├── topology/
│ │ └── volume_layout.rs # replication placement
│ ├── security/
│ │ ├── mod.rs
│ │ ├── guard.rs # whitelist + JWT gate
│ │ ├── jwt.rs
│ │ └── tls.rs
│ ├── stats/
│ │ ├── mod.rs
│ │ └── metrics.rs # Prometheus counters/gauges
│ └── util/
│ ├── mod.rs
│ ├── grpc.rs
│ ├── http.rs
│ └── file.rs
└── tests/
├── integration/
│ ├── http_read_test.rs
│ ├── http_write_test.rs
│ ├── grpc_test.rs
│ └── storage_test.rs
└── unit/
├── needle_test.rs
├── idx_test.rs
├── super_block_test.rs
└── ec_test.rs
| Purpose | Crate |
|---|---|
| Async runtime | tokio |
| gRPC | tonic + prost |
| HTTP server | hyper + axum |
| CLI parsing | clap (derive) |
| Prometheus metrics | prometheus |
| JWT | jsonwebtoken |
| TLS | rustls + tokio-rustls |
| LevelDB | rusty-leveldb or rocksdb |
| Reed-Solomon EC | reed-solomon-erasure |
| Logging | tracing + tracing-subscriber |
| Config (security.toml) | toml + serde |
| CRC32 | crc32fast |
| Memory-mapped files | memmap2 |
Goal: Cargo project compiles, proto codegen works, CLI parses all flags.
Steps:
1.1. Create seaweed-volume/Cargo.toml with all dependencies listed above.
1.2. Copy volume_server.proto and remote.proto into proto/. Adjust package paths for Rust codegen.
1.3. Create build.rs using tonic-build to compile .proto files into Rust types.
1.4. Create src/main.rs with clap derive structs mirroring all 40 CLI flags from weed/command/volume.go:
--port (default 8080)--port.grpc (default 0 → 10000+port)--port.public (default 0 → same as port)--ip (auto-detect)--id (default empty → ip:port)--publicUrl--ip.bind--master (default "localhost:9333")--mserver (deprecated compat)--preStopSeconds (default 10)--idleTimeout (default 30)--dataCenter--rack--index [memory|leveldb|leveldbMedium|leveldbLarge]--disk [hdd|ssd|<tag>]--tags--dir (default temp dir)--dir.idx--max (default "8")--whiteList--minFreeSpacePercent (default "1")--minFreeSpace--images.fix.orientation (default false)--readMode [local|proxy|redirect] (default "proxy")--cpuprofile--memprofile--compactionMBps (default 0)--maintenanceMBps (default 0)--fileSizeLimitMB (default 256)--concurrentUploadLimitMB (default 0)--concurrentDownloadLimitMB (default 0)--pprof (default false)--metricsPort (default 0)--metricsIp--inflightUploadDataTimeout (default 60s)--inflightDownloadDataTimeout (default 60s)--hasSlowRead (default true)--readBufferSizeMB (default 4)--index.leveldbTimeout (default 0)--debug (default false)--debug.port (default 6060)1.5. Implement the same flag validation logic from startVolumeServer():
--dir, --max, --minFreeSpace, --disk, --tags--mserver backward compat1.6. Test: cargo build succeeds. cargo run -- --help shows all flags. Proto types generated.
Verification: Run with --port 8080 --dir /tmp --master localhost:9333 — should parse without error and print config.
Goal: Read and write the SeaweedFS needle/volume binary format bit-for-bit compatible with Go.
Source files to port:
weed/storage/types/needle_types.go → src/storage/types.rsweed/storage/needle/needle.go → src/storage/needle/needle.rsweed/storage/needle/needle_read.go → src/storage/needle/needle_read.rsweed/storage/needle/needle_write.go (partial) → src/storage/needle/needle_write.rsweed/storage/needle/crc.go → src/storage/needle/crc.rsweed/storage/needle/needle_value_map.go → src/storage/needle/needle_value.rsweed/storage/super_block/super_block.go → src/storage/super_block.rsweed/storage/idx/ → src/storage/idx/Steps:
2.1. Fundamental types (types.rs):
NeedleId (u64), Offset (u32 or u64 depending on version), Size (i32, negative = deleted)Cookie (u32)DiskType enum (HDD, SSD, Custom)binary.BigEndian encoding2.2. SuperBlock (super_block.rs):
ReplicaPlacement struct with same/diff rack/dc countsTTL struct with count + unit.dat filesuper_block.go2.3. Needle binary format (needle.rs, needle_read.rs):
crc32.ChecksumIEEE)2.4. Idx file format (idx/):
2.5. NeedleMap (in-memory) (needle_map.rs):
.idx file on volume mount2.6. Tests:
.dat/.idx files in tests/fixtures/)Goal: Mount, read from, write to, and unmount a volume.
Source files to port:
weed/storage/volume.go → src/storage/volume.rsweed/storage/volume_read.go → src/storage/volume_read.rsweed/storage/volume_write.go → src/storage/volume_write.rsweed/storage/volume_loading.goweed/storage/volume_vacuum.go → src/storage/volume_compact.rsweed/storage/volume_info/volume_info.go → src/storage/volume_info.rsweed/storage/volume_super_block.goSteps:
3.1. Volume struct (volume.rs):
noWriteOrDelete / noWriteCanDelete / readOnly state flags.dat file (read + append)RwLock for concurrent reads, exclusive writes3.2. Volume loading — exact logic from volume_loading.go:
.dat file, read SuperBlock from first 8 bytes.idx file into NeedleMap.vif (VolumeInfo) JSON sidecar file3.3. Volume read (volume_read.rs) — from volume_read.go:
ReadNeedle(needleId, cookie): lookup in NeedleMap → seek in .dat → read needle bytes → verify cookie + CRC → return dataReadNeedleBlob(offset, size): raw blob readReadNeedleMeta(needleId, offset, size): read metadata only3.4. Volume write (volume_write.rs) — from volume_write.go:
WriteNeedle(needle): serialize needle → append to .dat → update .idx → update NeedleMapDeleteNeedle(needleId): mark as deleted in NeedleMap + append tombstone to .idx3.5. Volume compaction (volume_compact.rs) — from volume_vacuum.go:
CheckCompact(): compute garbage ratioCompact(): create new .dat/.idx, copy only live needles, update compact revisionCommitCompact(): rename compacted files over originalsCleanupCompact(): remove temp filescompactionBytePerSecond3.6. Volume info (volume_info.rs):
.vif JSON sidecar3.7. Tests:
Goal: Manage multiple volumes across multiple disk directories.
Source files to port:
weed/storage/store.go → src/storage/store.rsweed/storage/disk_location.go → src/storage/disk_location.rsweed/storage/store_ec.goweed/storage/store_state.goSteps:
4.1. DiskLocation (disk_location.rs):
4.2. Store (store.rs):
DiskLocationsGetVolume(volumeId) → lookup across all locationsHasVolume(volumeId) checkAllocateVolume(...) — create new volume in appropriate locationDeleteVolume(...), MountVolume(...), UnmountVolume(...)DeleteCollection(collection) — delete all volumes of a collectionSetStopping(), Close()store_state.go4.3. Store state — VolumeServerState protobuf with maintenance flag, persisted to disk.
4.4. Tests:
Goal: Full EC shard encode/decode/read/write/rebuild.
Source files to port:
weed/storage/erasure_coding/ (3,599 lines)Steps:
5.1. EC volume + shard structs — EcVolume, EcShard with file handles for .ec00–.ec13 shard files + .ecx index + .ecj journal.
5.2. EC encoder — Reed-Solomon 10+4 (configurable) encoding using reed-solomon-erasure crate:
VolumeEcShardsGenerate: read .dat → split into data shards → compute parity → write .ec00-.ec13 + .ecx5.3. EC decoder/reader — reconstruct data from any 10 of 14 shards:
EcShardRead: read range from a specific shard5.4. EC shard operations:
VolumeEcShardsRebuild: rebuild missing shards from remainingVolumeEcShardsToVolume: reconstruct .dat from EC shardsVolumeEcBlobDelete: mark deleted in EC journalVolumeEcShardsInfo: report shard metadata5.5. Tests:
Goal: Support tiered storage to remote backends (S3, etc).
Source files to port:
weed/storage/backend/ (1,850 lines)Steps:
6.1. Backend trait — abstract BackendStorage trait with ReadAt, WriteAt, Truncate, Close, Name.
6.2. Disk backend — default local disk implementation.
6.3. S3 backend — upload .dat to S3, read ranges via S3 range requests.
6.4. Tier move operations:
VolumeTierMoveDatToRemote: upload .dat to remote, optionally delete localVolumeTierMoveDatFromRemote: download .dat from remote6.5. Tests:
Goal: JWT authentication, whitelist guard, TLS configuration.
Source files to port:
weed/security/guard.go → src/security/guard.rsweed/security/jwt.go → src/security/jwt.rsweed/security/tls.go → src/security/tls.rsSteps:
7.1. Guard (guard.rs):
r.RemoteAddr)UpdateWhiteList() for live reload7.2. JWT (jwt.rs):
SeaweedFileIdClaims with fid fieldGetJwt(request) — extract from Authorization: Bearer header or jwt query param7.3. TLS (tls.rs):
security.toml config (same format as Go's viper config)7.4. Tests:
Goal: Export same metric names as Go for dashboard compatibility.
Source files to port:
weed/stats/metrics.go (volume server counters/gauges/histograms)Steps:
8.1. Define all Prometheus metrics matching Go names:
VolumeServerRequestCounter (labels: method, status)VolumeServerRequestHistogram (labels: method)VolumeServerInFlightRequestsGauge (labels: method)VolumeServerInFlightUploadSizeVolumeServerInFlightDownloadSizeVolumeServerConcurrentUploadLimitVolumeServerConcurrentDownloadLimitVolumeServerHandlerCounter (labels: type — UploadLimitCond, DownloadLimitCond)8.2. Metrics HTTP endpoint on --metricsPort.
8.3. Optional push-based metrics loop (LoopPushingMetric).
8.4. Test: Verify metric names and labels match Go output.
Goal: All HTTP endpoints with exact same behavior as Go.
Source files to port:
weed/server/volume_server.go → src/server/volume_server.rsweed/server/volume_server_handlers.go → src/server/http_handlers.rsweed/server/volume_server_handlers_read.go → src/server/http_read.rsweed/server/volume_server_handlers_write.go → src/server/http_write.rsweed/server/volume_server_handlers_admin.go → src/server/http_admin.rsweed/server/volume_server_handlers_helper.go (URL parsing, proxy, JSON responses)weed/server/volume_server_handlers_ui.go → src/server/http_admin.rsSteps:
9.1. URL path parsing — from handlers_helper.go:
/<vid>,<fid> and /<vid>/<fid> patterns9.2. Route dispatch — from privateStoreHandler and publicReadOnlyHandler:
GET / → GetOrHeadHandlerHEAD / → GetOrHeadHandlerPOST / → PostHandler (whitelist gated)PUT / → PostHandler (whitelist gated)DELETE / → DeleteHandler (whitelist gated)OPTIONS / → CORS preflightGET /status → JSON statusGET /healthz → health checkGET /ui/index.html → HTML UI page9.3. GET/HEAD handler (http_read.rs) — from handlers_read.go (468 lines):
9.4. POST/PUT handler (http_write.rs) — from handlers_write.go (170 lines):
DistributedOperation)9.5. DELETE handler — already in handlers.go:
9.6. Admin handlers (http_admin.rs):
/status → JSON with volumes, version, disk status/healthz → 200 OK if serving/ui/index.html → HTML dashboard9.7. Concurrency limiting — from handlers.go:
sync::Condvar + timeout9.8. Public port — if configured, separate listener with read-only routes (GET/HEAD/OPTIONS only).
9.9. Request ID middleware — generate unique request ID per request.
9.10. Tests:
Goal: All 40 gRPC methods with exact logic.
Source files to port:
weed/server/volume_grpc_admin.go (380 lines)weed/server/volume_grpc_vacuum.go (124 lines)weed/server/volume_grpc_copy.go (636 lines)weed/server/volume_grpc_copy_incremental.go (66 lines)weed/server/volume_grpc_read_write.go (74 lines)weed/server/volume_grpc_batch_delete.go (124 lines)weed/server/volume_grpc_tail.go (140 lines)weed/server/volume_grpc_erasure_coding.go (619 lines)weed/server/volume_grpc_scrub.go (121 lines)weed/server/volume_grpc_tier_upload.go (98 lines)weed/server/volume_grpc_tier_download.go (85 lines)weed/server/volume_grpc_remote.go (95 lines)weed/server/volume_grpc_query.go (69 lines)weed/server/volume_grpc_state.go (26 lines)weed/server/volume_grpc_read_all.go (35 lines)weed/server/volume_grpc_client_to_master.go (325 lines)Steps (grouped by functional area):
10.1. Implement tonic::Service for VolumeServer — the generated trait from proto.
10.2. Admin RPCs (grpc_admin.rs):
AllocateVolume — create volume on appropriate disk locationVolumeMount / VolumeUnmount / VolumeDeleteVolumeMarkReadonly / VolumeMarkWritableVolumeConfigure — change replicationVolumeStatus — return read-only, size, file countsVolumeServerStatus — disk statuses, memory, version, DC, rackVolumeServerLeave — deregister from masterDeleteCollectionVolumeNeedleStatus — get needle metadata by IDPing — latency measurementGetState / SetState — maintenance mode10.3. Vacuum RPCs (grpc_vacuum.rs):
VacuumVolumeCheck — return garbage ratioVacuumVolumeCompact — stream progress (streaming response)VacuumVolumeCommit — finalize compactionVacuumVolumeCleanup — remove temp files10.4. Copy RPCs (grpc_copy.rs):
VolumeCopy — stream .dat/.idx from source to create local copyVolumeSyncStatus — return sync metadataVolumeIncrementalCopy — stream .dat delta since timestamp (streaming)CopyFile — generic file copy by extension (streaming)ReceiveFile — receive streamed file (client streaming)ReadVolumeFileStatus — return file timestamps and sizes10.5. Read/Write RPCs (grpc_read_write.rs):
ReadNeedleBlob — raw needle blob readReadNeedleMeta — needle metadataWriteNeedleBlob — raw needle blob writeReadAllNeedles — stream all needles from volume(s) (streaming)10.6. Batch delete (grpc_batch_delete.rs):
BatchDelete — delete multiple file IDs, return per-ID results10.7. Tail RPCs (grpc_tail.rs):
VolumeTailSender — stream new needles since timestamp (streaming)VolumeTailReceiver — connect to another volume server and tail its changes10.8. Erasure coding RPCs (grpc_erasure_coding.rs):
VolumeEcShardsGenerate — generate EC shards from volumeVolumeEcShardsRebuild — rebuild missing shardsVolumeEcShardsCopy — copy shards from another serverVolumeEcShardsDelete — delete EC shardsVolumeEcShardsMount / VolumeEcShardsUnmountVolumeEcShardRead — read from EC shard (streaming)VolumeEcBlobDelete — mark blob deleted in EC volumeVolumeEcShardsToVolume — reconstruct volume from EC shardsVolumeEcShardsInfo — return shard metadata10.9. Scrub RPCs (grpc_scrub.rs):
ScrubVolume — integrity check volumes (INDEX / FULL / LOCAL modes)ScrubEcVolume — integrity check EC volumes10.10. Tier RPCs (grpc_tier.rs):
VolumeTierMoveDatToRemote — upload to remote backend (streaming progress)VolumeTierMoveDatFromRemote — download from remote (streaming progress)10.11. Remote storage (grpc_remote.rs):
FetchAndWriteNeedle — fetch from remote storage, write locally, replicate10.12. Query (grpc_query.rs):
Query — experimental CSV/JSON/Parquet select on stored data (streaming)10.13. Master heartbeat (grpc_client_to_master.rs):
heartbeat() background task — periodic gRPC stream to masterStopHeartbeat() for graceful shutdown10.14. Tests:
Goal: Full server startup matching Go's runVolume() and startVolumeServer().
Steps:
11.1. Startup sequence (match volume.go exactly):
security.tomlVolumeServer structStore (loads all existing volumes from disk)Guard11.2. Graceful shutdown (match Go exactly):
preStopSecondsvolumeServer.Shutdown() → store.Close() (flush all volumes)11.3. Reload (SIGHUP):
11.4. Tests:
Goal: Rust volume server is a drop-in replacement for Go volume server.
Steps:
12.1. Binary compatibility tests:
12.2. API compatibility tests:
12.3. Master interop test:
12.4. Performance benchmarks:
12.5. Edge cases:
Phase 1 (Skeleton + CLI) ← no deps, start here
↓
Phase 2 (Storage types) ← needs Phase 1 (types used everywhere)
↓
Phase 3 (Volume struct) ← needs Phase 2
↓
Phase 4 (Store manager) ← needs Phase 3
↓
Phase 7 (Security) ← independent, can parallel with 3-4
Phase 8 (Metrics) ← independent, can parallel with 3-4
↓
Phase 9 (HTTP server) ← needs Phase 4 + 7 + 8
Phase 10 (gRPC server) ← needs Phase 4 + 7 + 8
↓
Phase 5 (Erasure coding) ← needs Phase 4, wire into Phase 10
Phase 6 (Tiered storage) ← needs Phase 4, wire into Phase 10
↓
Phase 11 (Startup + shutdown) ← needs Phase 9 + 10
↓
Phase 12 (Integration tests) ← needs all above
| Phase | Estimated Rust Lines | Complexity |
|---|---|---|
| 1. Skeleton + CLI | ~400 | Low |
| 2. Storage types | ~2,000 | High (binary compat critical) |
| 3. Volume struct | ~2,500 | High |
| 4. Store manager | ~1,000 | Medium |
| 5. Erasure coding | ~3,000 | High |
| 6. Tiered storage | ~1,500 | Medium |
| 7. Security | ~500 | Medium |
| 8. Metrics | ~300 | Low |
| 9. HTTP server | ~2,000 | High |
| 10. gRPC server | ~3,500 | High |
| 11. Startup/shutdown | ~500 | Medium |
| 12. Integration tests | ~2,000 | Medium |
| Total | ~19,000 |
.dat, .idx, .vif, .ecX files identically to Go. A single byte off = data loss.crc32.ChecksumIEEE).