mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-09 15:57:47 +02:00
* refactor: centralize genUploadUrl in UploadOption Replace inline genFileUrlFn closures with operation.GenUploadUrl field: - Add GenUploadUrl func(host, fileId) string to UploadOption struct - Add GenUploadUrlProxy(filerAddress string) utility function - Remove genFileUrlFn parameter from UploadWithRetry signature - Update all callers: mount, gateway, mq, filer_copy, filer_sync This matches the weed mount -filerProxy pattern exactly, factorizing the URL generation logic across all consumers. Co-Authored-By: Athena 🏛️ <hermes-agent@local> (custom / Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf) * docker release: run all platform jobs in one wave, cache rocksdb compile Drop max-parallel so the 13 per-platform builds run together instead of two waves of 8 (rocksdb was queuing behind the cap and starting ~8 min late). Keep cache-to mode=max for rocksdb: its RocksDB static_lib compile is sha-independent, so it caches across releases and stops being the ~16-min long-pole that gates the merge fan-in. go-build variants stay mode=min. Co-Authored-By: Athena 🏛️ <hermes-agent@local> (custom / Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf) * refactor: centralize genUploadUrl in UploadOption Replace inline genFileUrlFn closures with operation.GenUploadUrl field: - Add GenUploadUrl func(host, fileId) string to UploadOption struct - Add GenUploadUrlProxy(filerAddress string) utility function - Remove genFileUrlFn parameter from UploadWithRetry signature - Update all callers: mount, gateway, mq, filer_copy, filer_sync This matches the weed mount -filerProxy pattern exactly, factorizing the URL generation logic across all consumers. Co-Authored-By: Athena 🏛️ <hermes-agent@local> (custom / Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf) * Remove accidental ROCmFPX submodule reference * gofmt chunk upload option block * Preserve broker cipher and re-read proxy filer per upload attempt Chunk uploads must keep the configured Cipher, and both the mount and broker current filer can change on failover, so build the proxy upload URL inside the closure instead of capturing the address once. --------- Co-authored-by: Chris Lu <chris.lu@gmail.com>
188 lines
6.2 KiB
Go
188 lines
6.2 KiB
Go
package broker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/filer"
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/mq"
|
|
"github.com/seaweedfs/seaweedfs/weed/operation"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
func (b *MessageQueueBroker) appendToFile(targetFile string, data []byte) error {
|
|
return b.appendToFileWithBufferIndex(targetFile, data, 0)
|
|
}
|
|
|
|
func (b *MessageQueueBroker) appendToFileWithBufferIndex(targetFile string, data []byte, bufferOffset int64, offsetArgs ...int64) error {
|
|
// Extract optional offset parameters (minOffset, maxOffset)
|
|
var minOffset, maxOffset int64
|
|
if len(offsetArgs) >= 2 {
|
|
minOffset = offsetArgs[0]
|
|
maxOffset = offsetArgs[1]
|
|
}
|
|
|
|
fileId, uploadResult, err2 := b.assignAndUpload(targetFile, data)
|
|
if err2 != nil {
|
|
return err2
|
|
}
|
|
|
|
// find out existing entry
|
|
fullpath := util.FullPath(targetFile)
|
|
dir, name := fullpath.DirAndName()
|
|
entry, err := filer_pb.GetEntry(context.Background(), b, fullpath)
|
|
var offset int64 = 0
|
|
if err == filer_pb.ErrNotFound {
|
|
entry = &filer_pb.Entry{
|
|
Name: name,
|
|
IsDirectory: false,
|
|
Attributes: &filer_pb.FuseAttributes{
|
|
Crtime: time.Now().Unix(),
|
|
Mtime: time.Now().Unix(),
|
|
FileMode: uint32(os.FileMode(0644)),
|
|
Uid: uint32(os.Getuid()),
|
|
Gid: uint32(os.Getgid()),
|
|
},
|
|
}
|
|
|
|
// Add buffer start offset for deduplication tracking (binary format)
|
|
if bufferOffset != 0 {
|
|
entry.Extended = make(map[string][]byte)
|
|
bufferStartBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(bufferStartBytes, uint64(bufferOffset))
|
|
entry.Extended[mq.ExtendedAttrBufferStart] = bufferStartBytes
|
|
}
|
|
|
|
// Add offset range metadata for Kafka integration
|
|
if minOffset > 0 && maxOffset >= minOffset {
|
|
if entry.Extended == nil {
|
|
entry.Extended = make(map[string][]byte)
|
|
}
|
|
minOffsetBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(minOffsetBytes, uint64(minOffset))
|
|
entry.Extended[mq.ExtendedAttrOffsetMin] = minOffsetBytes
|
|
|
|
maxOffsetBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(maxOffsetBytes, uint64(maxOffset))
|
|
entry.Extended[mq.ExtendedAttrOffsetMax] = maxOffsetBytes
|
|
}
|
|
} else if err != nil {
|
|
return fmt.Errorf("find %s: %v", fullpath, err)
|
|
} else {
|
|
offset = int64(filer.TotalSize(entry.GetChunks()))
|
|
|
|
// Verify buffer offset continuity for existing files (append operations)
|
|
if bufferOffset != 0 {
|
|
if entry.Extended == nil {
|
|
entry.Extended = make(map[string][]byte)
|
|
}
|
|
|
|
// Check for existing buffer start (binary format)
|
|
if existingData, exists := entry.Extended[mq.ExtendedAttrBufferStart]; exists {
|
|
if len(existingData) == 8 {
|
|
existingStartIndex := int64(binary.BigEndian.Uint64(existingData))
|
|
|
|
// Verify that the new buffer offset is consecutive
|
|
// Expected offset = start + number of existing chunks
|
|
expectedOffset := existingStartIndex + int64(len(entry.GetChunks()))
|
|
if bufferOffset != expectedOffset {
|
|
// This shouldn't happen in normal operation
|
|
// Log warning but continue (don't crash the system)
|
|
glog.Warningf("non-consecutive buffer offset for %s. Expected %d, got %d",
|
|
fullpath, expectedOffset, bufferOffset)
|
|
}
|
|
// Note: We don't update the start offset - it stays the same
|
|
}
|
|
} else {
|
|
// No existing buffer start, create new one (shouldn't happen for existing files)
|
|
bufferStartBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(bufferStartBytes, uint64(bufferOffset))
|
|
entry.Extended[mq.ExtendedAttrBufferStart] = bufferStartBytes
|
|
}
|
|
}
|
|
|
|
// Update offset range metadata for existing files
|
|
if minOffset > 0 && maxOffset >= minOffset {
|
|
// Update minimum offset if this chunk has a lower minimum
|
|
if existingMinData, exists := entry.Extended[mq.ExtendedAttrOffsetMin]; exists && len(existingMinData) == 8 {
|
|
existingMin := int64(binary.BigEndian.Uint64(existingMinData))
|
|
if minOffset < existingMin {
|
|
minOffsetBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(minOffsetBytes, uint64(minOffset))
|
|
entry.Extended[mq.ExtendedAttrOffsetMin] = minOffsetBytes
|
|
}
|
|
} else {
|
|
// No existing minimum, set it
|
|
minOffsetBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(minOffsetBytes, uint64(minOffset))
|
|
entry.Extended[mq.ExtendedAttrOffsetMin] = minOffsetBytes
|
|
}
|
|
|
|
// Update maximum offset if this chunk has a higher maximum
|
|
if existingMaxData, exists := entry.Extended[mq.ExtendedAttrOffsetMax]; exists && len(existingMaxData) == 8 {
|
|
existingMax := int64(binary.BigEndian.Uint64(existingMaxData))
|
|
if maxOffset > existingMax {
|
|
maxOffsetBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(maxOffsetBytes, uint64(maxOffset))
|
|
entry.Extended[mq.ExtendedAttrOffsetMax] = maxOffsetBytes
|
|
}
|
|
} else {
|
|
// No existing maximum, set it
|
|
maxOffsetBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(maxOffsetBytes, uint64(maxOffset))
|
|
entry.Extended[mq.ExtendedAttrOffsetMax] = maxOffsetBytes
|
|
}
|
|
}
|
|
}
|
|
|
|
// append to existing chunks
|
|
entry.Chunks = append(entry.GetChunks(), uploadResult.ToPbFileChunk(fileId, offset, time.Now().UnixNano()))
|
|
|
|
// update the entry
|
|
return b.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
|
|
return filer_pb.CreateEntry(context.Background(), client, &filer_pb.CreateEntryRequest{
|
|
Directory: dir,
|
|
Entry: entry,
|
|
})
|
|
})
|
|
}
|
|
|
|
func (b *MessageQueueBroker) assignAndUpload(targetFile string, data []byte) (fileId string, uploadResult *operation.UploadResult, err error) {
|
|
|
|
reader := util.NewBytesReader(data)
|
|
|
|
uploader, err := operation.NewUploader()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
uploadOption := &operation.UploadOption{
|
|
Cipher: b.option.Cipher,
|
|
}
|
|
if b.option.VolumeServerAccess == "filerProxy" {
|
|
// b.currentFiler can change on failover, so read it per attempt.
|
|
uploadOption.GenUploadUrl = func(host, fileId string) string {
|
|
return fmt.Sprintf("http://%s/?proxyChunkId=%s", b.currentFiler, fileId)
|
|
}
|
|
}
|
|
|
|
fileId, uploadResult, err, _ = uploader.UploadWithRetry(
|
|
b,
|
|
&filer_pb.AssignVolumeRequest{
|
|
Count: 1,
|
|
Replication: b.option.DefaultReplication,
|
|
Collection: "topics",
|
|
DataCenter: b.option.DataCenter,
|
|
Path: targetFile,
|
|
},
|
|
uploadOption,
|
|
reader,
|
|
)
|
|
return
|
|
}
|