github.com/qiniu/go-sdk/v7@v7.27.0/storage/region_uc_v4.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math"
     8  	"os"
     9  	"path/filepath"
    10  	"sync"
    11  	"time"
    12  
    13  	"golang.org/x/sync/singleflight"
    14  
    15  	"github.com/qiniu/go-sdk/v7/storagev2/apis"
    16  	"github.com/qiniu/go-sdk/v7/storagev2/apis/query_bucket_v4"
    17  )
    18  
    19  var ucQueryV4Group singleflight.Group
    20  
    21  type regionV4CacheValue struct {
    22  	Regions  []*Region `json:"regions"`
    23  	Deadline time.Time `json:"deadline"`
    24  }
    25  
    26  func (r *regionV4CacheValue) getRegions() []*Region {
    27  	if r == nil {
    28  		return nil
    29  	}
    30  	return r.Regions
    31  }
    32  
    33  type regionV4CacheMap map[string]regionV4CacheValue
    34  
    35  const regionV4CacheFileName = "query_v4_00.cache.json"
    36  
    37  var (
    38  	regionV4CachePath     = filepath.Join(os.TempDir(), "qiniu-golang-sdk", regionV4CacheFileName)
    39  	regionV4Cache         sync.Map
    40  	regionV4CacheLock     sync.RWMutex
    41  	regionV4CacheSyncLock sync.Mutex
    42  	regionV4CacheLoaded   bool = false
    43  )
    44  
    45  func setRegionV4CachePath(newPath string) {
    46  	cacheDir := filepath.Dir(newPath)
    47  	if len(cacheDir) == 0 {
    48  		return
    49  	}
    50  
    51  	regionV4CacheLock.Lock()
    52  	defer regionV4CacheLock.Unlock()
    53  
    54  	regionV4CachePath = filepath.Join(cacheDir, regionV4CacheFileName)
    55  	regionV4CacheLoaded = false
    56  }
    57  
    58  func loadRegionV4Cache() {
    59  	cacheFile, err := os.Open(regionV4CachePath)
    60  	if err != nil {
    61  		return
    62  	}
    63  	defer cacheFile.Close()
    64  
    65  	var cacheMap regionV4CacheMap
    66  	if err = json.NewDecoder(cacheFile).Decode(&cacheMap); err != nil {
    67  		return
    68  	}
    69  	for cacheKey, cacheValue := range cacheMap {
    70  		regionV4Cache.Store(cacheKey, cacheValue)
    71  	}
    72  }
    73  
    74  func storeRegionV4Cache() {
    75  	err := os.MkdirAll(filepath.Dir(regionV4CachePath), 0700)
    76  	if err != nil {
    77  		return
    78  	}
    79  
    80  	cacheFile, err := os.OpenFile(regionV4CachePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
    81  	if err != nil {
    82  		return
    83  	}
    84  	defer cacheFile.Close()
    85  
    86  	cacheMap := make(regionV4CacheMap)
    87  	regionV4Cache.Range(func(cacheKey, cacheValue interface{}) bool {
    88  		cacheMap[cacheKey.(string)] = cacheValue.(regionV4CacheValue)
    89  		return true
    90  	})
    91  	if err = json.NewEncoder(cacheFile).Encode(cacheMap); err != nil {
    92  		return
    93  	}
    94  }
    95  
    96  func getRegionByV4(ak, bucket string, options UCApiOptions) (*RegionGroup, error) {
    97  	regionV4CacheLock.RLock()
    98  	if regionV4CacheLoaded {
    99  		regionV4CacheLock.RUnlock()
   100  	} else {
   101  		regionV4CacheLock.RUnlock()
   102  		func() {
   103  			regionV4CacheLock.Lock()
   104  			defer regionV4CacheLock.Unlock()
   105  
   106  			if !regionV4CacheLoaded {
   107  				loadRegionV4Cache()
   108  				regionV4CacheLoaded = true
   109  			}
   110  		}()
   111  	}
   112  
   113  	regionCacheKey := makeRegionCacheKey(ak, bucket, options.Hosts, options.AccelerateUploading)
   114  	// check from cache
   115  	if v, ok := regionV4Cache.Load(regionCacheKey); ok && time.Now().Before(v.(regionV4CacheValue).Deadline) {
   116  		cacheValue, _ := v.(regionV4CacheValue)
   117  		return NewRegionGroup(cacheValue.getRegions()...), nil
   118  	}
   119  
   120  	newRegion, err, _ := ucQueryV4Group.Do(regionCacheKey, func() (interface{}, error) {
   121  		regions, ttl, err := _getRegionByV4WithoutCache(ak, bucket, options)
   122  		if err != nil {
   123  			return nil, fmt.Errorf("query region error, %s", err.Error())
   124  		}
   125  		regionV4Cache.Store(regionCacheKey, regionV4CacheValue{
   126  			Regions:  regions,
   127  			Deadline: time.Now().Add(time.Duration(ttl) * time.Second),
   128  		})
   129  
   130  		regionV4CacheSyncLock.Lock()
   131  		defer regionV4CacheSyncLock.Unlock()
   132  
   133  		storeRegionV4Cache()
   134  
   135  		return NewRegionGroup(regions...), nil
   136  	})
   137  
   138  	if newRegion == nil {
   139  		return nil, err
   140  	}
   141  
   142  	return newRegion.(*RegionGroup), err
   143  }
   144  
   145  func _getRegionByV4WithoutCache(ak, bucket string, options UCApiOptions) ([]*Region, int64, error) {
   146  	toRegion := func(r *query_bucket_v4.BucketQueryHost) *Region {
   147  		var rsHost, rsfHost, apiHost, ioVipHost, ioSrcHost string
   148  		upDomains := make([]string, 0, len(r.UpDomains.AcceleratedUpDomains)+len(r.UpDomains.PreferedUpDomains)+len(r.UpDomains.AlternativeUpDomains))
   149  		if options.AccelerateUploading && len(r.UpDomains.AcceleratedUpDomains) > 0 {
   150  			upDomains = append(upDomains, r.UpDomains.AcceleratedUpDomains...)
   151  		}
   152  		upDomains = append(upDomains, r.UpDomains.PreferedUpDomains...)
   153  		upDomains = append(upDomains, r.UpDomains.AlternativeUpDomains...)
   154  		if len(r.RsDomains.PreferedRsDomains) > 0 {
   155  			rsHost = r.RsDomains.PreferedRsDomains[0]
   156  		}
   157  		if len(r.RsfDomains.PreferedRsfDomains) > 0 {
   158  			rsfHost = r.RsfDomains.PreferedRsfDomains[0]
   159  		}
   160  		if len(r.ApiDomains.PreferedApiDomains) > 0 {
   161  			apiHost = r.ApiDomains.PreferedApiDomains[0]
   162  		}
   163  		if len(r.IoDomains.PreferedIoDomains) > 0 {
   164  			ioVipHost = r.IoDomains.PreferedIoDomains[0]
   165  		}
   166  		if len(r.IoSrcDomains.PreferedIoSrcDomains) > 0 {
   167  			ioSrcHost = r.IoSrcDomains.PreferedIoSrcDomains[0]
   168  		}
   169  		return &Region{
   170  			SrcUpHosts: upDomains,
   171  			CdnUpHosts: upDomains,
   172  			RsHost:     rsHost,
   173  			RsfHost:    rsfHost,
   174  			ApiHost:    apiHost,
   175  			IovipHost:  ioVipHost,
   176  			IoSrcHost:  ioSrcHost,
   177  		}
   178  	}
   179  	response, err := options.getApiStorageClient().QueryBucketV4(
   180  		context.Background(),
   181  		&apis.QueryBucketV4Request{
   182  			Bucket:    bucket,
   183  			AccessKey: ak,
   184  		},
   185  		options.getApiOptions(),
   186  	)
   187  	if err != nil {
   188  		return nil, 0, err
   189  	}
   190  	regions := make([]*Region, 0, len(response.Hosts))
   191  	var ttl int64 = math.MaxInt64
   192  	for _, host := range response.Hosts {
   193  		regions = append(regions, toRegion(&host))
   194  		if ttl > host.TimeToLive {
   195  			ttl = host.TimeToLive
   196  		}
   197  	}
   198  	return regions, ttl, nil
   199  }