|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- package strategy
-
- import (
- "fmt"
- "math"
- "reflect"
- "time"
-
- "github.com/samber/lo"
- "gitlink.org.cn/cloudream/common/pkgs/bitmap"
- "gitlink.org.cn/cloudream/common/utils/math2"
- "gitlink.org.cn/cloudream/common/utils/sort2"
- "gitlink.org.cn/cloudream/jcs-pub/client/internal/metacache"
- "gitlink.org.cn/cloudream/jcs-pub/client/types"
- "gitlink.org.cn/cloudream/jcs-pub/common/consts"
- cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
- )
-
- type Request struct {
- Detail types.ObjectDetail
- Range math2.Range
- DestHub cortypes.HubID // 可以为0。此字段不为0时,DestLocation字段无意义。
- DestLocation cortypes.LocationID // 可以为0
- }
-
- type Strategy interface {
- GetDetail() types.ObjectDetail
- }
-
- // 直接下载完整对象
- type DirectStrategy struct {
- Detail types.ObjectDetail
- UserSpace types.UserSpaceDetail
- }
-
- func (s *DirectStrategy) GetDetail() types.ObjectDetail {
- return s.Detail
- }
-
- // 从指定对象重建对象
- type ECReconstructStrategy struct {
- Detail types.ObjectDetail
- Redundancy types.ECRedundancy
- Blocks []types.ObjectBlock
- UserSpaces []types.UserSpaceDetail
- }
-
- func (s *ECReconstructStrategy) GetDetail() types.ObjectDetail {
- return s.Detail
- }
-
- type LRCReconstructStrategy struct {
- Detail types.ObjectDetail
- Redundancy types.LRCRedundancy
- Blocks []types.ObjectBlock
- Spaces []types.UserSpaceDetail
- }
-
- func (s *LRCReconstructStrategy) GetDetail() types.ObjectDetail {
- return s.Detail
- }
-
- type Selector struct {
- cfg Config
- storageMeta *metacache.UserSpaceMeta
- hubMeta *metacache.HubMeta
- connectivity *metacache.Connectivity
- }
-
- func NewSelector(cfg Config, storageMeta *metacache.UserSpaceMeta, hubMeta *metacache.HubMeta, connectivity *metacache.Connectivity) *Selector {
- return &Selector{
- cfg: cfg,
- storageMeta: storageMeta,
- hubMeta: hubMeta,
- connectivity: connectivity,
- }
- }
-
- func (s *Selector) Select(req Request) (Strategy, error) {
- req2 := request2{
- Detail: req.Detail,
- Range: req.Range,
- DestLocation: req.DestLocation,
- }
-
- if req.DestHub != 0 {
- req2.DestHub = s.hubMeta.Get(req.DestHub)
- }
-
- switch red := req.Detail.Object.Redundancy.(type) {
- case *types.NoneRedundancy:
- return s.selectForNoneOrRep(req2)
-
- case *types.RepRedundancy:
- return s.selectForNoneOrRep(req2)
-
- case *types.ECRedundancy:
- return s.selectForEC(req2, *red)
-
- case *types.LRCRedundancy:
- return s.selectForLRC(req2, *red)
- }
-
- return nil, fmt.Errorf("unsupported redundancy type: %v of object %v", reflect.TypeOf(req.Detail.Object.Redundancy), req.Detail.Object.ObjectID)
- }
-
- type downloadSpaceInfo struct {
- Space types.UserSpaceDetail
- ObjectPinned bool
- Blocks []types.ObjectBlock
- Distance float64
- }
-
- type downloadBlock struct {
- Space types.UserSpaceDetail
- Block types.ObjectBlock
- }
-
- type request2 struct {
- Detail types.ObjectDetail
- Range math2.Range
- DestHub *cortypes.Hub
- DestLocation cortypes.LocationID
- }
-
- func (s *Selector) selectForNoneOrRep(req request2) (Strategy, error) {
- sortedStgs := s.sortDownloadStorages(req)
- if len(sortedStgs) == 0 {
- return nil, fmt.Errorf("no storage available for download")
- }
-
- _, blks := s.getMinReadingBlockSolution(sortedStgs, 1)
- if len(blks) == 0 {
- return nil, fmt.Errorf("no block available for download")
- }
-
- return &DirectStrategy{
- Detail: req.Detail,
- UserSpace: sortedStgs[0].Space,
- }, nil
- }
-
- func (s *Selector) selectForEC(req request2, red types.ECRedundancy) (Strategy, error) {
- sortedStgs := s.sortDownloadStorages(req)
- if len(sortedStgs) == 0 {
- return nil, fmt.Errorf("no storage available for download")
- }
-
- bsc, blocks := s.getMinReadingBlockSolution(sortedStgs, red.K)
- osc, stg := s.getMinReadingObjectSolution(sortedStgs, red.K)
-
- if bsc < osc {
- bs := make([]types.ObjectBlock, len(blocks))
- ss := make([]types.UserSpaceDetail, len(blocks))
- for i, b := range blocks {
- bs[i] = b.Block
- ss[i] = b.Space
- }
-
- return &ECReconstructStrategy{
- Detail: req.Detail,
- Redundancy: red,
- Blocks: bs,
- UserSpaces: ss,
- }, nil
- }
-
- // bsc >= osc,如果osc是MaxFloat64,那么bsc也一定是,也就意味着没有足够块来恢复文件
- if osc == math.MaxFloat64 {
- return nil, fmt.Errorf("no enough blocks to reconstruct the object %v , want %d, get only %d", req.Detail.Object.ObjectID, red.K, len(blocks))
- }
-
- return &DirectStrategy{
- Detail: req.Detail,
- UserSpace: stg,
- }, nil
- }
-
- func (s *Selector) selectForLRC(req request2, red types.LRCRedundancy) (Strategy, error) {
- sortedStgs := s.sortDownloadStorages(req)
- if len(sortedStgs) == 0 {
- return nil, fmt.Errorf("no storage available for download")
- }
-
- var blocks []downloadBlock
- selectedBlkIdx := make(map[int]bool)
- for _, stg := range sortedStgs {
- for _, b := range stg.Blocks {
- if b.Index >= red.M() || selectedBlkIdx[b.Index] {
- continue
- }
- blocks = append(blocks, downloadBlock{
- Space: stg.Space,
- Block: b,
- })
- selectedBlkIdx[b.Index] = true
- }
- }
- if len(blocks) < red.K {
- return nil, fmt.Errorf("not enough blocks to download lrc object")
- }
-
- bs := make([]types.ObjectBlock, len(blocks))
- ss := make([]types.UserSpaceDetail, len(blocks))
- for i, b := range blocks {
- bs[i] = b.Block
- ss[i] = b.Space
- }
-
- return &LRCReconstructStrategy{
- Detail: req.Detail,
- Redundancy: red,
- Blocks: bs,
- Spaces: ss,
- }, nil
- }
-
- func (s *Selector) sortDownloadStorages(req request2) []*downloadSpaceInfo {
- var spaceIDs []types.UserSpaceID
- for _, id := range req.Detail.PinnedAt {
- if !lo.Contains(spaceIDs, id) {
- spaceIDs = append(spaceIDs, id)
- }
- }
- for _, b := range req.Detail.Blocks {
- if !lo.Contains(spaceIDs, b.UserSpaceID) {
- spaceIDs = append(spaceIDs, b.UserSpaceID)
- }
- }
-
- downloadSpaceMap := make(map[types.UserSpaceID]*downloadSpaceInfo)
- for _, id := range req.Detail.PinnedAt {
- storage, ok := downloadSpaceMap[id]
- if !ok {
- mod := s.storageMeta.Get(id)
- if mod == nil || mod.MasterHub == nil {
- continue
- }
-
- storage = &downloadSpaceInfo{
- Space: *mod,
- ObjectPinned: true,
- Distance: s.getStorageDistance(req, *mod),
- }
- downloadSpaceMap[id] = storage
- }
-
- storage.ObjectPinned = true
- }
-
- for _, b := range req.Detail.Blocks {
- space, ok := downloadSpaceMap[b.UserSpaceID]
- if !ok {
- mod := s.storageMeta.Get(b.UserSpaceID)
- if mod == nil || mod.MasterHub == nil {
- continue
- }
-
- space = &downloadSpaceInfo{
- Space: *mod,
- Distance: s.getStorageDistance(req, *mod),
- }
- downloadSpaceMap[b.UserSpaceID] = space
- }
-
- space.Blocks = append(space.Blocks, b)
- }
-
- return sort2.Sort(lo.Values(downloadSpaceMap), func(left, right *downloadSpaceInfo) int {
- return sort2.Cmp(left.Distance, right.Distance)
- })
- }
-
- func (s *Selector) getStorageDistance(req request2, src types.UserSpaceDetail) float64 {
- if req.DestHub != nil {
- if src.MasterHub.HubID == req.DestHub.HubID {
- return consts.StorageDistanceSameStorage
- }
-
- if src.MasterHub.LocationID == req.DestHub.LocationID {
- return consts.StorageDistanceSameLocation
- }
-
- latency := s.connectivity.Get(src.MasterHub.HubID, req.DestHub.HubID)
- if latency == nil || *latency > time.Duration(float64(time.Millisecond)*s.cfg.HighLatencyHubMs) {
- return consts.HubDistanceHighLatencyHub
- }
-
- return consts.StorageDistanceOther
- }
-
- if req.DestLocation != 0 {
- if src.MasterHub.LocationID == req.DestLocation {
- return consts.StorageDistanceSameLocation
- }
- }
-
- return consts.StorageDistanceOther
- }
-
- func (s *Selector) getMinReadingBlockSolution(sortedStgs []*downloadSpaceInfo, k int) (float64, []downloadBlock) {
- gotBlocksMap := bitmap.Bitmap64(0)
- var gotBlocks []downloadBlock
- dist := float64(0.0)
- for _, n := range sortedStgs {
- for _, b := range n.Blocks {
- if !gotBlocksMap.Get(b.Index) {
- gotBlocks = append(gotBlocks, downloadBlock{
- Space: n.Space,
- Block: b,
- })
- gotBlocksMap.Set(b.Index, true)
- dist += n.Distance
- }
-
- if len(gotBlocks) >= k {
- return dist, gotBlocks
- }
- }
- }
-
- return math.MaxFloat64, gotBlocks
- }
-
- func (s *Selector) getMinReadingObjectSolution(sortedStgs []*downloadSpaceInfo, k int) (float64, types.UserSpaceDetail) {
- dist := math.MaxFloat64
- var downloadSpace types.UserSpaceDetail
- for _, n := range sortedStgs {
- if n.ObjectPinned && float64(k)*n.Distance < dist {
- dist = float64(k) * n.Distance
- stg := n.Space
- downloadSpace = stg
- }
- }
-
- return dist, downloadSpace
- }
|