cubefs/master/data_partition_map.go
slasher 5c7898354f style(all): format all codes with golangci tools
closes #3371

@formatter:off

Signed-off-by: slasher <shenjie1@oppo.com>
2024-05-14 09:54:13 +08:00

358 lines
10 KiB
Go

// Copyright 2018 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package master
import (
"encoding/json"
"fmt"
"runtime"
"sync"
"time"
"github.com/cubefs/cubefs/proto"
"github.com/cubefs/cubefs/util/compressor"
"github.com/cubefs/cubefs/util/log"
)
// DataPartitionMap stores all the data partitionMap
type DataPartitionMap struct {
sync.RWMutex
partitionMap map[uint64]*DataPartition
readableAndWritableCnt int // number of readable and writable partitionMap
lastLoadedIndex uint64 // last loaded partition index
lastReleasedIndex uint64 // last released partition index
partitions []*DataPartition
responseCache []byte
responseCompressCache []byte
lastAutoCreateTime time.Time
volName string
readMutex sync.RWMutex
}
func newDataPartitionMap(volName string) (dpMap *DataPartitionMap) {
dpMap = new(DataPartitionMap)
dpMap.partitionMap = make(map[uint64]*DataPartition)
dpMap.partitions = make([]*DataPartition, 0)
dpMap.responseCache = make([]byte, 0)
dpMap.responseCompressCache = make([]byte, 0)
dpMap.volName = volName
dpMap.lastAutoCreateTime = time.Now()
return
}
// attention: it's not deep clone for element, dataPartition
func (dpMap *DataPartitionMap) clonePartitions() []*DataPartition {
dpMap.RLock()
partitions := make([]*DataPartition, 0, len(dpMap.partitions))
partitions = append(partitions, dpMap.partitions...)
dpMap.RUnlock()
return partitions
}
func (dpMap *DataPartitionMap) get(ID uint64) (*DataPartition, error) {
dpMap.RLock()
defer dpMap.RUnlock()
if v, ok := dpMap.partitionMap[ID]; ok {
return v, nil
}
return nil, proto.ErrDataPartitionNotExists
}
func (dpMap *DataPartitionMap) del(dp *DataPartition) {
dpMap.Lock()
defer dpMap.Unlock()
_, ok := dpMap.partitionMap[dp.PartitionID]
if !ok {
return
}
dataPartitions := make([]*DataPartition, 0)
for index, partition := range dpMap.partitions {
if partition.PartitionID == dp.PartitionID {
dataPartitions = append(dataPartitions, dpMap.partitions[:index]...)
dataPartitions = append(dataPartitions, dpMap.partitions[index+1:]...)
dpMap.partitions = dataPartitions
break
}
}
delete(dpMap.partitionMap, dp.PartitionID)
}
func (dpMap *DataPartitionMap) put(dp *DataPartition) {
dpMap.Lock()
defer dpMap.Unlock()
_, ok := dpMap.partitionMap[dp.PartitionID]
if !ok {
dpMap.partitions = append(dpMap.partitions, dp)
dpMap.partitionMap[dp.PartitionID] = dp
return
}
// replace the old partition with dp in the map and array
dpMap.partitionMap[dp.PartitionID] = dp
dataPartitions := make([]*DataPartition, 0)
for index, partition := range dpMap.partitions {
if partition.PartitionID == dp.PartitionID {
dataPartitions = append(dataPartitions, dpMap.partitions[:index]...)
dataPartitions = append(dataPartitions, dp)
dataPartitions = append(dataPartitions, dpMap.partitions[index+1:]...)
dpMap.partitions = dataPartitions
break
}
}
}
func (dpMap *DataPartitionMap) setReadWriteDataPartitions(readWrites int, clusterName string) {
dpMap.Lock()
defer dpMap.Unlock()
dpMap.readableAndWritableCnt = readWrites
}
func (dpMap *DataPartitionMap) getDataPartitionResponseCache() []byte {
dpMap.RLock()
defer dpMap.RUnlock()
return dpMap.responseCache
}
func (dpMap *DataPartitionMap) getDataPartitionCompressCache() []byte {
dpMap.RLock()
defer dpMap.RUnlock()
return dpMap.responseCompressCache
}
func (dpMap *DataPartitionMap) setDataPartitionResponseCache(responseCache []byte) {
dpMap.Lock()
defer dpMap.Unlock()
if responseCache != nil {
dpMap.responseCache = responseCache
}
}
func (dpMap *DataPartitionMap) setDataPartitionCompressCache(responseCompress []byte) {
dpMap.Lock()
defer dpMap.Unlock()
if responseCompress != nil {
dpMap.responseCompressCache = responseCompress
}
}
func (dpMap *DataPartitionMap) updateResponseCache(needsUpdate bool, minPartitionID uint64, volType int) (body []byte, err error) {
responseCache := dpMap.getDataPartitionResponseCache()
if responseCache == nil || needsUpdate || len(responseCache) == 0 {
dpMap.readMutex.Lock()
defer dpMap.readMutex.Unlock()
responseCache = dpMap.getDataPartitionResponseCache()
if !(responseCache == nil || needsUpdate || len(responseCache) == 0) {
body = responseCache
return
}
dpResps := dpMap.getDataPartitionsView(minPartitionID)
if len(dpResps) == 0 && proto.IsHot(volType) {
log.LogError(fmt.Sprintf("action[updateDpResponseCache],volName[%v] minPartitionID:%v,err:%v",
dpMap.volName, minPartitionID, proto.ErrNoAvailDataPartition))
return nil, proto.ErrNoAvailDataPartition
}
cv := proto.NewDataPartitionsView()
cv.DataPartitions = dpResps
reply := newSuccessHTTPReply(cv)
if body, err = json.Marshal(reply); err != nil {
log.LogError(fmt.Sprintf("action[updateDpResponseCache],minPartitionID:%v,err:%v",
minPartitionID, err.Error()))
return nil, proto.ErrMarshalData
}
dpMap.setDataPartitionResponseCache(body)
return
}
body = responseCache
return
}
func (dpMap *DataPartitionMap) updateCompressCache(needsUpdate bool, minPartitionID uint64, volType int) (body []byte, err error) {
body = dpMap.getDataPartitionCompressCache()
if len(body) > 0 {
return
}
if body, err = dpMap.updateResponseCache(needsUpdate, minPartitionID, volType); err != nil {
log.LogErrorf("action[updateCompressCache]updateResponseCache failed,err:%+v", err)
return
}
if body, err = compressor.New(compressor.EncodingGzip).Compress(body); err != nil {
log.LogErrorf("action[updateCompressCache]GzipCompressor.Compress failed,err:%+v", err)
err = proto.ErrCompressFailed
return
}
dpMap.setDataPartitionCompressCache(body)
return
}
func (dpMap *DataPartitionMap) getDataPartitionsView(minPartitionID uint64) (dpResps []*proto.DataPartitionResponse) {
dpResps = make([]*proto.DataPartitionResponse, 0)
log.LogDebugf("volName[%v] DataPartitionMapLen[%v],DataPartitionsLen[%v],minPartitionID[%v]",
dpMap.volName, len(dpMap.partitionMap), len(dpMap.partitions), minPartitionID)
dpMap.RLock()
defer dpMap.RUnlock()
for _, dp := range dpMap.partitionMap {
if len(dp.Hosts) == 0 {
log.LogErrorf("getDataPartitionsView. dp %v host nil", dp.PartitionID)
continue
}
if dp.PartitionID <= minPartitionID {
continue
}
dpResp := dp.convertToDataPartitionResponse()
dpResps = append(dpResps, dpResp)
}
return
}
func (dpMap *DataPartitionMap) getDataPartitionsToBeReleased(numberOfDataPartitionsToFree int, secondsToFreeDataPartitionAfterLoad int64) (partitions []*DataPartition, startIndex uint64) {
partitions = make([]*DataPartition, 0)
dpMap.RLock()
defer dpMap.RUnlock()
dpLen := len(dpMap.partitions)
if dpLen == 0 {
return
}
startIndex = dpMap.lastReleasedIndex
count := numberOfDataPartitionsToFree
if dpLen < numberOfDataPartitionsToFree {
count = dpLen
}
for i := 0; i < count; i++ {
if dpMap.lastReleasedIndex >= uint64(dpLen) {
dpMap.lastReleasedIndex = 0
}
dp := dpMap.partitions[dpMap.lastReleasedIndex]
dpMap.lastReleasedIndex++
if time.Now().Unix()-dp.LastLoadedTime >= secondsToFreeDataPartitionAfterLoad {
partitions = append(partitions, dp)
}
}
return
}
func (dpMap *DataPartitionMap) freeMemOccupiedByDataPartitions(partitions []*DataPartition) {
var wg sync.WaitGroup
for _, dp := range partitions {
wg.Add(1)
go func(dp *DataPartition) {
defer func() {
wg.Done()
if err := recover(); err != nil {
const size = runtimeStackBufSize
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.LogError(fmt.Sprintf("[%v] freeMemOccupiedByDataPartitions panic %v: %s\n", dpMap.volName, err, buf))
}
}()
dp.releaseDataPartition()
}(dp)
}
wg.Wait()
}
func (dpMap *DataPartitionMap) getDataPartitionsToBeChecked(loadFrequencyTime int64) (partitions []*DataPartition, startIndex uint64) {
partitions = make([]*DataPartition, 0)
dpMap.RLock()
defer dpMap.RUnlock()
dpLen := len(dpMap.partitions)
if dpLen == 0 {
return
}
startIndex = dpMap.lastLoadedIndex
// determine the number of data partitions to load
count := dpLen / intervalToLoadDataPartition
if count == 0 {
count = 1
}
for i := 0; i < count; i++ {
if dpMap.lastLoadedIndex >= (uint64)(len(dpMap.partitions)) {
dpMap.lastLoadedIndex = 0
}
dp := dpMap.partitions[dpMap.lastLoadedIndex]
dpMap.lastLoadedIndex++
if time.Now().Unix()-dp.LastLoadedTime >= loadFrequencyTime {
partitions = append(partitions, dp)
}
}
return
}
func (dpMap *DataPartitionMap) totalUsedSpace() (totalUsed uint64) {
dpMap.RLock()
defer dpMap.RUnlock()
for _, dp := range dpMap.partitions {
totalUsed = totalUsed + dp.getMaxUsedSpace()
}
return
}
func (dpMap *DataPartitionMap) setAllDataPartitionsToReadOnly() {
dpMap.Lock()
defer dpMap.Unlock()
changedCnt := 0
for _, dp := range dpMap.partitions {
if proto.ReadWrite == dp.Status {
dp.Status = proto.ReadOnly
changedCnt++
}
}
log.LogDebugf("action[setAllDataPartitionsToReadOnly] ReadWrite->ReadOnly dp cnt: %v", changedCnt)
}
func (dpMap *DataPartitionMap) checkBadDiskDataPartitions(diskPath, nodeAddr string) (partitions []*DataPartition) {
dpMap.RLock()
defer dpMap.RUnlock()
partitions = make([]*DataPartition, 0)
for _, dp := range dpMap.partitionMap {
if dp.containsBadDisk(diskPath, nodeAddr) {
partitions = append(partitions, dp)
}
}
return
}
func (dpMap *DataPartitionMap) getReplicaDiskPaths(nodeAddr string) (diskPaths []string) {
dpMap.RLock()
defer dpMap.RUnlock()
diskPaths = make([]string, 0)
for _, dp := range dpMap.partitionMap {
disk := dp.getReplicaDisk(nodeAddr)
if len(disk) != 0 && !inStingList(disk, diskPaths) {
diskPaths = append(diskPaths, disk)
}
}
return
}
func inStingList(target string, strArray []string) bool {
for _, element := range strArray {
if target == element {
return true
}
}
return false
}