| @@ -0,0 +1,96 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "sync" | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| stgglb "gitlink.org.cn/cloudream/storage2/common/globals" | |||||
| coormq "gitlink.org.cn/cloudream/storage2/common/pkgs/mq/coordinator" | |||||
| ) | |||||
| func (m *MetaCacheHost) AddConnectivity() *Connectivity { | |||||
| cache := &Connectivity{ | |||||
| entries: make(map[cdssdk.HubID]*ConnectivityEntry), | |||||
| } | |||||
| m.caches = append(m.caches, cache) | |||||
| return cache | |||||
| } | |||||
| type Connectivity struct { | |||||
| lock sync.RWMutex | |||||
| entries map[cdssdk.HubID]*ConnectivityEntry | |||||
| } | |||||
| func (c *Connectivity) Get(from cdssdk.HubID, to cdssdk.HubID) *time.Duration { | |||||
| for i := 0; i < 2; i++ { | |||||
| c.lock.RLock() | |||||
| entry, ok := c.entries[from] | |||||
| if ok { | |||||
| con, ok := entry.To[to] | |||||
| if ok { | |||||
| c.lock.RUnlock() | |||||
| if con.Latency == nil { | |||||
| return nil | |||||
| } | |||||
| l := time.Millisecond * time.Duration(*con.Latency) | |||||
| return &l | |||||
| } | |||||
| } | |||||
| c.lock.RUnlock() | |||||
| c.load(from) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (c *Connectivity) ClearOutdated() { | |||||
| c.lock.Lock() | |||||
| defer c.lock.Unlock() | |||||
| for hubID, entry := range c.entries { | |||||
| if time.Since(entry.UpdateTime) > time.Minute*5 { | |||||
| delete(c.entries, hubID) | |||||
| } | |||||
| } | |||||
| } | |||||
| func (c *Connectivity) load(hubID cdssdk.HubID) { | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| logger.Warnf("new coordinator client: %v", err) | |||||
| return | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| get, err := coorCli.GetHubConnectivities(coormq.ReqGetHubConnectivities([]cdssdk.HubID{hubID})) | |||||
| if err != nil { | |||||
| logger.Warnf("get hub connectivities: %v", err) | |||||
| return | |||||
| } | |||||
| c.lock.Lock() | |||||
| defer c.lock.Unlock() | |||||
| ce := &ConnectivityEntry{ | |||||
| From: hubID, | |||||
| To: make(map[cdssdk.HubID]cdssdk.HubConnectivity), | |||||
| UpdateTime: time.Now(), | |||||
| } | |||||
| for _, conn := range get.Connectivities { | |||||
| ce.To[conn.ToHubID] = conn | |||||
| } | |||||
| c.entries[hubID] = ce | |||||
| } | |||||
| type ConnectivityEntry struct { | |||||
| From cdssdk.HubID | |||||
| To map[cdssdk.HubID]cdssdk.HubConnectivity | |||||
| UpdateTime time.Time | |||||
| } | |||||
| @@ -0,0 +1,27 @@ | |||||
| package metacache | |||||
| import "time" | |||||
| type MetaCache interface { | |||||
| ClearOutdated() | |||||
| } | |||||
| type MetaCacheHost struct { | |||||
| caches []MetaCache | |||||
| } | |||||
| func NewHost() *MetaCacheHost { | |||||
| return &MetaCacheHost{} | |||||
| } | |||||
| func (m *MetaCacheHost) Serve() { | |||||
| ticker := time.NewTicker(time.Minute) | |||||
| for { | |||||
| select { | |||||
| case <-ticker.C: | |||||
| for _, cache := range m.caches { | |||||
| cache.ClearOutdated() | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,75 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| stgglb "gitlink.org.cn/cloudream/storage2/common/globals" | |||||
| coormq "gitlink.org.cn/cloudream/storage2/common/pkgs/mq/coordinator" | |||||
| ) | |||||
| func (m *MetaCacheHost) AddHubMeta() *HubMeta { | |||||
| meta := &HubMeta{} | |||||
| meta.cache = NewSimpleMetaCache(SimpleMetaCacheConfig[cdssdk.HubID, cdssdk.Hub]{ | |||||
| Getter: meta.load, | |||||
| Expire: time.Minute * 5, | |||||
| }) | |||||
| m.caches = append(m.caches, meta) | |||||
| return meta | |||||
| } | |||||
| type HubMeta struct { | |||||
| cache *SimpleMetaCache[cdssdk.HubID, cdssdk.Hub] | |||||
| } | |||||
| func (h *HubMeta) Get(hubID cdssdk.HubID) *cdssdk.Hub { | |||||
| v, ok := h.cache.Get(hubID) | |||||
| if ok { | |||||
| return &v | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (h *HubMeta) GetMany(hubIDs []cdssdk.HubID) []*cdssdk.Hub { | |||||
| vs, oks := h.cache.GetMany(hubIDs) | |||||
| ret := make([]*cdssdk.Hub, len(vs)) | |||||
| for i := range vs { | |||||
| if oks[i] { | |||||
| ret[i] = &vs[i] | |||||
| } | |||||
| } | |||||
| return ret | |||||
| } | |||||
| func (h *HubMeta) ClearOutdated() { | |||||
| h.cache.ClearOutdated() | |||||
| } | |||||
| func (h *HubMeta) load(keys []cdssdk.HubID) ([]cdssdk.Hub, []bool) { | |||||
| vs := make([]cdssdk.Hub, len(keys)) | |||||
| oks := make([]bool, len(keys)) | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| logger.Warnf("new coordinator client: %v", err) | |||||
| return vs, oks | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| get, err := coorCli.GetHubs(coormq.NewGetHubs(keys)) | |||||
| if err != nil { | |||||
| logger.Warnf("get hubs: %v", err) | |||||
| return vs, oks | |||||
| } | |||||
| for i := range keys { | |||||
| if get.Hubs[i] != nil { | |||||
| vs[i] = *get.Hubs[i] | |||||
| oks[i] = true | |||||
| } | |||||
| } | |||||
| return vs, oks | |||||
| } | |||||
| @@ -0,0 +1,121 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "sync" | |||||
| "time" | |||||
| ) | |||||
| type SimpleMetaCacheConfig[K comparable, V any] struct { | |||||
| Getter Getter[K, V] | |||||
| Expire time.Duration | |||||
| } | |||||
| type Getter[K comparable, V any] func(keys []K) ([]V, []bool) | |||||
| type SimpleMetaCache[K comparable, V any] struct { | |||||
| lock sync.RWMutex | |||||
| cache map[K]*CacheEntry[K, V] | |||||
| cfg SimpleMetaCacheConfig[K, V] | |||||
| } | |||||
| func NewSimpleMetaCache[K comparable, V any](cfg SimpleMetaCacheConfig[K, V]) *SimpleMetaCache[K, V] { | |||||
| return &SimpleMetaCache[K, V]{ | |||||
| cache: make(map[K]*CacheEntry[K, V]), | |||||
| cfg: cfg, | |||||
| } | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) Get(key K) (V, bool) { | |||||
| var ret V | |||||
| var ok bool | |||||
| for i := 0; i < 2; i++ { | |||||
| mc.lock.RLock() | |||||
| entry, o := mc.cache[key] | |||||
| if o { | |||||
| ret = entry.Data | |||||
| ok = true | |||||
| } | |||||
| mc.lock.RUnlock() | |||||
| if o { | |||||
| break | |||||
| } | |||||
| mc.load([]K{key}) | |||||
| } | |||||
| return ret, ok | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) GetMany(keys []K) ([]V, []bool) { | |||||
| result := make([]V, len(keys)) | |||||
| oks := make([]bool, len(keys)) | |||||
| for i := 0; i < 2; i++ { | |||||
| allGet := true | |||||
| mc.lock.RLock() | |||||
| for i, key := range keys { | |||||
| entry, ok := mc.cache[key] | |||||
| if ok { | |||||
| result[i] = entry.Data | |||||
| oks[i] = true | |||||
| } else { | |||||
| allGet = false | |||||
| } | |||||
| } | |||||
| mc.lock.RUnlock() | |||||
| if allGet { | |||||
| break | |||||
| } | |||||
| mc.load(keys) | |||||
| } | |||||
| return result, oks | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) load(keys []K) { | |||||
| vs, getOks := mc.cfg.Getter(keys) | |||||
| mc.lock.Lock() | |||||
| defer mc.lock.Unlock() | |||||
| for i, key := range keys { | |||||
| if !getOks[i] { | |||||
| continue | |||||
| } | |||||
| _, ok := mc.cache[key] | |||||
| // 缓存中已有key则认为缓存中是最新的,不再更新 | |||||
| if ok { | |||||
| continue | |||||
| } | |||||
| entry := &CacheEntry[K, V]{ | |||||
| Key: key, | |||||
| Data: vs[i], | |||||
| UpdateTime: time.Now(), | |||||
| } | |||||
| mc.cache[key] = entry | |||||
| } | |||||
| } | |||||
| func (mc *SimpleMetaCache[K, V]) ClearOutdated() { | |||||
| mc.lock.Lock() | |||||
| defer mc.lock.Unlock() | |||||
| for key, entry := range mc.cache { | |||||
| dt := time.Since(entry.UpdateTime) | |||||
| if dt > mc.cfg.Expire || dt < 0 { | |||||
| delete(mc.cache, key) | |||||
| } | |||||
| } | |||||
| } | |||||
| type CacheEntry[K comparable, V any] struct { | |||||
| Key K | |||||
| Data V | |||||
| UpdateTime time.Time | |||||
| } | |||||
| @@ -0,0 +1,73 @@ | |||||
| package metacache | |||||
| import ( | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/storage2/client/types" | |||||
| ) | |||||
| func (m *MetaCacheHost) AddStorageMeta() *UserSpaceMeta { | |||||
| meta := &UserSpaceMeta{} | |||||
| meta.cache = NewSimpleMetaCache(SimpleMetaCacheConfig[types.UserSpaceID, types.UserSpaceDetail]{ | |||||
| Getter: meta.load, | |||||
| Expire: time.Minute * 5, | |||||
| }) | |||||
| m.caches = append(m.caches, meta) | |||||
| return meta | |||||
| } | |||||
| type UserSpaceMeta struct { | |||||
| cache *SimpleMetaCache[types.UserSpaceID, types.UserSpaceDetail] | |||||
| } | |||||
| func (s *UserSpaceMeta) Get(spaceID types.UserSpaceID) *types.UserSpaceDetail { | |||||
| v, ok := s.cache.Get(spaceID) | |||||
| if ok { | |||||
| return &v | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (s *UserSpaceMeta) GetMany(spaceIDs []types.UserSpaceID) []*types.UserSpaceDetail { | |||||
| vs, oks := s.cache.GetMany(spaceIDs) | |||||
| ret := make([]*types.UserSpaceDetail, len(vs)) | |||||
| for i := range vs { | |||||
| if oks[i] { | |||||
| ret[i] = &vs[i] | |||||
| } | |||||
| } | |||||
| return ret | |||||
| } | |||||
| func (s *UserSpaceMeta) ClearOutdated() { | |||||
| s.cache.ClearOutdated() | |||||
| } | |||||
| func (s *UserSpaceMeta) load(keys []types.UserSpaceID) ([]types.UserSpaceDetail, []bool) { | |||||
| // vs := make([]stgmod.StorageDetail, len(keys)) | |||||
| // oks := make([]bool, len(keys)) | |||||
| // coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| // if err != nil { | |||||
| // logger.Warnf("new coordinator client: %v", err) | |||||
| // return vs, oks | |||||
| // } | |||||
| // defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| // get, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails(keys)) | |||||
| // if err != nil { | |||||
| // logger.Warnf("get storage details: %v", err) | |||||
| // return vs, oks | |||||
| // } | |||||
| // for i := range keys { | |||||
| // if get.Storages[i] != nil { | |||||
| // vs[i] = *get.Storages[i] | |||||
| // oks[i] = true | |||||
| // } | |||||
| // } | |||||
| // return vs, oks | |||||
| } | |||||