2019-05-22 13:41:20 +08:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-04-18 10:35:43 +08:00
|
|
|
"golang.org/x/exp/slices"
|
2020-08-27 00:16:58 +08:00
|
|
|
"os"
|
2019-05-22 13:41:20 +08:00
|
|
|
"path"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/chrislusf/seaweedfs/weed/storage/erasure_coding"
|
|
|
|
"github.com/chrislusf/seaweedfs/weed/storage/needle"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-02-18 14:05:28 +08:00
|
|
|
re = regexp.MustCompile(`\.ec[0-9][0-9]`)
|
2019-05-22 13:41:20 +08:00
|
|
|
)
|
|
|
|
|
2019-05-28 12:40:51 +08:00
|
|
|
func (l *DiskLocation) FindEcVolume(vid needle.VolumeId) (*erasure_coding.EcVolume, bool) {
|
|
|
|
l.ecVolumesLock.RLock()
|
|
|
|
defer l.ecVolumesLock.RUnlock()
|
2019-05-27 16:29:46 +08:00
|
|
|
|
2019-05-28 12:40:51 +08:00
|
|
|
ecVolume, ok := l.ecVolumes[vid]
|
2019-05-27 16:29:46 +08:00
|
|
|
if ok {
|
2019-05-28 12:40:51 +08:00
|
|
|
return ecVolume, true
|
2019-05-27 16:29:46 +08:00
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2019-06-01 16:51:28 +08:00
|
|
|
func (l *DiskLocation) DestroyEcVolume(vid needle.VolumeId) {
|
|
|
|
l.ecVolumesLock.Lock()
|
|
|
|
defer l.ecVolumesLock.Unlock()
|
|
|
|
|
|
|
|
ecVolume, found := l.ecVolumes[vid]
|
|
|
|
if found {
|
|
|
|
ecVolume.Destroy()
|
2019-06-05 13:04:10 +08:00
|
|
|
delete(l.ecVolumes, vid)
|
2019-06-01 16:51:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-26 14:23:19 +08:00
|
|
|
func (l *DiskLocation) FindEcShard(vid needle.VolumeId, shardId erasure_coding.ShardId) (*erasure_coding.EcVolumeShard, bool) {
|
2019-05-28 12:40:51 +08:00
|
|
|
l.ecVolumesLock.RLock()
|
|
|
|
defer l.ecVolumesLock.RUnlock()
|
2019-05-22 13:41:20 +08:00
|
|
|
|
2019-05-28 12:40:51 +08:00
|
|
|
ecVolume, ok := l.ecVolumes[vid]
|
2019-05-26 14:23:19 +08:00
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
2019-05-28 12:40:51 +08:00
|
|
|
for _, ecShard := range ecVolume.Shards {
|
2019-05-26 14:23:19 +08:00
|
|
|
if ecShard.ShardId == shardId {
|
|
|
|
return ecShard, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *DiskLocation) LoadEcShard(collection string, vid needle.VolumeId, shardId erasure_coding.ShardId) (err error) {
|
|
|
|
|
2021-02-16 18:47:02 +08:00
|
|
|
ecVolumeShard, err := erasure_coding.NewEcVolumeShard(l.DiskType, l.Directory, collection, vid, shardId)
|
2019-05-26 14:23:19 +08:00
|
|
|
if err != nil {
|
2020-08-27 00:16:58 +08:00
|
|
|
if err == os.ErrNotExist {
|
|
|
|
return os.ErrNotExist
|
|
|
|
}
|
2019-05-26 14:23:19 +08:00
|
|
|
return fmt.Errorf("failed to create ec shard %d.%d: %v", vid, shardId, err)
|
|
|
|
}
|
2019-05-28 12:40:51 +08:00
|
|
|
l.ecVolumesLock.Lock()
|
2019-06-03 17:26:31 +08:00
|
|
|
defer l.ecVolumesLock.Unlock()
|
2019-05-28 13:00:36 +08:00
|
|
|
ecVolume, found := l.ecVolumes[vid]
|
|
|
|
if !found {
|
2021-02-16 18:47:02 +08:00
|
|
|
ecVolume, err = erasure_coding.NewEcVolume(l.DiskType, l.Directory, l.IdxDirectory, collection, vid)
|
2019-05-28 13:00:36 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create ec volume %d: %v", vid, err)
|
|
|
|
}
|
|
|
|
l.ecVolumes[vid] = ecVolume
|
|
|
|
}
|
|
|
|
ecVolume.AddEcVolumeShard(ecVolumeShard)
|
2019-05-26 14:23:19 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *DiskLocation) UnloadEcShard(vid needle.VolumeId, shardId erasure_coding.ShardId) bool {
|
|
|
|
|
2019-05-28 12:40:51 +08:00
|
|
|
l.ecVolumesLock.Lock()
|
|
|
|
defer l.ecVolumesLock.Unlock()
|
2019-05-26 14:23:19 +08:00
|
|
|
|
2019-05-28 12:40:51 +08:00
|
|
|
ecVolume, found := l.ecVolumes[vid]
|
2019-05-26 14:23:19 +08:00
|
|
|
if !found {
|
|
|
|
return false
|
|
|
|
}
|
2019-06-01 16:41:22 +08:00
|
|
|
if _, deleted := ecVolume.DeleteEcVolumeShard(shardId); deleted {
|
2019-05-28 12:40:51 +08:00
|
|
|
if len(ecVolume.Shards) == 0 {
|
|
|
|
delete(l.ecVolumes, vid)
|
2019-06-06 14:20:26 +08:00
|
|
|
ecVolume.Close()
|
2019-05-26 14:23:19 +08:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *DiskLocation) loadEcShards(shards []string, collection string, vid needle.VolumeId) (err error) {
|
|
|
|
|
|
|
|
for _, shard := range shards {
|
2019-05-22 13:41:20 +08:00
|
|
|
shardId, err := strconv.ParseInt(path.Ext(shard)[3:], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to parse ec shard name %v: %v", shard, err)
|
|
|
|
}
|
2019-05-26 14:23:19 +08:00
|
|
|
|
|
|
|
err = l.LoadEcShard(collection, vid, erasure_coding.ShardId(shardId))
|
2019-05-22 13:41:20 +08:00
|
|
|
if err != nil {
|
2019-05-26 14:23:19 +08:00
|
|
|
return fmt.Errorf("failed to load ec shard %v: %v", shard, err)
|
2019-05-22 13:41:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-26 14:23:19 +08:00
|
|
|
func (l *DiskLocation) loadAllEcShards() (err error) {
|
2019-05-22 13:41:20 +08:00
|
|
|
|
2021-10-14 12:27:58 +08:00
|
|
|
dirEntries, err := os.ReadDir(l.Directory)
|
2019-05-22 13:41:20 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("load all ec shards in dir %s: %v", l.Directory, err)
|
|
|
|
}
|
2020-11-27 19:17:10 +08:00
|
|
|
if l.IdxDirectory != l.Directory {
|
2021-10-14 12:27:58 +08:00
|
|
|
indexDirEntries, err := os.ReadDir(l.IdxDirectory)
|
2020-11-27 19:17:10 +08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("load all ec shards in dir %s: %v", l.IdxDirectory, err)
|
|
|
|
}
|
2021-10-14 12:27:58 +08:00
|
|
|
dirEntries = append(dirEntries, indexDirEntries...)
|
2020-11-27 19:17:10 +08:00
|
|
|
}
|
2022-04-18 10:35:43 +08:00
|
|
|
slices.SortFunc(dirEntries, func(a, b os.DirEntry) bool {
|
|
|
|
return a.Name() < b.Name()
|
2019-05-22 13:41:20 +08:00
|
|
|
})
|
|
|
|
var sameVolumeShards []string
|
|
|
|
var prevVolumeId needle.VolumeId
|
2021-10-14 12:27:58 +08:00
|
|
|
for _, fileInfo := range dirEntries {
|
2019-05-26 14:23:19 +08:00
|
|
|
if fileInfo.IsDir() {
|
2019-05-22 13:41:20 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
ext := path.Ext(fileInfo.Name())
|
|
|
|
name := fileInfo.Name()
|
|
|
|
baseName := name[:len(name)-len(ext)]
|
|
|
|
|
|
|
|
collection, volumeId, err := parseCollectionVolumeId(baseName)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-05-26 14:23:19 +08:00
|
|
|
if re.MatchString(ext) {
|
2019-05-22 13:41:20 +08:00
|
|
|
if prevVolumeId == 0 || volumeId == prevVolumeId {
|
|
|
|
sameVolumeShards = append(sameVolumeShards, fileInfo.Name())
|
2019-05-26 14:23:19 +08:00
|
|
|
} else {
|
2019-05-22 13:41:20 +08:00
|
|
|
sameVolumeShards = []string{fileInfo.Name()}
|
|
|
|
}
|
|
|
|
prevVolumeId = volumeId
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-05-26 14:23:19 +08:00
|
|
|
if ext == ".ecx" && volumeId == prevVolumeId {
|
|
|
|
if err = l.loadEcShards(sameVolumeShards, collection, volumeId); err != nil {
|
2019-05-22 13:41:20 +08:00
|
|
|
return fmt.Errorf("loadEcShards collection:%v volumeId:%d : %v", collection, volumeId, err)
|
|
|
|
}
|
|
|
|
prevVolumeId = volumeId
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-05-31 00:47:54 +08:00
|
|
|
|
|
|
|
func (l *DiskLocation) deleteEcVolumeById(vid needle.VolumeId) (e error) {
|
|
|
|
ecVolume, ok := l.ecVolumes[vid]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ecVolume.Destroy()
|
|
|
|
delete(l.ecVolumes, vid)
|
|
|
|
return
|
|
|
|
}
|
2019-12-24 17:20:34 +08:00
|
|
|
|
|
|
|
func (l *DiskLocation) unmountEcVolumeByCollection(collectionName string) map[needle.VolumeId]*erasure_coding.EcVolume {
|
|
|
|
deltaVols := make(map[needle.VolumeId]*erasure_coding.EcVolume, 0)
|
|
|
|
for k, v := range l.ecVolumes {
|
|
|
|
if v.Collection == collectionName {
|
|
|
|
deltaVols[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, _ := range deltaVols {
|
|
|
|
delete(l.ecVolumes, k)
|
|
|
|
}
|
|
|
|
return deltaVols
|
|
|
|
}
|
2020-03-23 07:21:42 +08:00
|
|
|
|
|
|
|
func (l *DiskLocation) EcVolumesLen() int {
|
|
|
|
l.ecVolumesLock.RLock()
|
|
|
|
defer l.ecVolumesLock.RUnlock()
|
|
|
|
|
|
|
|
return len(l.ecVolumes)
|
|
|
|
}
|