| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:53:08 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v3" | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 	mrfOpsQueueSize = 100000 | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // partialOperation is a successful upload/delete of an object
 | 
					
						
							|  |  |  | // but not written in all disks (having quorum)
 | 
					
						
							|  |  |  | type partialOperation struct { | 
					
						
							| 
									
										
										
										
											2023-06-25 10:31:04 +08:00
										 |  |  | 	bucket              string | 
					
						
							|  |  |  | 	object              string | 
					
						
							|  |  |  | 	versionID           string | 
					
						
							|  |  |  | 	allVersions         bool | 
					
						
							|  |  |  | 	setIndex, poolIndex int | 
					
						
							|  |  |  | 	queued              time.Time | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // mrfState sncapsulates all the information
 | 
					
						
							|  |  |  | // related to the global background MRF.
 | 
					
						
							|  |  |  | type mrfState struct { | 
					
						
							| 
									
										
										
										
											2023-06-25 10:31:04 +08:00
										 |  |  | 	ctx   context.Context | 
					
						
							|  |  |  | 	pools *erasureServerPools | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 	mu   sync.Mutex | 
					
						
							|  |  |  | 	opCh chan partialOperation | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 23:00:59 +08:00
										 |  |  | // Initialize healing MRF subsystem
 | 
					
						
							|  |  |  | func (m *mrfState) init(ctx context.Context, objAPI ObjectLayer) { | 
					
						
							|  |  |  | 	m.mu.Lock() | 
					
						
							|  |  |  | 	defer m.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m.ctx = ctx | 
					
						
							|  |  |  | 	m.opCh = make(chan partialOperation, mrfOpsQueueSize) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-25 10:31:04 +08:00
										 |  |  | 	var ok bool | 
					
						
							|  |  |  | 	m.pools, ok = objAPI.(*erasureServerPools) | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		go m.healRoutine() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-26 23:00:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | // Add a partial S3 operation (put/delete) when one or more disks are offline.
 | 
					
						
							|  |  |  | func (m *mrfState) addPartialOp(op partialOperation) { | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 	if m == nil { | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case m.opCh <- op: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | var healSleeper = newDynamicSleeper(5, time.Second, false) | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // healRoutine listens to new disks reconnection events and
 | 
					
						
							|  |  |  | // issues healing requests for queued objects belonging to the
 | 
					
						
							|  |  |  | // corresponding erasure set
 | 
					
						
							|  |  |  | func (m *mrfState) healRoutine() { | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-m.ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 		case u, ok := <-m.opCh: | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 			now := time.Now() | 
					
						
							|  |  |  | 			if now.Sub(u.queued) < time.Second { | 
					
						
							|  |  |  | 				// let recently failed networks to reconnect
 | 
					
						
							|  |  |  | 				// making MRF wait for 1s before retrying,
 | 
					
						
							|  |  |  | 				// i.e 4 reconnect attempts.
 | 
					
						
							|  |  |  | 				time.Sleep(1 * time.Second) | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 			// wait on timer per heal
 | 
					
						
							|  |  |  | 			wait := healSleeper.Timer(context.Background()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if u.object == "" { | 
					
						
							|  |  |  | 				healBucket(u.bucket, madmin.HealNormalScan) | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2023-06-25 10:31:04 +08:00
										 |  |  | 				if u.allVersions { | 
					
						
							|  |  |  | 					m.pools.serverPools[u.poolIndex].sets[u.setIndex].listAndHeal(u.bucket, u.object, healObjectVersionsDisparity) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					healObject(u.bucket, u.object, u.versionID, madmin.HealNormalScan) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-26 08:46:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 			wait() | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Initialize healing MRF
 | 
					
						
							|  |  |  | func initHealMRF(ctx context.Context, obj ObjectLayer) { | 
					
						
							| 
									
										
										
										
											2021-07-26 23:00:59 +08:00
										 |  |  | 	globalMRFState.init(ctx, obj) | 
					
						
							| 
									
										
										
										
											2021-07-16 13:32:06 +08:00
										 |  |  | } |