@ -26,7 +26,7 @@ import (
"github.com/minio/minio/cmd/logger"
)
const defaultMonitorNewDiskInterval = time . Minute * 3
const defaultMonitorNewDiskInterval = time . Second * 10
func initAutoHeal ( ctx context . Context , objAPI ObjectLayer ) {
z , ok := objAPI . ( * erasureZones )
@ -36,15 +36,6 @@ func initAutoHeal(ctx context.Context, objAPI ObjectLayer) {
initBackgroundHealing ( ctx , objAPI ) // start quick background healing
localDisksInZoneHeal := getLocalDisksToHeal ( objAPI )
globalBackgroundHealState . updateHealLocalDisks ( localDisksInZoneHeal )
drivesToHeal := getDrivesToHealCount ( localDisksInZoneHeal )
if drivesToHeal != 0 {
logger . Info ( fmt . Sprintf ( "Found drives to heal %d, waiting until %s to heal the content..." ,
drivesToHeal , defaultMonitorNewDiskInterval ) )
}
var bgSeq * healSequence
var found bool
@ -56,7 +47,14 @@ func initAutoHeal(ctx context.Context, objAPI ObjectLayer) {
time . Sleep ( time . Second )
}
if drivesToHeal != 0 {
for _ , ep := range getLocalDisksToHeal ( ) {
globalBackgroundHealState . pushHealLocalDisks ( ep )
}
if drivesToHeal := globalBackgroundHealState . healDriveCount ( ) ; drivesToHeal > 0 {
logger . Info ( fmt . Sprintf ( "Found drives to heal %d, waiting until %s to heal the content..." ,
drivesToHeal , defaultMonitorNewDiskInterval ) )
// Heal any disk format and metadata early, if possible.
if err := bgSeq . healDiskMeta ( ) ; err != nil {
if newObjectLayerFn ( ) != nil {
@ -67,19 +65,11 @@ func initAutoHeal(ctx context.Context, objAPI ObjectLayer) {
}
}
go monitorLocalDisksAndHeal ( ctx , z , drivesToHeal , localDisksInZoneHeal , bgSeq )
go monitorLocalDisksAndHeal ( ctx , z , bgSeq )
}
func getLocalDisksToHeal ( objAPI ObjectLayer ) [ ] Endpoints {
z , ok := objAPI . ( * erasureZones )
if ! ok {
return nil
}
// Attempt a heal as the server starts-up first.
localDisksInZoneHeal := make ( [ ] Endpoints , len ( z . zones ) )
for i , ep := range globalEndpoints {
localDisksToHeal := Endpoints { }
func getLocalDisksToHeal ( ) ( disksToHeal Endpoints ) {
for _ , ep := range globalEndpoints {
for _ , endpoint := range ep . Endpoints {
if ! endpoint . IsLocal {
continue
@ -88,28 +78,14 @@ func getLocalDisksToHeal(objAPI ObjectLayer) []Endpoints {
// and reformat if the current disk is not formatted
_ , _ , err := connectEndpoint ( endpoint )
if errors . Is ( err , errUnformattedDisk ) {
localD isksToHeal = append ( localD isksToHeal, endpoint )
d isksToHeal = append ( d isksToHeal, endpoint )
}
}
if len ( localDisksToHeal ) == 0 {
continue
}
localDisksInZoneHeal [ i ] = localDisksToHeal
}
return localDisksInZone Heal
return disksToHeal
}
func getDrivesToHealCount ( localDisksInZoneHeal [ ] Endpoints ) int {
var drivesToHeal int
for _ , eps := range localDisksInZoneHeal {
for range eps {
drivesToHeal ++
}
}
return drivesToHeal
}
func initBackgroundHealing ( ctx context . Context , objAPI ObjectLayer ) {
// Run the background healer
globalBackgroundHealRoutine = newHealRoutine ( )
@ -121,77 +97,65 @@ func initBackgroundHealing(ctx context.Context, objAPI ObjectLayer) {
// monitorLocalDisksAndHeal - ensures that detected new disks are healed
// 1. Only the concerned erasure set will be listed and healed
// 2. Only the node hosting the disk is responsible to perform the heal
func monitorLocalDisksAndHeal ( ctx context . Context , z * erasureZones , drivesToHeal int , localDisksInZoneHeal [ ] Endpoints , bgSeq * healSequence ) {
func monitorLocalDisksAndHeal ( ctx context . Context , z * erasureZones , bgSeq * healSequence ) {
// Perform automatic disk healing when a disk is replaced locally.
for {
select {
case <- ctx . Done ( ) :
return
case <- time . After ( defaultMonitorNewDiskInterval ) :
// heal only if new disks found.
if drivesToHeal == 0 {
localDisksInZoneHeal = getLocalDisksToHeal ( z )
drivesToHeal = getDrivesToHealCount ( localDisksInZoneHeal )
if drivesToHeal == 0 {
// No drives to heal.
globalBackgroundHealState . updateHealLocalDisks ( nil )
continue
}
globalBackgroundHealState . updateHealLocalDisks ( localDisksInZoneHeal )
waitForLowHTTPReq ( int32 ( globalEndpoints . NEndpoints ( ) ) , time . Second )
var erasureSetInZoneEndpointToHeal = make ( [ ] map [ int ] Endpoint , len ( z . zones ) )
for i := range z . zones {
erasureSetInZoneEndpointToHeal [ i ] = map [ int ] Endpoint { }
}
healDisks := globalBackgroundHealState . getHealLocalDisks ( )
// heal only if new disks found.
for _ , endpoint := range healDisks {
logger . Info ( fmt . Sprintf ( "Found drives to heal %d, proceeding to heal content..." ,
drivesToHeal ) )
len ( healDisks ) ) )
// Reformat disks
bgSeq . sourceCh <- healSource { bucket : SlashSeparator }
// Ensure that reformatting disks is finished
bgSeq . sourceCh <- healSource { bucket : nopHeal }
}
var erasureSetInZoneToHeal = make ( [ ] [ ] int , len ( localDisksInZoneHeal ) )
// Compute the list of erasure set to heal
for i , localDisksToHeal := range localDisksInZoneHeal {
var erasureSetToHeal [ ] int
for _ , endpoint := range localDisksToHeal {
// Load the new format of this passed endpoint
_ , format , err := connectEndpoint ( endpoint )
if err != nil {
printEndpointError ( endpoint , err , true )
continue
}
// Calculate the set index where the current endpoint belongs
setIndex , _ , err := findDiskIndex ( z . zones [ i ] . format , format )
if err != nil {
printEndpointError ( endpoint , err , false )
continue
}
// Load the new format of this passed endpoint
_ , format , err := connectEndpoint ( endpoint )
if err != nil {
printEndpointError ( endpoint , err , true )
continue
}
erasureSetToHeal = append ( erasureSetToHeal , setIndex )
zoneIdx := globalEndpoints . GetLocalZoneIdx ( endpoint )
if zoneIdx < 0 {
continue
}
erasureSetInZoneToHeal [ i ] = erasureSetToHeal
}
logger . Info ( "New unformatted drives detected attempting to heal the content..." )
for i , disks := range localDisksInZoneHeal {
for _ , disk := range disks {
logger . Info ( "Healing disk '%s' on %s zone" , disk , humanize . Ordinal ( i + 1 ) )
// Calculate the set index where the current endpoint belongs
setIndex , _ , err := findDiskIndex ( z . zones [ zoneIdx ] . format , format )
if err != nil {
printEndpointError ( endpoint , err , false )
continue
}
erasureSetInZoneEndpointToHeal [ zoneIdx ] [ setIndex ] = endpoint
}
// Heal all erasure sets that need
for i , erasureSetToHeal := range erasureSetInZoneToHeal {
for _ , setIndex := range erasureSetToHeal {
err := healErasureSet ( ctx , setIndex , z . zones [ i ] . sets [ setIndex ] , z . zones [ i ] . setDriveCount )
if err != nil {
for i , setMap := range erasureSetInZoneEndpointToHeal {
for setIndex , endpoint := range setMap {
logger . Info ( "Healing disk '%s' on %s zone" , endpoint , humanize . Ordinal ( i + 1 ) )
if err := healErasureSet ( ctx , setIndex , z . zones [ i ] . sets [ setIndex ] , z . zones [ i ] . setDriveCount ) ; err != nil {
logger . LogIf ( ctx , err )
continue
}
// Only upon success reduce the counter
if err == nil {
drivesToHeal --
}
// Only upon success pop the healed disk.
globalBackgroundHealState . popHealLocalDisks ( endpoint )
}
}
}