2012-09-10 15:18:07 +08:00
package topology
import (
2015-01-08 15:54:50 +08:00
"fmt"
2024-03-22 22:39:11 +08:00
"github.com/seaweedfs/seaweedfs/weed/stats"
2024-08-30 00:52:21 +08:00
"math/rand/v2"
2013-07-16 12:34:43 +08:00
"sync"
2022-08-30 04:23:02 +08:00
"sync/atomic"
2018-11-23 16:24:51 +08:00
"time"
2014-10-27 02:34:55 +08:00
2023-06-06 01:17:21 +08:00
"github.com/seaweedfs/seaweedfs/weed/storage/types"
2022-07-29 15:17:28 +08:00
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/storage"
"github.com/seaweedfs/seaweedfs/weed/storage/needle"
"github.com/seaweedfs/seaweedfs/weed/storage/super_block"
2012-09-10 15:18:07 +08:00
)
2020-09-22 21:31:14 +08:00
type copyState int
const (
noCopies copyState = 0 + iota
insufficientCopies
enoughCopies
)
type volumeState string
const (
2024-07-31 04:21:35 +08:00
readOnlyState volumeState = "ReadOnly"
oversizedState = "Oversized"
crowdedState = "Crowded"
noWritableVolumes = "No writable volumes"
2020-09-22 21:31:14 +08:00
)
type stateIndicator func ( copyState ) bool
func ExistCopies ( ) stateIndicator {
return func ( state copyState ) bool { return state != noCopies }
}
func NoCopies ( ) stateIndicator {
return func ( state copyState ) bool { return state == noCopies }
}
type volumesBinaryState struct {
rp * super_block . ReplicaPlacement
name volumeState // the name for volume state (eg. "Readonly", "Oversized")
indicator stateIndicator // indicate whether the volumes should be marked as `name`
copyMap map [ needle . VolumeId ] * VolumeLocationList
}
func NewVolumesBinaryState ( name volumeState , rp * super_block . ReplicaPlacement , indicator stateIndicator ) * volumesBinaryState {
return & volumesBinaryState {
rp : rp ,
name : name ,
indicator : indicator ,
copyMap : make ( map [ needle . VolumeId ] * VolumeLocationList ) ,
}
}
func ( v * volumesBinaryState ) Dump ( ) ( res [ ] uint32 ) {
for vid , list := range v . copyMap {
if v . indicator ( v . copyState ( list ) ) {
res = append ( res , uint32 ( vid ) )
}
}
return
}
func ( v * volumesBinaryState ) IsTrue ( vid needle . VolumeId ) bool {
list , _ := v . copyMap [ vid ]
return v . indicator ( v . copyState ( list ) )
}
func ( v * volumesBinaryState ) Add ( vid needle . VolumeId , dn * DataNode ) {
list , _ := v . copyMap [ vid ]
if list != nil {
list . Set ( dn )
return
}
list = NewVolumeLocationList ( )
list . Set ( dn )
v . copyMap [ vid ] = list
}
func ( v * volumesBinaryState ) Remove ( vid needle . VolumeId , dn * DataNode ) {
list , _ := v . copyMap [ vid ]
if list != nil {
list . Remove ( dn )
if list . Length ( ) == 0 {
delete ( v . copyMap , vid )
}
}
}
func ( v * volumesBinaryState ) copyState ( list * VolumeLocationList ) copyState {
if list == nil {
return noCopies
}
if list . Length ( ) < v . rp . GetCopyCount ( ) {
return insufficientCopies
}
return enoughCopies
}
2014-03-11 02:43:54 +08:00
// mapping from volume to its locations, inverted from server to volume
2012-09-10 15:18:07 +08:00
type VolumeLayout struct {
2024-07-16 01:51:21 +08:00
growRequest atomic . Bool
2024-07-16 23:03:40 +08:00
lastGrowCount atomic . Uint32
2024-07-31 04:21:35 +08:00
rp * super_block . ReplicaPlacement
2019-04-19 12:43:36 +08:00
ttl * needle . TTL
2021-02-16 18:47:02 +08:00
diskType types . DiskType
2019-04-19 12:43:36 +08:00
vid2location map [ needle . VolumeId ] * VolumeLocationList
2021-05-06 18:46:14 +08:00
writables [ ] needle . VolumeId // transient array of writable volume id
2021-05-12 01:05:31 +08:00
crowded map [ needle . VolumeId ] struct { }
2020-09-22 21:31:14 +08:00
readonlyVolumes * volumesBinaryState // readonly volumes
oversizedVolumes * volumesBinaryState // oversized volumes
2023-06-06 01:17:21 +08:00
vacuumedVolumes map [ needle . VolumeId ] time . Time
2016-06-28 06:28:23 +08:00
volumeSizeLimit uint64
2020-04-02 03:18:40 +08:00
replicationAsMin bool
2016-06-28 06:28:23 +08:00
accessLock sync . RWMutex
2012-09-10 15:18:07 +08:00
}
2018-11-23 16:24:51 +08:00
type VolumeLayoutStats struct {
TotalSize uint64
UsedSize uint64
FileCount uint64
}
2021-02-16 18:47:02 +08:00
func NewVolumeLayout ( rp * super_block . ReplicaPlacement , ttl * needle . TTL , diskType types . DiskType , volumeSizeLimit uint64 , replicationAsMin bool ) * VolumeLayout {
2012-09-10 15:18:07 +08:00
return & VolumeLayout {
2016-06-28 06:28:23 +08:00
rp : rp ,
ttl : ttl ,
2020-12-17 01:14:05 +08:00
diskType : diskType ,
2019-04-19 12:43:36 +08:00
vid2location : make ( map [ needle . VolumeId ] * VolumeLocationList ) ,
writables : * new ( [ ] needle . VolumeId ) ,
2021-05-12 01:05:31 +08:00
crowded : make ( map [ needle . VolumeId ] struct { } ) ,
2020-09-22 21:31:14 +08:00
readonlyVolumes : NewVolumesBinaryState ( readOnlyState , rp , ExistCopies ( ) ) ,
oversizedVolumes : NewVolumesBinaryState ( oversizedState , rp , ExistCopies ( ) ) ,
2023-06-06 01:17:21 +08:00
vacuumedVolumes : make ( map [ needle . VolumeId ] time . Time ) ,
2016-06-28 06:28:23 +08:00
volumeSizeLimit : volumeSizeLimit ,
2020-04-02 03:18:40 +08:00
replicationAsMin : replicationAsMin ,
2012-09-10 15:18:07 +08:00
}
}
2015-01-08 15:54:50 +08:00
func ( vl * VolumeLayout ) String ( ) string {
2022-03-21 15:04:01 +08:00
return fmt . Sprintf ( "rp:%v, ttl:%v, writables:%v, volumeSizeLimit:%v" , vl . rp , vl . ttl , vl . writables , vl . volumeSizeLimit )
2015-01-08 15:54:50 +08:00
}
2012-09-10 15:18:07 +08:00
func ( vl * VolumeLayout ) RegisterVolume ( v * storage . VolumeInfo , dn * DataNode ) {
2013-07-16 12:34:43 +08:00
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
2020-09-22 21:31:14 +08:00
defer vl . rememberOversizedVolume ( v , dn )
2020-03-14 06:41:24 +08:00
2019-04-22 04:32:36 +08:00
if _ , ok := vl . vid2location [ v . Id ] ; ! ok {
2012-09-20 07:48:04 +08:00
vl . vid2location [ v . Id ] = NewVolumeLocationList ( )
2012-09-10 15:18:07 +08:00
}
2014-03-19 19:48:13 +08:00
vl . vid2location [ v . Id ] . Set ( dn )
2019-01-03 04:58:06 +08:00
// glog.V(4).Infof("volume %d added to %s len %d copy %d", v.Id, dn.Id(), vl.vid2location[v.Id].Length(), v.ReplicaPlacement.GetCopyCount())
2016-08-09 20:12:39 +08:00
for _ , dn := range vl . vid2location [ v . Id ] . list {
2019-01-17 09:17:19 +08:00
if vInfo , err := dn . GetVolumesById ( v . Id ) ; err == nil {
if vInfo . ReadOnly {
2019-04-21 14:53:37 +08:00
glog . V ( 1 ) . Infof ( "vid %d removed from writable" , v . Id )
2016-08-09 20:12:39 +08:00
vl . removeFromWritable ( v . Id )
2020-09-22 21:31:14 +08:00
vl . readonlyVolumes . Add ( v . Id , dn )
2016-08-09 20:12:39 +08:00
return
2017-05-23 08:05:27 +08:00
} else {
2020-09-22 21:31:14 +08:00
vl . readonlyVolumes . Remove ( v . Id , dn )
2016-08-09 20:12:39 +08:00
}
} else {
2019-04-21 14:53:37 +08:00
glog . V ( 1 ) . Infof ( "vid %d removed from writable" , v . Id )
2016-08-09 20:12:39 +08:00
vl . removeFromWritable ( v . Id )
2020-09-22 21:31:14 +08:00
vl . readonlyVolumes . Remove ( v . Id , dn )
2016-08-09 20:12:39 +08:00
return
}
}
2019-04-22 04:32:36 +08:00
2014-03-19 19:48:13 +08:00
}
2020-09-22 21:31:14 +08:00
func ( vl * VolumeLayout ) rememberOversizedVolume ( v * storage . VolumeInfo , dn * DataNode ) {
2016-06-28 06:28:23 +08:00
if vl . isOversized ( v ) {
2020-09-22 21:31:14 +08:00
vl . oversizedVolumes . Add ( v . Id , dn )
} else {
vl . oversizedVolumes . Remove ( v . Id , dn )
2016-06-28 06:28:23 +08:00
}
}
2014-09-21 03:38:59 +08:00
func ( vl * VolumeLayout ) UnRegisterVolume ( v * storage . VolumeInfo , dn * DataNode ) {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
2019-04-22 04:32:36 +08:00
// remove from vid2location map
location , ok := vl . vid2location [ v . Id ]
if ! ok {
return
}
if location . Remove ( dn ) {
2020-09-22 21:31:14 +08:00
vl . readonlyVolumes . Remove ( v . Id , dn )
vl . oversizedVolumes . Remove ( v . Id , dn )
2020-10-24 16:34:31 +08:00
vl . ensureCorrectWritables ( v . Id )
2019-04-22 04:32:36 +08:00
if location . Length ( ) == 0 {
delete ( vl . vid2location , v . Id )
}
}
}
adding locking to avoid nil VolumeLocationList
fix panic: runtime error: invalid memory address or nil pointer dereference
Oct 22 00:53:44 bedb-master1 weed[8055]: [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x17658da]
Oct 22 00:53:44 bedb-master1 weed[8055]: goroutine 310 [running]:
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*VolumeLocationList).Length(...)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/volume_location_list.go:35
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*VolumeLayout).enoughCopies(...)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/volume_layout.go:376
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*VolumeLayout).ensureCorrectWritables(0xc000111d50, 0xc000b55438)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/volume_layout.go:202 +0x5a
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*Topology).SyncDataNodeRegistration(0xc00042ac60, 0xc001454d30, 0x1, 0x1, 0xc0005fc000, 0xc00135de40, 0x4, 0xc00135de50, 0x10, 0x10d, ...)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/topology.go:224 +0x616
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/server.(*MasterServer).SendHeartbeat(0xc000162700, 0x23b97c0, 0xc000ae2c90, 0x0, 0x0)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/server/master_grpc_server.go:106 +0x325
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/pb/master_pb._Seaweed_SendHeartbeat_Handler(0x1f8e7c0, 0xc000162700, 0x23b0a60, 0xc00024b440, 0x3172c38, 0xc000ab7100)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/pb/master_pb/master.pb.go:4250 +0xad
Oct 22 00:53:44 bedb-master1 weed[8055]: google.golang.org/grpc.(*Server).processStreamingRPC(0xc0001f31e0, 0x23bb800, 0xc000ac5500, 0xc000ab7100, 0xc0001fea80, 0x311fec0, 0x0, 0x0, 0x0)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:1329 +0xcd8
Oct 22 00:53:44 bedb-master1 weed[8055]: google.golang.org/grpc.(*Server).handleStream(0xc0001f31e0, 0x23bb800, 0xc000ac5500, 0xc000ab7100, 0x0)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:1409 +0xc5c
Oct 22 00:53:44 bedb-master1 weed[8055]: google.golang.org/grpc.(*Server).serveStreams.func1.1(0xc0001ce8b0, 0xc0001f31e0, 0x23bb800, 0xc000ac5500, 0xc000ab7100)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:746 +0xa5
Oct 22 00:53:44 bedb-master1 weed[8055]: created by google.golang.org/grpc.(*Server).serveStreams.func1
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:744 +0xa5
Oct 22 00:53:44 bedb-master1 systemd[1]: weedmaster.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
Oct 22 00:53:44 bedb-master1 systemd[1]: weedmaster.service: Failed with result 'exit-code'.
2020-10-22 14:15:48 +08:00
func ( vl * VolumeLayout ) EnsureCorrectWritables ( v * storage . VolumeInfo ) {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
2020-10-24 16:34:31 +08:00
vl . ensureCorrectWritables ( v . Id )
adding locking to avoid nil VolumeLocationList
fix panic: runtime error: invalid memory address or nil pointer dereference
Oct 22 00:53:44 bedb-master1 weed[8055]: [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x17658da]
Oct 22 00:53:44 bedb-master1 weed[8055]: goroutine 310 [running]:
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*VolumeLocationList).Length(...)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/volume_location_list.go:35
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*VolumeLayout).enoughCopies(...)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/volume_layout.go:376
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*VolumeLayout).ensureCorrectWritables(0xc000111d50, 0xc000b55438)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/volume_layout.go:202 +0x5a
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/topology.(*Topology).SyncDataNodeRegistration(0xc00042ac60, 0xc001454d30, 0x1, 0x1, 0xc0005fc000, 0xc00135de40, 0x4, 0xc00135de50, 0x10, 0x10d, ...)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/topology/topology.go:224 +0x616
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/server.(*MasterServer).SendHeartbeat(0xc000162700, 0x23b97c0, 0xc000ae2c90, 0x0, 0x0)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/server/master_grpc_server.go:106 +0x325
Oct 22 00:53:44 bedb-master1 weed[8055]: github.com/chrislusf/seaweedfs/weed/pb/master_pb._Seaweed_SendHeartbeat_Handler(0x1f8e7c0, 0xc000162700, 0x23b0a60, 0xc00024b440, 0x3172c38, 0xc000ab7100)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/seaweedfs/weed/pb/master_pb/master.pb.go:4250 +0xad
Oct 22 00:53:44 bedb-master1 weed[8055]: google.golang.org/grpc.(*Server).processStreamingRPC(0xc0001f31e0, 0x23bb800, 0xc000ac5500, 0xc000ab7100, 0xc0001fea80, 0x311fec0, 0x0, 0x0, 0x0)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:1329 +0xcd8
Oct 22 00:53:44 bedb-master1 weed[8055]: google.golang.org/grpc.(*Server).handleStream(0xc0001f31e0, 0x23bb800, 0xc000ac5500, 0xc000ab7100, 0x0)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:1409 +0xc5c
Oct 22 00:53:44 bedb-master1 weed[8055]: google.golang.org/grpc.(*Server).serveStreams.func1.1(0xc0001ce8b0, 0xc0001f31e0, 0x23bb800, 0xc000ac5500, 0xc000ab7100)
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:746 +0xa5
Oct 22 00:53:44 bedb-master1 weed[8055]: created by google.golang.org/grpc.(*Server).serveStreams.func1
Oct 22 00:53:44 bedb-master1 weed[8055]: #011/root/go/pkg/mod/google.golang.org/grpc@v1.29.1/server.go:744 +0xa5
Oct 22 00:53:44 bedb-master1 systemd[1]: weedmaster.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
Oct 22 00:53:44 bedb-master1 systemd[1]: weedmaster.service: Failed with result 'exit-code'.
2020-10-22 14:15:48 +08:00
}
2020-10-24 16:34:31 +08:00
func ( vl * VolumeLayout ) ensureCorrectWritables ( vid needle . VolumeId ) {
if vl . enoughCopies ( vid ) && vl . isAllWritable ( vid ) {
if ! vl . oversizedVolumes . IsTrue ( vid ) {
vl . setVolumeWritable ( vid )
2019-04-22 04:32:36 +08:00
}
} else {
2022-03-21 15:41:44 +08:00
if ! vl . enoughCopies ( vid ) {
glog . V ( 0 ) . Infof ( "volume %d does not have enough copies" , vid )
}
if ! vl . isAllWritable ( vid ) {
glog . V ( 0 ) . Infof ( "volume %d are not all writable" , vid )
}
glog . V ( 0 ) . Infof ( "volume %d remove from writable" , vid )
2020-10-24 16:34:31 +08:00
vl . removeFromWritable ( vid )
2019-04-22 04:32:36 +08:00
}
2014-09-21 03:38:59 +08:00
}
2020-10-24 16:34:31 +08:00
func ( vl * VolumeLayout ) isAllWritable ( vid needle . VolumeId ) bool {
2024-04-03 00:06:19 +08:00
if location , ok := vl . vid2location [ vid ] ; ok {
for _ , dn := range location . list {
if v , getError := dn . GetVolumesById ( vid ) ; getError == nil {
if v . ReadOnly {
return false
}
2020-10-24 16:34:31 +08:00
}
}
2024-04-03 00:06:19 +08:00
} else {
return false
2020-10-24 16:34:31 +08:00
}
2024-04-03 00:06:19 +08:00
2020-10-24 16:34:31 +08:00
return true
}
2016-06-28 06:28:23 +08:00
func ( vl * VolumeLayout ) isOversized ( v * storage . VolumeInfo ) bool {
2016-06-29 16:05:00 +08:00
return uint64 ( v . Size ) >= vl . volumeSizeLimit
2016-06-28 06:28:23 +08:00
}
2013-01-20 19:40:04 +08:00
func ( vl * VolumeLayout ) isWritable ( v * storage . VolumeInfo ) bool {
2016-06-29 16:05:00 +08:00
return ! vl . isOversized ( v ) &&
2019-04-19 12:43:36 +08:00
v . Version == needle . CurrentVersion &&
2013-04-15 10:30:26 +08:00
! v . ReadOnly
2012-12-18 08:48:54 +08:00
}
2018-07-11 17:01:33 +08:00
func ( vl * VolumeLayout ) isEmpty ( ) bool {
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
return len ( vl . vid2location ) == 0
}
2019-04-19 12:43:36 +08:00
func ( vl * VolumeLayout ) Lookup ( vid needle . VolumeId ) [ ] * DataNode {
2016-04-14 16:28:40 +08:00
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
2013-04-15 10:30:26 +08:00
if location := vl . vid2location [ vid ] ; location != nil {
return location . list
}
2013-03-20 16:23:15 +08:00
return nil
2012-09-24 06:45:26 +08:00
}
2014-03-11 02:43:54 +08:00
func ( vl * VolumeLayout ) ListVolumeServers ( ) ( nodes [ ] * DataNode ) {
2016-04-14 16:28:40 +08:00
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
2014-03-11 02:43:54 +08:00
for _ , location := range vl . vid2location {
nodes = append ( nodes , location . list ... )
}
return
}
2023-12-28 03:45:44 +08:00
func ( vl * VolumeLayout ) PickForWrite ( count uint64 , option * VolumeGrowOption ) ( vid needle . VolumeId , counter uint64 , locationList * VolumeLocationList , shouldGrow bool , err error ) {
2016-04-14 16:28:40 +08:00
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
2019-01-17 09:17:19 +08:00
lenWriters := len ( vl . writables )
if lenWriters <= 0 {
2024-07-31 04:21:35 +08:00
return 0 , 0 , nil , true , fmt . Errorf ( "%s in volume layout" , noWritableVolumes )
2012-09-11 08:08:52 +08:00
}
2021-12-21 17:28:33 +08:00
if option . DataCenter == "" && option . Rack == "" && option . DataNode == "" {
2024-08-30 00:52:21 +08:00
vid := vl . writables [ rand . IntN ( lenWriters ) ]
2023-12-28 03:45:44 +08:00
locationList = vl . vid2location [ vid ]
2024-07-31 04:21:35 +08:00
if locationList == nil || len ( locationList . list ) == 0 {
return 0 , 0 , nil , false , fmt . Errorf ( "Strangely vid %s is on no machine!" , vid . String ( ) )
2013-06-20 09:10:38 +08:00
}
2024-07-31 04:21:35 +08:00
return vid , count , locationList . Copy ( ) , false , nil
2015-03-10 15:20:31 +08:00
}
2023-12-28 03:45:44 +08:00
// clone vl.writables
writables := make ( [ ] needle . VolumeId , len ( vl . writables ) )
copy ( writables , vl . writables )
// randomize the writables
rand . Shuffle ( len ( writables ) , func ( i , j int ) {
writables [ i ] , writables [ j ] = writables [ j ] , writables [ i ]
} )
for _ , writableVolumeId := range writables {
volumeLocationList := vl . vid2location [ writableVolumeId ]
2015-03-10 15:20:31 +08:00
for _ , dn := range volumeLocationList . list {
2021-12-21 17:28:33 +08:00
if option . DataCenter != "" && dn . GetDataCenter ( ) . Id ( ) != NodeId ( option . DataCenter ) {
continue
}
if option . Rack != "" && dn . GetRack ( ) . Id ( ) != NodeId ( option . Rack ) {
continue
}
if option . DataNode != "" && dn . Id ( ) != NodeId ( option . DataNode ) {
continue
}
2024-07-31 04:21:35 +08:00
vid , locationList , counter = writableVolumeId , volumeLocationList . Copy ( ) , count
2023-12-28 03:45:44 +08:00
return
2013-06-20 09:10:38 +08:00
}
2012-09-11 08:08:52 +08:00
}
2024-07-31 04:21:35 +08:00
return vid , count , locationList , true , fmt . Errorf ( "%s in DataCenter:%v Rack:%v DataNode:%v" , noWritableVolumes , option . DataCenter , option . Rack , option . DataNode )
2012-09-17 08:31:15 +08:00
}
2021-10-05 16:58:30 +08:00
func ( vl * VolumeLayout ) HasGrowRequest ( ) bool {
2024-07-16 01:51:21 +08:00
return vl . growRequest . Load ( )
2021-10-05 16:58:30 +08:00
}
func ( vl * VolumeLayout ) AddGrowRequest ( ) {
2024-07-16 01:51:21 +08:00
vl . growRequest . Store ( true )
2021-10-05 16:58:30 +08:00
}
func ( vl * VolumeLayout ) DoneGrowRequest ( ) {
2024-07-16 01:51:21 +08:00
vl . growRequest . Store ( false )
2021-10-05 16:58:30 +08:00
}
2024-07-16 23:03:40 +08:00
func ( vl * VolumeLayout ) SetLastGrowCount ( count uint32 ) {
2024-08-28 00:03:11 +08:00
if vl . lastGrowCount . Load ( ) != count && count != 0 {
2024-07-16 23:03:40 +08:00
vl . lastGrowCount . Store ( count )
}
}
func ( vl * VolumeLayout ) GetLastGrowCount ( ) uint32 {
return vl . lastGrowCount . Load ( )
}
2021-10-05 15:40:04 +08:00
func ( vl * VolumeLayout ) ShouldGrowVolumes ( option * VolumeGrowOption ) bool {
2024-03-22 22:39:11 +08:00
total , active , crowded := vl . GetActiveVolumeCount ( option )
2024-07-13 03:32:25 +08:00
stats . MasterVolumeLayout . WithLabelValues ( option . Collection , option . DataCenter , "total" ) . Set ( float64 ( total ) )
stats . MasterVolumeLayout . WithLabelValues ( option . Collection , option . DataCenter , "active" ) . Set ( float64 ( active ) )
stats . MasterVolumeLayout . WithLabelValues ( option . Collection , option . DataCenter , "crowded" ) . Set ( float64 ( crowded ) )
2021-10-05 15:40:04 +08:00
//glog.V(0).Infof("active volume: %d, high usage volume: %d\n", active, high)
return active <= crowded
}
2024-03-22 22:39:11 +08:00
func ( vl * VolumeLayout ) GetActiveVolumeCount ( option * VolumeGrowOption ) ( total , active , crowded int ) {
2016-04-14 16:28:40 +08:00
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
2014-04-13 16:29:52 +08:00
if option . DataCenter == "" {
2024-03-22 22:39:11 +08:00
return len ( vl . writables ) , len ( vl . writables ) , len ( vl . crowded )
2013-06-20 09:10:38 +08:00
}
2024-03-22 22:39:11 +08:00
total = len ( vl . writables )
2013-06-20 09:10:38 +08:00
for _ , v := range vl . writables {
for _ , dn := range vl . vid2location [ v ] . list {
2014-04-13 16:29:52 +08:00
if dn . GetDataCenter ( ) . Id ( ) == NodeId ( option . DataCenter ) {
if option . Rack != "" && dn . GetRack ( ) . Id ( ) != NodeId ( option . Rack ) {
continue
}
if option . DataNode != "" && dn . Id ( ) != NodeId ( option . DataNode ) {
continue
}
2021-05-06 18:46:14 +08:00
active ++
info , _ := dn . GetVolumesById ( v )
2024-05-21 02:03:56 +08:00
if float64 ( info . Size ) > float64 ( vl . volumeSizeLimit ) * VolumeGrowStrategy . Threshold {
2021-05-06 18:46:14 +08:00
crowded ++
}
2013-06-20 09:10:38 +08:00
}
}
}
2021-05-06 18:46:14 +08:00
return
2012-09-17 08:31:15 +08:00
}
2019-04-19 12:43:36 +08:00
func ( vl * VolumeLayout ) removeFromWritable ( vid needle . VolumeId ) bool {
2013-08-13 07:39:49 +08:00
toDeleteIndex := - 1
for k , id := range vl . writables {
if id == vid {
toDeleteIndex = k
break
2012-09-20 07:48:04 +08:00
}
}
2013-08-13 07:39:49 +08:00
if toDeleteIndex >= 0 {
glog . V ( 0 ) . Infoln ( "Volume" , vid , "becomes unwritable" )
vl . writables = append ( vl . writables [ 0 : toDeleteIndex ] , vl . writables [ toDeleteIndex + 1 : ] ... )
2021-05-06 18:46:14 +08:00
vl . removeFromCrowded ( vid )
2013-08-13 07:39:49 +08:00
return true
}
2012-09-20 07:48:04 +08:00
return false
}
2019-04-19 12:43:36 +08:00
func ( vl * VolumeLayout ) setVolumeWritable ( vid needle . VolumeId ) bool {
2012-09-20 07:48:04 +08:00
for _ , v := range vl . writables {
if v == vid {
return false
}
}
2013-08-09 14:57:22 +08:00
glog . V ( 0 ) . Infoln ( "Volume" , vid , "becomes writable" )
2012-09-20 07:48:04 +08:00
vl . writables = append ( vl . writables , vid )
return true
}
2023-03-20 09:30:13 +08:00
func ( vl * VolumeLayout ) SetVolumeReadOnly ( dn * DataNode , vid needle . VolumeId ) bool {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
if _ , ok := vl . vid2location [ vid ] ; ok {
vl . readonlyVolumes . Add ( vid , dn )
return vl . removeFromWritable ( vid )
}
return true
}
func ( vl * VolumeLayout ) SetVolumeWritable ( dn * DataNode , vid needle . VolumeId ) bool {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
if _ , ok := vl . vid2location [ vid ] ; ok {
vl . readonlyVolumes . Remove ( vid , dn )
}
if vl . enoughCopies ( vid ) {
return vl . setVolumeWritable ( vid )
}
return false
}
2019-04-19 12:43:36 +08:00
func ( vl * VolumeLayout ) SetVolumeUnavailable ( dn * DataNode , vid needle . VolumeId ) bool {
2013-07-16 12:34:43 +08:00
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
2014-03-11 02:43:54 +08:00
if location , ok := vl . vid2location [ vid ] ; ok {
if location . Remove ( dn ) {
2020-09-22 21:31:14 +08:00
vl . readonlyVolumes . Remove ( vid , dn )
vl . oversizedVolumes . Remove ( vid , dn )
2014-03-11 02:43:54 +08:00
if location . Length ( ) < vl . rp . GetCopyCount ( ) {
glog . V ( 0 ) . Infoln ( "Volume" , vid , "has" , location . Length ( ) , "replica, less than required" , vl . rp . GetCopyCount ( ) )
return vl . removeFromWritable ( vid )
}
2012-09-20 17:11:08 +08:00
}
}
return false
2012-09-19 16:45:30 +08:00
}
2023-06-06 01:17:21 +08:00
func ( vl * VolumeLayout ) SetVolumeAvailable ( dn * DataNode , vid needle . VolumeId , isReadOnly , isFullCapacity bool ) bool {
2013-07-16 12:34:43 +08:00
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
2020-03-14 06:51:38 +08:00
vInfo , err := dn . GetVolumesById ( vid )
2020-03-14 06:41:24 +08:00
if err != nil {
return false
}
2014-03-19 19:48:13 +08:00
vl . vid2location [ vid ] . Set ( dn )
2020-03-14 06:41:24 +08:00
2023-06-06 01:17:21 +08:00
if vInfo . ReadOnly || isReadOnly || isFullCapacity {
2020-03-14 06:41:24 +08:00
return false
}
2020-04-02 03:18:40 +08:00
if vl . enoughCopies ( vid ) {
2014-03-19 19:48:13 +08:00
return vl . setVolumeWritable ( vid )
2012-09-20 07:48:04 +08:00
}
return false
}
2012-09-19 16:45:30 +08:00
2020-04-02 03:18:40 +08:00
func ( vl * VolumeLayout ) enoughCopies ( vid needle . VolumeId ) bool {
locations := vl . vid2location [ vid ] . Length ( )
desired := vl . rp . GetCopyCount ( )
return locations == desired || ( vl . replicationAsMin && locations > desired )
}
2019-04-19 12:43:36 +08:00
func ( vl * VolumeLayout ) SetVolumeCapacityFull ( vid needle . VolumeId ) bool {
2013-07-16 12:34:43 +08:00
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
2022-04-18 13:55:26 +08:00
wasWritable := vl . removeFromWritable ( vid )
if wasWritable {
glog . V ( 0 ) . Infof ( "Volume %d reaches full capacity." , vid )
}
return wasWritable
2012-09-19 16:45:30 +08:00
}
2021-05-06 18:46:14 +08:00
func ( vl * VolumeLayout ) removeFromCrowded ( vid needle . VolumeId ) {
delete ( vl . crowded , vid )
}
func ( vl * VolumeLayout ) setVolumeCrowded ( vid needle . VolumeId ) {
if _ , ok := vl . crowded [ vid ] ; ! ok {
2021-05-12 01:05:31 +08:00
vl . crowded [ vid ] = struct { } { }
2021-05-06 18:46:14 +08:00
glog . V ( 0 ) . Infoln ( "Volume" , vid , "becomes crowded" )
}
}
func ( vl * VolumeLayout ) SetVolumeCrowded ( vid needle . VolumeId ) {
// since delete is guarded by accessLock.Lock(),
// and is always called in sequential order,
// RLock() should be safe enough
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
for _ , v := range vl . writables {
if v == vid {
vl . setVolumeCrowded ( vid )
break
}
}
}
2022-07-22 17:18:33 +08:00
type VolumeLayoutInfo struct {
Replication string ` json:"replication" `
TTL string ` json:"ttl" `
Writables [ ] needle . VolumeId ` json:"writables" `
Collection string ` json:"collection" `
2023-10-13 12:39:11 +08:00
DiskType string ` json:"diskType" `
2022-07-22 17:18:33 +08:00
}
func ( vl * VolumeLayout ) ToInfo ( ) ( info VolumeLayoutInfo ) {
info . Replication = vl . rp . String ( )
info . TTL = vl . ttl . String ( )
info . Writables = vl . writables
2023-10-13 12:39:11 +08:00
info . DiskType = vl . diskType . ReadableString ( )
2012-09-17 08:31:15 +08:00
//m["locations"] = vl.vid2location
2022-07-22 17:18:33 +08:00
return
2012-09-10 15:18:07 +08:00
}
2018-11-23 16:24:51 +08:00
2024-07-16 23:03:40 +08:00
func ( vl * VolumeLayout ) ToGrowOption ( ) ( option * VolumeGrowOption ) {
2024-07-22 12:01:29 +08:00
option = & VolumeGrowOption { }
2024-07-16 23:03:40 +08:00
option . ReplicaPlacement = vl . rp
option . Ttl = vl . ttl
option . DiskType = vl . diskType
return
}
2018-11-23 16:24:51 +08:00
func ( vl * VolumeLayout ) Stats ( ) * VolumeLayoutStats {
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
ret := & VolumeLayoutStats { }
freshThreshold := time . Now ( ) . Unix ( ) - 60
for vid , vll := range vl . vid2location {
size , fileCount := vll . Stats ( vid , freshThreshold )
ret . FileCount += uint64 ( fileCount )
2022-06-27 03:14:34 +08:00
ret . UsedSize += size * uint64 ( vll . Length ( ) )
2020-09-22 21:31:14 +08:00
if vl . readonlyVolumes . IsTrue ( vid ) {
2022-06-27 03:14:34 +08:00
ret . TotalSize += size * uint64 ( vll . Length ( ) )
2018-11-23 16:24:51 +08:00
} else {
2021-02-26 20:58:40 +08:00
ret . TotalSize += vl . volumeSizeLimit * uint64 ( vll . Length ( ) )
2018-11-23 16:24:51 +08:00
}
}
return ret
}