@ -1,7 +1,6 @@
package topology
import (
"errors"
"fmt"
"math/rand"
"sync"
@ -13,14 +12,12 @@ import (
// mapping from volume to its locations, inverted from server to volume
type VolumeLayout struct {
rp * storage . ReplicaPlacement
ttl * storage . TTL
vid2location map [ storage . VolumeId ] * VolumeLocationList
writables [ ] storage . VolumeId // transient array of writable volume id
readonlyVolumes map [ storage . VolumeId ] bool // transient set of readonly volumes
oversizedVolumes map [ storage . VolumeId ] bool // set of oversized volumes
volumeSizeLimit uint64
accessLock sync . RWMutex
rp * storage . ReplicaPlacement
ttl * storage . TTL
vid2location map [ storage . VolumeId ] * VolumeLocationList
volumeSizeLimit uint64
accessLock sync . RWMutex
}
type VolumeLayoutStats struct {
@ -31,18 +28,15 @@ type VolumeLayoutStats struct {
func NewVolumeLayout ( rp * storage . ReplicaPlacement , ttl * storage . TTL , volumeSizeLimit uint64 ) * VolumeLayout {
return & VolumeLayout {
rp : rp ,
ttl : ttl ,
vid2location : make ( map [ storage . VolumeId ] * VolumeLocationList ) ,
writables : * new ( [ ] storage . VolumeId ) ,
readonlyVolumes : make ( map [ storage . VolumeId ] bool ) ,
oversizedVolumes : make ( map [ storage . VolumeId ] bool ) ,
volumeSizeLimit : volumeSizeLimit ,
rp : rp ,
ttl : ttl ,
vid2location : make ( map [ storage . VolumeId ] * VolumeLocationList ) ,
volumeSizeLimit : volumeSizeLimit ,
}
}
func ( vl * VolumeLayout ) String ( ) string {
return fmt . Sprintf ( "rp:%v, ttl:%v, vid2location:%v, writables:%v, volumeSizeLimit:%v" , vl . rp , vl . ttl , vl . vid2location , vl . writables , vl . volumeSizeLimit )
return fmt . Sprintf ( "rp:%v, ttl:%v, vid2location:%v, volumeSizeLimit:%v" , vl . rp , vl . ttl , vl . vid2location , vl . volumeSizeLimit )
}
func ( vl * VolumeLayout ) RegisterVolume ( v * storage . VolumeInfo , dn * DataNode ) {
@ -58,60 +52,28 @@ func (vl *VolumeLayout) RegisterVolume(v *storage.VolumeInfo, dn *DataNode) {
if vInfo , err := dn . GetVolumesById ( v . Id ) ; err == nil {
if vInfo . IsReadOnly ( ) {
glog . V ( 3 ) . Infof ( "vid %d removed from writable" , v . Id )
vl . removeFromWritable ( v . Id )
vl . readonlyVolumes [ v . Id ] = true
return
} else {
delete ( vl . readonlyVolumes , v . Id )
}
} else {
glog . V ( 3 ) . Infof ( "vid %d removed from writable" , v . Id )
vl . removeFromWritable ( v . Id )
delete ( vl . readonlyVolumes , v . Id )
return
}
}
if vl . vid2location [ v . Id ] . Length ( ) == vl . rp . GetCopyCount ( ) && vl . isWritable ( v ) {
if _ , ok := vl . oversizedVolumes [ v . Id ] ; ! ok {
vl . addToWritable ( v . Id )
}
} else {
vl . rememberOversizedVolumne ( v )
vl . removeFromWritable ( v . Id )
}
}
func ( vl * VolumeLayout ) rememberOversizedVolumne ( v * storage . VolumeInfo ) {
if vl . isOversized ( v ) {
vl . oversizedVolumes [ v . Id ] = true
}
}
func ( vl * VolumeLayout ) UnRegisterVolume ( v * storage . VolumeInfo , dn * DataNode ) {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
vl . removeFromWritable ( v . Id )
delete ( vl . vid2location , v . Id )
}
func ( vl * VolumeLayout ) addToWritable ( vid storage . VolumeId ) {
for _ , id := range vl . writables {
if vid == id {
return
}
}
vl . writables = append ( vl . writables , vid )
}
func ( vl * VolumeLayout ) isOversized ( v * storage . VolumeInfo ) bool {
func ( vl * VolumeLayout ) isOverSized ( v * storage . VolumeInfo ) bool {
return uint64 ( v . Size ) >= vl . volumeSizeLimit
}
func ( vl * VolumeLayout ) isWritable ( v * storage . VolumeInfo ) bool {
return ! vl . isOversized ( v ) &&
v . Version == storage . CurrentVersion &&
! v . IsReadOnly ( )
return ! vl . isOverSized ( v ) && v . Version == storage . CurrentVersion && ! v . IsReadOnly ( )
}
func ( vl * VolumeLayout ) isEmpty ( ) bool {
@ -145,127 +107,76 @@ func (vl *VolumeLayout) PickForWrite(count uint64, option *VolumeGrowOption) (*s
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
lenWriters := len ( vl . writables )
if lenWriters <= 0 {
glog . V ( 0 ) . Infoln ( "No more writable volumes!" )
return nil , 0 , nil , errors . New ( "No more writable volumes!" )
}
if option . DataCenter == "" {
vid := vl . writables [ rand . Intn ( lenWriters ) ]
locationList := vl . vid2location [ vid ]
if locationList != nil {
return & vid , count , locationList , nil
}
return nil , 0 , nil , errors . New ( "Strangely vid " + vid . String ( ) + " is on no machine!" )
}
var vid storage . VolumeId
var locationList * VolumeLocationList
counter := 0
for _ , v := range vl . writables {
volumeLocationList := vl . vid2location [ v ]
for _ , dn := range volumeLocationList . list {
if option . MatchesDataCenter ( dn ) {
if ! option . MatchesRackDataNode ( dn ) {
continue
}
counter ++
if rand . Intn ( counter ) < 1 {
vid , locationList = v , volumeLocationList
}
NextVolume :
for v , vll := range vl . vid2location {
if option . ReplicaPlacement . GetCopyCount ( ) != vll . Length ( ) {
continue
}
for _ , dn := range vll . list {
if ! option . MatchesDataCenter ( dn ) || ! option . MatchesRackDataNode ( dn ) {
continue NextVolume
}
vi , _ := dn . GetVolumesById ( v )
if ! vl . isWritable ( & vi ) {
continue NextVolume
}
}
counter ++
if rand . Intn ( counter ) < 1 {
vid , locationList = v , vll
}
}
return & vid , count , locationList , nil
}
func ( vl * VolumeLayout ) GetActiveVolumeCount ( option * VolumeGrowOption ) int {
func ( vl * VolumeLayout ) GetWritabl eVolumeCount ( option * VolumeGrowOption ) int {
vl . accessLock . RLock ( )
defer vl . accessLock . RUnlock ( )
if option . DataCenter == "" {
return len ( vl . writables )
}
counter := 0
for _ , v := range vl . writables {
for _ , dn := range vl . vid2location [ v ] . list {
if option . MatchesDataCenter ( dn ) {
if ! option . MatchesRackDataNode ( dn ) {
continue
}
counter ++
}
}
}
return counter
}
func ( vl * VolumeLayout ) removeFromWritable ( vid storage . VolumeId ) bool {
toDeleteIndex := - 1
for k , id := range vl . writables {
if id == vid {
toDeleteIndex = k
break
}
}
if toDeleteIndex >= 0 {
glog . V ( 0 ) . Infoln ( "Volume" , vid , "becomes unwritable" )
vl . writables = append ( vl . writables [ 0 : toDeleteIndex ] , vl . writables [ toDeleteIndex + 1 : ] ... )
return true
}
return false
}
func ( vl * VolumeLayout ) setVolumeWritable ( vid storage . VolumeId ) bool {
for _ , v := range vl . writables {
if v == vid {
return false
NextVolume :
for vid , vll := range vl . vid2location {
if option . ReplicaPlacement . GetCopyCount ( ) != vll . Length ( ) {
continue
}
}
glog . V ( 0 ) . Infoln ( "Volume" , vid , "becomes writable" )
vl . writables = append ( vl . writables , vid )
return true
}
func ( vl * VolumeLayout ) SetVolumeUnavailable ( dn * DataNode , vid storage . VolumeId ) bool {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
for _ , dn := range vll . list {
if ! option . MatchesDataCenter ( dn ) || ! option . MatchesRackDataNode ( dn ) {
continue NextVolume
}
if location , ok := vl . vid2location [ vid ] ; ok {
if location . Remove ( dn ) {
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 )
vi , _ := dn . GetVolumesById ( vid )
if ! vl . isWritable ( & vi ) {
continue NextVolume
}
}
counter ++
}
return false
return counter
}
func ( vl * VolumeLayout ) SetVolumeAvailable ( dn * DataNode , vid storage . VolumeId ) bool {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
vl . vid2location [ vid ] . Set ( dn )
if vl . vid2location [ vid ] . Length ( ) >= vl . rp . GetCopyCount ( ) {
return vl . setVolumeWritable ( vid )
}
return false
}
func ( vl * VolumeLayout ) SetVolumeCapacityFull ( vid storage . VolumeId ) bool {
vl . accessLock . Lock ( )
defer vl . accessLock . Unlock ( )
// glog.V(0).Infoln("Volume", vid, "reaches full capacity.")
return vl . removeFromWritable ( vid )
}
func ( vl * VolumeLayout ) ToMap ( ) map [ string ] interface { } {
m := make ( map [ string ] interface { } )
m [ "replication" ] = vl . rp . String ( )
m [ "ttl" ] = vl . ttl . String ( )
m [ "writables" ] = vl . writables
//m["locations"] = vl.vid2location
return m
}
@ -281,7 +192,17 @@ func (vl *VolumeLayout) Stats() *VolumeLayoutStats {
size , fileCount := vll . Stats ( vid , freshThreshold )
ret . FileCount += uint64 ( fileCount )
ret . UsedSize += size
if vl . readonlyVolumes [ vid ] {
writable := true
for _ , dn := range vll . list {
vi , _ := dn . GetVolumesById ( vid )
if ! vl . isWritable ( & vi ) {
writable = false
break
}
}
if writable {
ret . TotalSize += size
} else {
ret . TotalSize += vl . volumeSizeLimit