You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							373 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							373 lines
						
					
					
						
							11 KiB
						
					
					
				
								package weed_server
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"context"
							 | 
						|
									"fmt"
							 | 
						|
									"math"
							 | 
						|
									"math/rand/v2"
							 | 
						|
									"strings"
							 | 
						|
									"sync"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/stats"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/topology"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/raft"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/security"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/storage/needle"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/storage/super_block"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/storage/types"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								const (
							 | 
						|
									volumeGrowStepCount = 2
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) DoAutomaticVolumeGrow(req *topology.VolumeGrowRequest) {
							 | 
						|
									if ms.option.VolumeGrowthDisabled {
							 | 
						|
										glog.V(1).Infof("automatic volume grow disabled")
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									glog.V(1).Infoln("starting automatic volume grow")
							 | 
						|
									start := time.Now()
							 | 
						|
									newVidLocations, err := ms.vg.AutomaticGrowByType(req.Option, ms.grpcDialOption, ms.Topo, req.Count)
							 | 
						|
									glog.V(1).Infoln("finished automatic volume grow, cost ", time.Now().Sub(start))
							 | 
						|
									if err != nil {
							 | 
						|
										glog.V(1).Infof("automatic volume grow failed: %+v", err)
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									for _, newVidLocation := range newVidLocations {
							 | 
						|
										ms.broadcastToClients(&master_pb.KeepConnectedResponse{VolumeLocation: newVidLocation})
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) ProcessGrowRequest() {
							 | 
						|
									go func() {
							 | 
						|
										ctx := context.Background()
							 | 
						|
										firstRun := true
							 | 
						|
										for {
							 | 
						|
											if firstRun {
							 | 
						|
												firstRun = false
							 | 
						|
											} else {
							 | 
						|
												time.Sleep(5*time.Minute + time.Duration(30*rand.Float32())*time.Second)
							 | 
						|
											}
							 | 
						|
											if !ms.Topo.IsLeader() {
							 | 
						|
												continue
							 | 
						|
											}
							 | 
						|
											dcs := ms.Topo.ListDCAndRacks()
							 | 
						|
											var err error
							 | 
						|
											for _, vlc := range ms.Topo.ListVolumeLayoutCollections() {
							 | 
						|
												vl := vlc.VolumeLayout
							 | 
						|
												lastGrowCount := vl.GetLastGrowCount()
							 | 
						|
												if vl.HasGrowRequest() {
							 | 
						|
													continue
							 | 
						|
												}
							 | 
						|
												writable, crowded := vl.GetWritableVolumeCount()
							 | 
						|
												mustGrow := int(lastGrowCount) - writable
							 | 
						|
												vgr := vlc.ToVolumeGrowRequest()
							 | 
						|
												stats.MasterVolumeLayoutWritable.WithLabelValues(vlc.Collection, vgr.DiskType, vgr.Replication, vgr.Ttl).Set(float64(writable))
							 | 
						|
												stats.MasterVolumeLayoutCrowded.WithLabelValues(vlc.Collection, vgr.DiskType, vgr.Replication, vgr.Ttl).Set(float64(crowded))
							 | 
						|
								
							 | 
						|
												switch {
							 | 
						|
												case mustGrow > 0:
							 | 
						|
													vgr.WritableVolumeCount = uint32(mustGrow)
							 | 
						|
													_, err = ms.VolumeGrow(ctx, vgr)
							 | 
						|
												case lastGrowCount > 0 && writable < int(lastGrowCount*2) && float64(crowded+volumeGrowStepCount) > float64(writable)*topology.VolumeGrowStrategy.Threshold:
							 | 
						|
													vgr.WritableVolumeCount = volumeGrowStepCount
							 | 
						|
													_, err = ms.VolumeGrow(ctx, vgr)
							 | 
						|
												}
							 | 
						|
												if err != nil {
							 | 
						|
													glog.V(0).Infof("volume grow request failed: %+v", err)
							 | 
						|
												}
							 | 
						|
												writableVolumes := vl.CloneWritableVolumes()
							 | 
						|
												for dcId, racks := range dcs {
							 | 
						|
													for _, rackId := range racks {
							 | 
						|
														if vl.ShouldGrowVolumesByDcAndRack(&writableVolumes, dcId, rackId) {
							 | 
						|
															vgr.DataCenter = string(dcId)
							 | 
						|
															vgr.Rack = string(rackId)
							 | 
						|
															if lastGrowCount > 0 {
							 | 
						|
																vgr.WritableVolumeCount = uint32(math.Ceil(float64(lastGrowCount) / float64(len(dcs)*len(racks))))
							 | 
						|
															} else {
							 | 
						|
																vgr.WritableVolumeCount = volumeGrowStepCount
							 | 
						|
															}
							 | 
						|
								
							 | 
						|
															if _, err = ms.VolumeGrow(ctx, vgr); err != nil {
							 | 
						|
																glog.V(0).Infof("volume grow request for dc:%s rack:%s failed: %+v", dcId, rackId, err)
							 | 
						|
															}
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}()
							 | 
						|
									go func() {
							 | 
						|
										filter := sync.Map{}
							 | 
						|
										for {
							 | 
						|
											req, ok := <-ms.volumeGrowthRequestChan
							 | 
						|
											if !ok {
							 | 
						|
												break
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											option := req.Option
							 | 
						|
											vl := ms.Topo.GetVolumeLayout(option.Collection, option.ReplicaPlacement, option.Ttl, option.DiskType)
							 | 
						|
								
							 | 
						|
											if !ms.Topo.IsLeader() {
							 | 
						|
												//discard buffered requests
							 | 
						|
												time.Sleep(time.Second * 1)
							 | 
						|
												vl.DoneGrowRequest()
							 | 
						|
												continue
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// filter out identical requests being processed
							 | 
						|
											found := false
							 | 
						|
											filter.Range(func(k, v interface{}) bool {
							 | 
						|
												existingReq := k.(*topology.VolumeGrowRequest)
							 | 
						|
												if existingReq.Equals(req) {
							 | 
						|
													found = true
							 | 
						|
												}
							 | 
						|
												return !found
							 | 
						|
											})
							 | 
						|
								
							 | 
						|
											// not atomic but it's okay
							 | 
						|
											if found || (!req.Force && !vl.ShouldGrowVolumes()) {
							 | 
						|
												glog.V(4).Infoln("discard volume grow request")
							 | 
						|
												time.Sleep(time.Millisecond * 211)
							 | 
						|
												vl.DoneGrowRequest()
							 | 
						|
												continue
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											filter.Store(req, nil)
							 | 
						|
											// we have lock called inside vg
							 | 
						|
											glog.V(0).Infof("volume grow %+v", req)
							 | 
						|
											go func(req *topology.VolumeGrowRequest, vl *topology.VolumeLayout) {
							 | 
						|
												ms.DoAutomaticVolumeGrow(req)
							 | 
						|
												vl.DoneGrowRequest()
							 | 
						|
												filter.Delete(req)
							 | 
						|
											}(req, vl)
							 | 
						|
										}
							 | 
						|
									}()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) LookupVolume(ctx context.Context, req *master_pb.LookupVolumeRequest) (*master_pb.LookupVolumeResponse, error) {
							 | 
						|
								
							 | 
						|
									resp := &master_pb.LookupVolumeResponse{}
							 | 
						|
									volumeLocations := ms.lookupVolumeId(req.VolumeOrFileIds, req.Collection)
							 | 
						|
								
							 | 
						|
									for _, volumeOrFileId := range req.VolumeOrFileIds {
							 | 
						|
										vid := volumeOrFileId
							 | 
						|
										commaSep := strings.Index(vid, ",")
							 | 
						|
										if commaSep > 0 {
							 | 
						|
											vid = vid[0:commaSep]
							 | 
						|
										}
							 | 
						|
										if result, found := volumeLocations[vid]; found {
							 | 
						|
											var locations []*master_pb.Location
							 | 
						|
											for _, loc := range result.Locations {
							 | 
						|
												locations = append(locations, &master_pb.Location{
							 | 
						|
													Url:        loc.Url,
							 | 
						|
													PublicUrl:  loc.PublicUrl,
							 | 
						|
													DataCenter: loc.DataCenter,
							 | 
						|
													GrpcPort:   uint32(loc.GrpcPort),
							 | 
						|
												})
							 | 
						|
											}
							 | 
						|
											var auth string
							 | 
						|
											if commaSep > 0 { // this is a file id
							 | 
						|
												auth = string(security.GenJwtForVolumeServer(ms.guard.SigningKey, ms.guard.ExpiresAfterSec, result.VolumeOrFileId))
							 | 
						|
											}
							 | 
						|
											resp.VolumeIdLocations = append(resp.VolumeIdLocations, &master_pb.LookupVolumeResponse_VolumeIdLocation{
							 | 
						|
												VolumeOrFileId: result.VolumeOrFileId,
							 | 
						|
												Locations:      locations,
							 | 
						|
												Error:          result.Error,
							 | 
						|
												Auth:           auth,
							 | 
						|
											})
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) Statistics(ctx context.Context, req *master_pb.StatisticsRequest) (*master_pb.StatisticsResponse, error) {
							 | 
						|
								
							 | 
						|
									if !ms.Topo.IsLeader() {
							 | 
						|
										return nil, raft.NotLeaderError
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if req.Replication == "" {
							 | 
						|
										req.Replication = ms.option.DefaultReplicaPlacement
							 | 
						|
									}
							 | 
						|
									replicaPlacement, err := super_block.NewReplicaPlacementFromString(req.Replication)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
									ttl, err := needle.ReadTTL(req.Ttl)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									volumeLayout := ms.Topo.GetVolumeLayout(req.Collection, replicaPlacement, ttl, types.ToDiskType(req.DiskType))
							 | 
						|
									stats := volumeLayout.Stats()
							 | 
						|
									totalSize := ms.Topo.GetDiskUsages().GetMaxVolumeCount() * int64(ms.option.VolumeSizeLimitMB) * 1024 * 1024
							 | 
						|
									resp := &master_pb.StatisticsResponse{
							 | 
						|
										TotalSize: uint64(totalSize),
							 | 
						|
										UsedSize:  stats.UsedSize,
							 | 
						|
										FileCount: stats.FileCount,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) VolumeList(ctx context.Context, req *master_pb.VolumeListRequest) (*master_pb.VolumeListResponse, error) {
							 | 
						|
								
							 | 
						|
									if !ms.Topo.IsLeader() {
							 | 
						|
										return nil, raft.NotLeaderError
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									resp := &master_pb.VolumeListResponse{
							 | 
						|
										TopologyInfo:      ms.Topo.ToTopologyInfo(),
							 | 
						|
										VolumeSizeLimitMb: uint64(ms.option.VolumeSizeLimitMB),
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) LookupEcVolume(ctx context.Context, req *master_pb.LookupEcVolumeRequest) (*master_pb.LookupEcVolumeResponse, error) {
							 | 
						|
								
							 | 
						|
									if !ms.Topo.IsLeader() {
							 | 
						|
										return nil, raft.NotLeaderError
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									resp := &master_pb.LookupEcVolumeResponse{}
							 | 
						|
								
							 | 
						|
									ecLocations, found := ms.Topo.LookupEcShards(needle.VolumeId(req.VolumeId))
							 | 
						|
								
							 | 
						|
									if !found {
							 | 
						|
										return resp, fmt.Errorf("ec volume %d not found", req.VolumeId)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									resp.VolumeId = req.VolumeId
							 | 
						|
								
							 | 
						|
									for shardId, shardLocations := range ecLocations.Locations {
							 | 
						|
										var locations []*master_pb.Location
							 | 
						|
										for _, dn := range shardLocations {
							 | 
						|
											locations = append(locations, &master_pb.Location{
							 | 
						|
												Url:        string(dn.Id()),
							 | 
						|
												PublicUrl:  dn.PublicUrl,
							 | 
						|
												DataCenter: dn.GetDataCenterId(),
							 | 
						|
											})
							 | 
						|
										}
							 | 
						|
										resp.ShardIdLocations = append(resp.ShardIdLocations, &master_pb.LookupEcVolumeResponse_EcShardIdLocation{
							 | 
						|
											ShardId:   uint32(shardId),
							 | 
						|
											Locations: locations,
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) VacuumVolume(ctx context.Context, req *master_pb.VacuumVolumeRequest) (*master_pb.VacuumVolumeResponse, error) {
							 | 
						|
								
							 | 
						|
									if !ms.Topo.IsLeader() {
							 | 
						|
										return nil, raft.NotLeaderError
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									resp := &master_pb.VacuumVolumeResponse{}
							 | 
						|
								
							 | 
						|
									ms.Topo.Vacuum(ms.grpcDialOption, float64(req.GarbageThreshold), ms.option.MaxParallelVacuumPerServer, req.VolumeId, req.Collection, ms.preallocateSize, false)
							 | 
						|
								
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) DisableVacuum(ctx context.Context, req *master_pb.DisableVacuumRequest) (*master_pb.DisableVacuumResponse, error) {
							 | 
						|
								
							 | 
						|
									ms.Topo.DisableVacuum()
							 | 
						|
									resp := &master_pb.DisableVacuumResponse{}
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) EnableVacuum(ctx context.Context, req *master_pb.EnableVacuumRequest) (*master_pb.EnableVacuumResponse, error) {
							 | 
						|
								
							 | 
						|
									ms.Topo.EnableVacuum()
							 | 
						|
									resp := &master_pb.EnableVacuumResponse{}
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) VolumeMarkReadonly(ctx context.Context, req *master_pb.VolumeMarkReadonlyRequest) (*master_pb.VolumeMarkReadonlyResponse, error) {
							 | 
						|
								
							 | 
						|
									if !ms.Topo.IsLeader() {
							 | 
						|
										return nil, raft.NotLeaderError
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									resp := &master_pb.VolumeMarkReadonlyResponse{}
							 | 
						|
								
							 | 
						|
									replicaPlacement, _ := super_block.NewReplicaPlacementFromByte(byte(req.ReplicaPlacement))
							 | 
						|
									vl := ms.Topo.GetVolumeLayout(req.Collection, replicaPlacement, needle.LoadTTLFromUint32(req.Ttl), types.ToDiskType(req.DiskType))
							 | 
						|
									dataNodes := ms.Topo.Lookup(req.Collection, needle.VolumeId(req.VolumeId))
							 | 
						|
								
							 | 
						|
									for _, dn := range dataNodes {
							 | 
						|
										if dn.Ip == req.Ip && dn.Port == int(req.Port) {
							 | 
						|
											if req.IsReadonly {
							 | 
						|
												vl.SetVolumeReadOnly(dn, needle.VolumeId(req.VolumeId))
							 | 
						|
											} else {
							 | 
						|
												vl.SetVolumeWritable(dn, needle.VolumeId(req.VolumeId))
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return resp, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ms *MasterServer) VolumeGrow(ctx context.Context, req *master_pb.VolumeGrowRequest) (*master_pb.VolumeGrowResponse, error) {
							 | 
						|
									if !ms.Topo.IsLeader() {
							 | 
						|
										return nil, raft.NotLeaderError
							 | 
						|
									}
							 | 
						|
									if req.Replication == "" {
							 | 
						|
										req.Replication = ms.option.DefaultReplicaPlacement
							 | 
						|
									}
							 | 
						|
									replicaPlacement, err := super_block.NewReplicaPlacementFromString(req.Replication)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
									ttl, err := needle.ReadTTL(req.Ttl)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
									if req.DataCenter != "" && !ms.Topo.DataCenterExists(req.DataCenter) {
							 | 
						|
										return nil, fmt.Errorf("data center not exists")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									ver := needle.GetCurrentVersion()
							 | 
						|
									volumeGrowOption := topology.VolumeGrowOption{
							 | 
						|
										Collection:         req.Collection,
							 | 
						|
										ReplicaPlacement:   replicaPlacement,
							 | 
						|
										Ttl:                ttl,
							 | 
						|
										DiskType:           types.ToDiskType(req.DiskType),
							 | 
						|
										Preallocate:        ms.preallocateSize,
							 | 
						|
										DataCenter:         req.DataCenter,
							 | 
						|
										Rack:               req.Rack,
							 | 
						|
										DataNode:           req.DataNode,
							 | 
						|
										MemoryMapMaxSizeMb: req.MemoryMapMaxSizeMb,
							 | 
						|
										Version:            uint32(ver),
							 | 
						|
									}
							 | 
						|
									volumeGrowRequest := topology.VolumeGrowRequest{
							 | 
						|
										Option: &volumeGrowOption,
							 | 
						|
										Count:  req.WritableVolumeCount,
							 | 
						|
										Force:  true,
							 | 
						|
										Reason: "grpc volume grow",
							 | 
						|
									}
							 | 
						|
									replicaCount := int64(req.WritableVolumeCount * uint32(replicaPlacement.GetCopyCount()))
							 | 
						|
								
							 | 
						|
									if ms.Topo.AvailableSpaceFor(&volumeGrowOption) < replicaCount {
							 | 
						|
										return nil, fmt.Errorf("only %d volumes left, not enough for %d", ms.Topo.AvailableSpaceFor(&volumeGrowOption), replicaCount)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if !ms.Topo.DataCenterExists(volumeGrowOption.DataCenter) {
							 | 
						|
										err = fmt.Errorf("data center %v not found in topology", volumeGrowOption.DataCenter)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									ms.DoAutomaticVolumeGrow(&volumeGrowRequest)
							 | 
						|
								
							 | 
						|
									return &master_pb.VolumeGrowResponse{}, nil
							 | 
						|
								}
							 |