feat(uds): include .dat file path in locate response

Add DatPathLen (2 bytes at offset 24) + variable-length DatPath after
the 32-byte header. Solves the filename mismatch where sra-volume
assumed {vol}.dat but SeaweedFS uses {collection}_{vol}.dat.

Backward compatible: old clients that only read 32 bytes still work
(DatPathLen is in the formerly-reserved region, and the trailing
path bytes are simply ignored by old readers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
pingqiu
2026-02-08 14:50:29 -08:00
co-authored by Claude Opus 4.6
parent c1c73b0a5d
commit cc634da5bd
2 changed files with 38 additions and 12 deletions
+32 -12
View File
@@ -15,8 +15,9 @@ import (
// UDS protocol constants
const (
UdsRequestSize = 24 // fid(16) + version(4) + flags(4)
UdsResponseSize = 32 // status(1) + pad(3) + volume_id(4) + offset(8) + length(8) + reserved(8)
UdsRequestSize = 24 // fid(16) + version(4) + flags(4)
UdsResponseSize = 32 // status(1) + pad(3) + volume_id(4) + offset(8) + length(8) + dat_path_len(2) + reserved(6)
UdsMaxDatPathLen = 256 // max .dat file path length
)
// UDS status codes
@@ -43,12 +44,14 @@ type LocateRequest struct {
// LocateResponse represents a UDS locate response
type LocateResponse struct {
Status uint8
_pad [3]byte
VolumeId uint32
Offset uint64
Length uint64
_ [8]byte // reserved
Status uint8
_pad [3]byte
VolumeId uint32
Offset uint64
Length uint64
DatPathLen uint16 // length of .dat file path (follows the 32-byte header)
_ [6]byte // reserved
DatPath string // variable-length .dat file path (not in wire header)
}
// NewUdsServer creates a new UDS server for the volume server
@@ -142,7 +145,7 @@ func (u *UdsServer) handleConnection(conn net.Conn) {
// Handle request
resp := u.handleLocate(&req)
// Serialize response
// Serialize response header (32 bytes)
respBuf[0] = resp.Status
respBuf[1] = 0
respBuf[2] = 0
@@ -150,13 +153,22 @@ func (u *UdsServer) handleConnection(conn net.Conn) {
binary.LittleEndian.PutUint32(respBuf[4:8], resp.VolumeId)
binary.LittleEndian.PutUint64(respBuf[8:16], resp.Offset)
binary.LittleEndian.PutUint64(respBuf[16:24], resp.Length)
// reserved bytes 24-32 are zero
binary.LittleEndian.PutUint16(respBuf[24:26], resp.DatPathLen)
// reserved bytes 26-32 are zero
// Write response
// Write fixed header
if _, err := conn.Write(respBuf); err != nil {
glog.V(2).Infof("UDS write error: %v", err)
return
}
// Write variable-length .dat path if present
if resp.DatPathLen > 0 {
if _, err := conn.Write([]byte(resp.DatPath)); err != nil {
glog.V(2).Infof("UDS write dat path error: %v", err)
return
}
}
}
}
@@ -228,6 +240,14 @@ func (u *UdsServer) handleLocate(req *LocateRequest) *LocateResponse {
resp.Offset = uint64(nv.Offset.ToActualOffset())
resp.Length = uint64(nv.Size)
glog.V(3).Infof("UDS: located %s -> vol=%d offset=%d size=%d", fid, volumeId, resp.Offset, resp.Length)
// Include .dat file path so sidecar knows the exact filename
// (SeaweedFS uses {collection}_{id}.dat when collection is non-empty)
datPath := v.DataFileName() + ".dat"
if len(datPath) <= UdsMaxDatPathLen {
resp.DatPath = datPath
resp.DatPathLen = uint16(len(datPath))
}
glog.V(3).Infof("UDS: located %s -> vol=%d offset=%d size=%d dat=%s", fid, volumeId, resp.Offset, resp.Length, datPath)
return resp
}
+6
View File
@@ -167,6 +167,12 @@ func TestUdsServerProtocol(t *testing.T) {
if status != UdsStatusError {
t.Errorf("Expected UdsStatusError (%d) for nil VolumeServer, got %d", UdsStatusError, status)
}
// DatPathLen should be 0 for error responses
datPathLen := binary.LittleEndian.Uint16(respBuf[24:26])
if datPathLen != 0 {
t.Errorf("Expected DatPathLen=0 for error, got %d", datPathLen)
}
}
// TestUdsSocketCleanup tests that existing socket is removed