Files
seaweedfs/test/erasure_coding
Chris Lu 37bf1cd91d volume: validate copy/tail source addresses before dialing (#11390)
* pb: stop exiting the process on malformed server addresses

ServerToGrpcAddress and GrpcAddressToServerAddress called glog.Fatalf
when hostAndPort could not parse the port, which os.Exit(255)ed the whole
process. A caller-supplied copy or tail source address reached this path
synchronously in the serving goroutine, so one anonymous VolumeCopy with
a non-numeric port terminated the volume server.

Log the parse error and return the input unchanged instead: the dial or
request that consumes the address then fails as an ordinary error.

* volume: validate copy and tail source addresses before dialing

VolumeCopy, VolumeEcShardsCopy and VolumeTailReceiver dial a
caller-supplied source address (SourceDataNode / SourceVolumeServer)
with no endpoint validation, so an anonymous caller could aim the volume
server at loopback, link-local (cloud metadata) or other unintended
destinations and read dial behavior back as a connectivity oracle.

Apply the same peer-target deny list FetchAndWriteNeedle uses for
replica targets: the source must be a bare host:port whose host is not
loopback, link-local or unspecified; cluster peers stay reachable on
private networks, and -volume.allowUntrustedRemoteEndpoints opts out.
The loopback-using copy tests set the flag to keep exercising the copy
path in process.

* rust volume: validate copy and tail source addresses before dialing

Mirror the Go guard on the Rust volume server: volume_copy,
volume_ec_shards_copy and volume_tail_receiver dial a caller-supplied
source address, so run it through validate_replica_target first (bare
host:port; no loopback, link-local or unspecified hosts; private peers
stay allowed). --volume.allowUntrustedRemoteEndpoints opts out; the test
fixture and the Rust test-cluster launcher set it so loopback sources in
tests keep working.

* volume: pin validated copy/tail source addresses at dial time

validateReplicaTarget resolves the source hostname once, but the gRPC
client resolved it again at connect, leaving a DNS-rebinding window for
hostname sources. The copy and tail source dials now run through the
same guardedDialerPolicy the remote-storage path uses, so every resolved
address is re-checked against the replica deny list (private peers
allowed) immediately before the TCP connect. guardedDialerPolicy also
moves to util.OutboundDialContext so the guarded path keeps the -ip.bind
source binding the default gRPC dialer had.

The Rust volume server mirrors this with connect_guarded, a tonic
connector that resolves, re-checks each address, and connects to the
first passing IP; handlers use it whenever the untrusted-endpoint
opt-out is off. A handler-level test now exercises the enabled
validation branches for all three source-taking RPCs.

* pb: return empty server address for malformed grpc addresses

GrpcAddressToServerAddress used to return the unparseable input on a
hostAndPort failure, so a malformed raft address (e.g. "host:abc")
flowed into admin dashboard master maps unchanged. Return an empty
string instead, skip empty conversions at the two raft-cluster merge
sites, and drop the now-stale comment about the fatal exit the earlier
commit removed.

* test: opt erasure-coding loopback clusters out of the remote endpoint guard

The erasure-coding suites drive VolumeEcShardsCopy / VolumeCopy between
volume servers bound to 127.0.0.1, which the copy/tail source guard now
rejects by default. Pass -volume.allowUntrustedRemoteEndpoints to the
test volume launches, matching what the volume_server framework
harnesses already do.

* admin: only claim fallback master leadership on an empty raft response

A nonempty RaftListClusterServers response whose entries were all
rejected left masterMap empty, so the fallback marked the reachable
current master as leader the same way a genuinely empty (non-raft)
response does. Track whether the successful response returned zero
servers and only promote the fallback master then.
2026-09-18 12:55:47 -07:00
..

Erasure Coding Integration Tests

This directory contains integration tests for the EC (Erasure Coding) encoding volume location timing bug fix.

The Bug

The bug caused double storage usage during EC encoding because:

  1. Silent failure: Functions returned nil instead of proper error messages
  2. Timing race condition: Volume locations were collected AFTER EC encoding when master metadata was already updated
  3. Missing cleanup: Original volumes weren't being deleted after EC encoding

This resulted in both original .dat files AND EC .ec00-.ec13 files coexisting, effectively doubling storage usage.

The Fix

The fix addresses all three issues:

  1. Fixed silent failures: Updated doDeleteVolumes() and doEcEncode() to return proper errors
  2. Fixed timing race condition: Created doDeleteVolumesWithLocations() that uses pre-collected volume locations
  3. Enhanced cleanup: Volume locations are now collected BEFORE EC encoding, preventing the race condition

Integration Tests

TestECEncodingVolumeLocationTimingBug

The main integration test that:

  • Simulates master timing race condition: Tests what happens when volume locations are read from master AFTER EC encoding has updated the metadata
  • Verifies fix effectiveness: Checks for the "Collecting volume locations...before EC encoding" message that proves the fix is working
  • Tests multi-server distribution: Runs EC encoding with 6 volume servers to test shard distribution
  • Validates cleanup: Ensures original volumes are properly cleaned up after EC encoding

TestECEncodingMasterTimingRaceCondition

A focused test that specifically targets the master metadata timing race condition:

  • Simulates the exact race condition: Tests volume location collection timing relative to master metadata updates
  • Detects timing fix: Verifies that volume locations are collected BEFORE EC encoding starts
  • Demonstrates bug impact: Shows what happens when volume locations are unavailable after master metadata update

TestECEncodingRegressionPrevention

Regression tests that ensure:

  • Function signatures: Fixed functions still exist and return proper errors
  • Timing patterns: Volume location collection happens in the correct order

Test Architecture

The tests use:

  • Real SeaweedFS cluster: 1 master server + 6 volume servers
  • Multi-server setup: Tests realistic EC shard distribution across multiple servers
  • Timing simulation: Goroutines and delays to simulate race conditions
  • Output validation: Checks for specific log messages that prove the fix is working

Why Integration Tests Were Necessary

Unit tests could not catch this bug because:

  1. Race condition: The bug only occurred in real-world timing scenarios
  2. Master-volume server interaction: Required actual master metadata updates
  3. File system operations: Needed real volume creation and EC shard generation
  4. Cleanup timing: Required testing the sequence of operations in correct order

The integration tests successfully catch the timing bug by:

  • Testing real command execution: Uses actual ec.encode shell command
  • Simulating race conditions: Creates timing scenarios that expose the bug
  • Validating output messages: Checks for the key "Collecting volume locations...before EC encoding" message
  • Monitoring cleanup behavior: Ensures original volumes are properly deleted

Running the Tests

# Run all integration tests
go test -v

# Run only the main timing test
go test -v -run TestECEncodingVolumeLocationTimingBug

# Run only the race condition test
go test -v -run TestECEncodingMasterTimingRaceCondition

# Skip integration tests (short mode)
go test -v -short

Manual Testing with Makefile

A Makefile is provided for manual EC testing.

Requirements: curl, jq (command-line JSON processor)

# Quick start: start cluster and populate data
make setup

# Open weed shell to run EC commands
make shell

# Individual targets
make start      # Start test cluster (master + 6 volume servers + filer)
make stop       # Stop test cluster
make populate   # Populate ~300MB of test data
make status     # Show cluster and EC shard status
make clean      # Stop cluster and remove all test data
make help       # Show all targets

EC Rebalance Limited Slots (Unit Test)

The "no free ec shard slots" issue is tested with a unit test that works directly on topology data structures without requiring a running cluster.

Location: weed/shell/ec_rebalance_slots_test.go

Tests included:

  • TestECRebalanceWithLimitedSlots: Tests a topology with 6 servers, 7 EC volumes (98 shards)
  • TestECRebalanceZeroFreeSlots: Reproduces the exact 0 free slots scenario

Known Issue: When volume servers are at capacity (volumeCount == maxVolumeCount), the rebalance step fails with "no free ec shard slots" instead of recognizing that moving shards frees slots on source servers.

Test Results

With the fix: Shows "Collecting volume locations for N volumes before EC encoding..." message Without the fix: No collection message, potential timing race condition

The tests demonstrate that the fix prevents the volume location timing bug that caused double storage usage in EC encoding operations.