mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
* pb: add multipart concurrency fields to RemoteConf and tier move requests RemoteConf gains upload_concurrency/download_concurrency (0 = client default); VolumeTierMoveDatToRemote/FromRemote requests gain a concurrency field (0 = backend default). * remote storage: honor RemoteConf upload/download concurrency in s3 and azure clients s3 client: ReadFile passes conf download_concurrency to the downloader, WriteFile uses upload_concurrency for the uploader; previously hard-coded 1 upload / 5 download parts. 0 keeps defaults. Same for azure client. * storage: plumb concurrency through backend interface and tier upload/download BackendStorage.CopyFile/DownloadFile take a concurrency hint (<=0 = backend configured default); s3 backend reads upload_concurrency/download_concurrency from scaffold config with parseConcurrency fallback, rclone updated to the new signature. Tier move gRPC handlers forward the request concurrency to the backend. * shell: -upload_concurrency/-download_concurrency for remote.configure, -concurrent for volume.tier remote.configure exposes upload/download concurrency persisted into RemoteConf; volume.tier move/evict commands forward -concurrent to the tier move requests. Documented in master-cloud.toml scaffold. * test: cover concurrency propagation in remote tier integration test * remote.configure: merge existing config on partial update Load the stored RemoteConf before saving so a partial update (e.g. only -upload_concurrency) preserves credentials, endpoints, and type instead of replacing them with new-config defaults. Only treat a confirmed ErrNotFound as a new configuration; propagate all other load errors so a transient filer failure does not overwrite stored settings. On a type transition, reset backend-specific fields to the destination type's new-config defaults rather than inheriting the old backend's empty values. Bound configured concurrency to a sane maximum. * remote storage: honor configured download concurrency in S3 and Azure ReadFileWithConcurrency now resolves a zero request override against the client's configured download_concurrency (new downloadConcurrency() helpers), so the remote-mount/cache read path honors RemoteConf.DownloadConcurrency instead of the hard-coded default. Azure also clamps the resolved value to math.MaxUint16 regardless of whether the fallback was used, preventing uint16 wraparound when a configured value exceeds 65535. * shell: rename -concurrent to -concurrency and validate tier transfer bounds Rename the -concurrent flag to -concurrency across volume.tier.upload, volume.tier.download, and volume.tier.compact to match the proto field and RemoteConf field names. Add validateTierConcurrency to reject values that would wrap int32 or exceed a 1024 cap before constructing the request. * server: clamp tier move concurrency in gRPC handlers Add clampTierConcurrency to both VolumeTierMoveDatToRemote and VolumeTierMoveDatFromRemote handlers so a direct gRPC caller cannot spawn an unbounded number of network workers. * trim verbose comments added with concurrency feature Remove redundant doc comments on the backend interface, rclone backend, s3_backend parseConcurrency, and test helpers that restated the obvious. * remote.configure: apply type defaults before re-parse so explicit flags win applyTypeDefaults ran after the second flag parse, overwriting explicit destination flags (e.g. -s3.region=eu-west-1) with new-config defaults. Move the type-transition default reset before the re-parse so user-supplied flags override the destination defaults. * remote.configure: only treat explicit -type as a type transition The first parse defaults -type to s3, so a concurrency-only update on an existing non-S3 config captured requestedType=s3 and wrongly triggered a type transition, resetting the stored backend to S3. Use fs.Visit to detect whether -type was explicitly supplied; an omitted -type keeps the stored backend. --------- Co-authored-by: Jack Meredith <9480542+jackusm@users.noreply.github.com> Co-authored-by: Chris Lu <chris.lu@gmail.com>
120 lines
3.5 KiB
Go
120 lines
3.5 KiB
Go
package weed_server
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/backend"
|
|
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
|
|
)
|
|
|
|
// clampTierConcurrency bounds a per-request transfer concurrency so a direct
|
|
// gRPC caller cannot spawn an unbounded number of network workers.
|
|
const maxTierConcurrency = 1024
|
|
|
|
func clampTierConcurrency(n int) int {
|
|
if n < 0 {
|
|
return 0
|
|
}
|
|
if n > maxTierConcurrency {
|
|
return maxTierConcurrency
|
|
}
|
|
return n
|
|
}
|
|
|
|
// VolumeTierMoveDatToRemote copy dat file to a remote tier
|
|
func (vs *VolumeServer) VolumeTierMoveDatToRemote(req *volume_server_pb.VolumeTierMoveDatToRemoteRequest, stream volume_server_pb.VolumeServer_VolumeTierMoveDatToRemoteServer) error {
|
|
if err := vs.checkGrpcAdminAuth(stream.Context()); err != nil {
|
|
return err
|
|
}
|
|
if err := vs.CheckMaintenanceMode(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// find existing volume
|
|
v := vs.store.GetVolume(needle.VolumeId(req.VolumeId))
|
|
if v == nil {
|
|
return fmt.Errorf("volume %d not found", req.VolumeId)
|
|
}
|
|
|
|
// verify the collection
|
|
if v.Collection != req.Collection {
|
|
return fmt.Errorf("existing collection:%v unexpected input: %v", v.Collection, req.Collection)
|
|
}
|
|
|
|
// locate the disk file
|
|
diskFile, ok := v.DataBackend.(*backend.DiskFile)
|
|
if !ok {
|
|
return nil // already copied to remove. fmt.Errorf("volume %d is not on local disk", req.VolumeId)
|
|
}
|
|
_, modTime, err := diskFile.GetStat()
|
|
if err != nil {
|
|
return fmt.Errorf("stat data file %s: %v", diskFile.Name(), err)
|
|
}
|
|
|
|
// check valid storage backend type
|
|
backendStorage, found := backend.BackendStorages[req.DestinationBackendName]
|
|
if !found {
|
|
var keys []string
|
|
for key := range backend.BackendStorages {
|
|
keys = append(keys, key)
|
|
}
|
|
return fmt.Errorf("destination %s not found, supported: %v", req.DestinationBackendName, keys)
|
|
}
|
|
|
|
// check whether the existing backend storage is the same as requested
|
|
// if same, skip
|
|
backendType, backendId := backend.BackendNameToTypeId(req.DestinationBackendName)
|
|
for _, remoteFile := range v.GetVolumeInfo().GetFiles() {
|
|
if remoteFile.BackendType == backendType && remoteFile.BackendId == backendId {
|
|
return fmt.Errorf("destination %s already exists", req.DestinationBackendName)
|
|
}
|
|
}
|
|
|
|
startTime := time.Now()
|
|
fn := func(progressed int64, percentage float32) error {
|
|
now := time.Now()
|
|
if now.Sub(startTime) < time.Second {
|
|
return nil
|
|
}
|
|
startTime = now
|
|
return stream.Send(&volume_server_pb.VolumeTierMoveDatToRemoteResponse{
|
|
Processed: progressed,
|
|
ProcessedPercentage: percentage,
|
|
})
|
|
}
|
|
|
|
// copy the data file
|
|
key, size, err := backendStorage.CopyFile(diskFile.File, fn, clampTierConcurrency(int(req.Concurrency)))
|
|
if err != nil {
|
|
return fmt.Errorf("backend %s copy file %s: %v", req.DestinationBackendName, diskFile.Name(), err)
|
|
}
|
|
|
|
// save the remote file to volume tier info
|
|
v.GetVolumeInfo().Files = append(v.GetVolumeInfo().GetFiles(), &volume_server_pb.RemoteFile{
|
|
BackendType: backendType,
|
|
BackendId: backendId,
|
|
Key: key,
|
|
Offset: 0,
|
|
FileSize: uint64(size),
|
|
ModifiedTime: uint64(modTime.Unix()),
|
|
Extension: ".dat",
|
|
})
|
|
|
|
if err := v.SaveVolumeInfo(); err != nil {
|
|
return fmt.Errorf("volume %d failed to save remote file info: %v", v.Id, err)
|
|
}
|
|
|
|
if err := v.LoadRemoteFile(); err != nil {
|
|
return fmt.Errorf("volume %d failed to load remote file: %v", v.Id, err)
|
|
}
|
|
|
|
if !req.KeepLocalDatFile {
|
|
os.Remove(v.FileName(".dat"))
|
|
}
|
|
|
|
return nil
|
|
}
|