| 
									
										
										
										
											2021-04-19 03:41:13 +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/>.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2022-01-27 00:34:56 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-11-09 02:25:34 +08:00
										 |  |  | 	"io/fs" | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	"math" | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 08:53:08 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v3" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/bucket/lifecycle" | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 	"github.com/minio/minio/internal/bucket/object/lock" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/bucket/replication" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/color" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/config/heal" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/event" | 
					
						
							| 
									
										
										
										
											2024-01-29 02:04:17 +08:00
										 |  |  | 	xioutil "github.com/minio/minio/internal/ioutil" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  | 	"github.com/minio/pkg/v2/console" | 
					
						
							| 
									
										
										
										
											2022-06-07 07:14:52 +08:00
										 |  |  | 	uatomic "go.uber.org/atomic" | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2022-07-11 22:59:03 +08:00
										 |  |  | 	dataScannerSleepPerFolder        = time.Millisecond                 // Time to wait between folders.
 | 
					
						
							|  |  |  | 	dataUsageUpdateDirCycles         = 16                               // Visit all folders every n cycles.
 | 
					
						
							|  |  |  | 	dataScannerCompactLeastObject    = 500                              // Compact when there is less than this many objects in a branch.
 | 
					
						
							|  |  |  | 	dataScannerCompactAtChildren     = 10000                            // Compact when there are this many children in a branch.
 | 
					
						
							|  |  |  | 	dataScannerCompactAtFolders      = dataScannerCompactAtChildren / 4 // Compact when this many subfolders in a single folder.
 | 
					
						
							|  |  |  | 	dataScannerForceCompactAtFolders = 1_000_000                        // Compact when this many subfolders in a single folder (even top level).
 | 
					
						
							|  |  |  | 	dataScannerStartDelay            = 1 * time.Minute                  // Time to wait on startup and between cycles.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-20 00:24:22 +08:00
										 |  |  | 	healDeleteDangling   = true | 
					
						
							|  |  |  | 	healObjectSelectProb = 1024 // Overall probability of a file being scanned; one in n.
 | 
					
						
							| 
									
										
										
										
											2020-09-12 15:08:12 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 	globalHealConfig heal.Config | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Sleeper values are updated when config is loaded.
 | 
					
						
							| 
									
										
										
										
											2024-02-12 15:41:53 +08:00
										 |  |  | 	scannerSleeper              = newDynamicSleeper(2, time.Second, true) // Keep defaults same as config defaults
 | 
					
						
							|  |  |  | 	scannerCycle                = uatomic.NewDuration(dataScannerStartDelay) | 
					
						
							|  |  |  | 	scannerIdleMode             = uatomic.NewInt32(0) // default is throttled when idle
 | 
					
						
							|  |  |  | 	scannerExcessObjectVersions = uatomic.NewInt64(100) | 
					
						
							|  |  |  | 	scannerExcessFolders        = uatomic.NewInt64(50000) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-18 04:04:11 +08:00
										 |  |  | // initDataScanner will start the scanner in the background.
 | 
					
						
							|  |  |  | func initDataScanner(ctx context.Context, objAPI ObjectLayer) { | 
					
						
							| 
									
										
										
										
											2021-12-17 00:32:15 +08:00
										 |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:34 +08:00
										 |  |  | 		r := rand.New(rand.NewSource(time.Now().UnixNano())) | 
					
						
							| 
									
										
										
										
											2021-12-17 00:32:15 +08:00
										 |  |  | 		// Run the data scanner in a loop
 | 
					
						
							|  |  |  | 		for { | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:34 +08:00
										 |  |  | 			runDataScanner(ctx, objAPI) | 
					
						
							| 
									
										
										
										
											2022-06-07 07:14:52 +08:00
										 |  |  | 			duration := time.Duration(r.Float64() * float64(scannerCycle.Load())) | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:34 +08:00
										 |  |  | 			if duration < time.Second { | 
					
						
							| 
									
										
										
										
											2024-01-18 15:03:17 +08:00
										 |  |  | 				// Make sure to sleep at least a second to avoid high CPU ticks.
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:34 +08:00
										 |  |  | 				duration = time.Second | 
					
						
							| 
									
										
										
										
											2021-12-17 00:32:15 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:34 +08:00
										 |  |  | 			time.Sleep(duration) | 
					
						
							| 
									
										
										
										
											2021-12-17 00:32:15 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | func getCycleScanMode(currentCycle, bitrotStartCycle uint64, bitrotStartTime time.Time) madmin.HealScanMode { | 
					
						
							|  |  |  | 	bitrotCycle := globalHealConfig.BitrotScanCycle() | 
					
						
							|  |  |  | 	switch bitrotCycle { | 
					
						
							|  |  |  | 	case -1: | 
					
						
							|  |  |  | 		return madmin.HealNormalScan | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		return madmin.HealDeepScan | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if currentCycle-bitrotStartCycle < healObjectSelectProb { | 
					
						
							|  |  |  | 		return madmin.HealDeepScan | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if time.Since(bitrotStartTime) > bitrotCycle { | 
					
						
							|  |  |  | 		return madmin.HealDeepScan | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return madmin.HealNormalScan | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type backgroundHealInfo struct { | 
					
						
							|  |  |  | 	BitrotStartTime  time.Time           `json:"bitrotStartTime"` | 
					
						
							|  |  |  | 	BitrotStartCycle uint64              `json:"bitrotStartCycle"` | 
					
						
							|  |  |  | 	CurrentScanMode  madmin.HealScanMode `json:"currentScanMode"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func readBackgroundHealInfo(ctx context.Context, objAPI ObjectLayer) backgroundHealInfo { | 
					
						
							| 
									
										
										
										
											2022-10-31 22:26:10 +08:00
										 |  |  | 	if globalIsErasureSD { | 
					
						
							|  |  |  | 		return backgroundHealInfo{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	// Get last healing information
 | 
					
						
							|  |  |  | 	buf, err := readConfig(ctx, objAPI, backgroundHealInfoPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if !errors.Is(err, errConfigNotFound) { | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 			logger.LogOnceIf(ctx, err, backgroundHealInfoPath) | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return backgroundHealInfo{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var info backgroundHealInfo | 
					
						
							| 
									
										
										
										
											2022-10-31 22:26:10 +08:00
										 |  |  | 	if err = json.Unmarshal(buf, &info); err != nil { | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 		logger.LogOnceIf(ctx, err, backgroundHealInfoPath) | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return info | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func saveBackgroundHealInfo(ctx context.Context, objAPI ObjectLayer, info backgroundHealInfo) { | 
					
						
							| 
									
										
										
										
											2022-10-31 22:26:10 +08:00
										 |  |  | 	if globalIsErasureSD { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	b, err := json.Marshal(info) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Get last healing information
 | 
					
						
							|  |  |  | 	err = saveConfig(ctx, objAPI, backgroundHealInfoPath, b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-18 04:04:11 +08:00
										 |  |  | // runDataScanner will start a data scanner.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | // The function will block until the context is canceled.
 | 
					
						
							| 
									
										
										
										
											2021-02-18 04:04:11 +08:00
										 |  |  | // There should only ever be one scanner running per cluster.
 | 
					
						
							| 
									
										
										
										
											2022-11-09 00:55:55 +08:00
										 |  |  | func runDataScanner(ctx context.Context, objAPI ObjectLayer) { | 
					
						
							|  |  |  | 	ctx, cancel := globalLeaderLock.GetLock(ctx) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							| 
									
										
										
										
											2020-09-19 02:15:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	// Load current bloom cycle
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	var cycleInfo currentScannerCycle | 
					
						
							| 
									
										
										
										
											2021-02-06 01:57:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-04 02:22:58 +08:00
										 |  |  | 	buf, _ := readConfig(ctx, objAPI, dataUsageBloomNamePath) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	if len(buf) == 8 { | 
					
						
							|  |  |  | 		cycleInfo.next = binary.LittleEndian.Uint64(buf) | 
					
						
							|  |  |  | 	} else if len(buf) > 8 { | 
					
						
							|  |  |  | 		cycleInfo.next = binary.LittleEndian.Uint64(buf[:8]) | 
					
						
							|  |  |  | 		buf = buf[8:] | 
					
						
							| 
									
										
										
										
											2022-11-09 00:55:55 +08:00
										 |  |  | 		_, err := cycleInfo.UnmarshalMsg(buf) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 07:14:52 +08:00
										 |  |  | 	scannerTimer := time.NewTimer(scannerCycle.Load()) | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 	defer scannerTimer.Stop() | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	defer globalScannerMetrics.setCycle(nil) | 
					
						
							| 
									
										
										
										
											2020-12-18 04:35:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 		case <-scannerTimer.C: | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			// Reset the timer for next cycle.
 | 
					
						
							|  |  |  | 			// If scanner takes longer we start at once.
 | 
					
						
							|  |  |  | 			scannerTimer.Reset(scannerCycle.Load()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			stopFn := globalScannerMetrics.log(scannerMetricScanCycle) | 
					
						
							|  |  |  | 			cycleInfo.current = cycleInfo.next | 
					
						
							|  |  |  | 			cycleInfo.started = time.Now() | 
					
						
							|  |  |  | 			globalScannerMetrics.setCycle(&cycleInfo) | 
					
						
							| 
									
										
										
										
											2020-12-18 08:52:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 			bgHealInfo := readBackgroundHealInfo(ctx, objAPI) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			scanMode := getCycleScanMode(cycleInfo.current, bgHealInfo.BitrotStartCycle, bgHealInfo.BitrotStartTime) | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 			if bgHealInfo.CurrentScanMode != scanMode { | 
					
						
							|  |  |  | 				newHealInfo := bgHealInfo | 
					
						
							|  |  |  | 				newHealInfo.CurrentScanMode = scanMode | 
					
						
							|  |  |  | 				if scanMode == madmin.HealDeepScan { | 
					
						
							|  |  |  | 					newHealInfo.BitrotStartTime = time.Now().UTC() | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 					newHealInfo.BitrotStartCycle = cycleInfo.current | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				saveBackgroundHealInfo(ctx, objAPI, newHealInfo) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			// Wait before starting next cycle and wait on startup.
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 			results := make(chan DataUsageInfo, 1) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			go storeDataUsageInBackend(ctx, objAPI, results) | 
					
						
							| 
									
										
										
										
											2023-02-24 11:33:31 +08:00
										 |  |  | 			err := objAPI.NSScanner(ctx, results, uint32(cycleInfo.current), scanMode) | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 			logger.LogOnceIf(ctx, err, "ns-scanner") | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:58 +08:00
										 |  |  | 			res := map[string]string{"cycle": strconv.FormatUint(cycleInfo.current, 10)} | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				res["error"] = err.Error() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			stopFn(res) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				// Store new cycle...
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 				cycleInfo.next++ | 
					
						
							|  |  |  | 				cycleInfo.current = 0 | 
					
						
							|  |  |  | 				cycleInfo.cycleCompleted = append(cycleInfo.cycleCompleted, time.Now()) | 
					
						
							|  |  |  | 				if len(cycleInfo.cycleCompleted) > dataUsageUpdateDirCycles { | 
					
						
							|  |  |  | 					cycleInfo.cycleCompleted = cycleInfo.cycleCompleted[len(cycleInfo.cycleCompleted)-dataUsageUpdateDirCycles:] | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 				globalScannerMetrics.setCycle(&cycleInfo) | 
					
						
							|  |  |  | 				tmp := make([]byte, 8, 8+cycleInfo.Msgsize()) | 
					
						
							|  |  |  | 				// Cycle for backward compat.
 | 
					
						
							|  |  |  | 				binary.LittleEndian.PutUint64(tmp, cycleInfo.next) | 
					
						
							|  |  |  | 				tmp, _ = cycleInfo.MarshalMsg(tmp) | 
					
						
							|  |  |  | 				err = saveConfig(ctx, objAPI, dataUsageBloomNamePath, tmp) | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 				logger.LogOnceIf(ctx, err, dataUsageBloomNamePath) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type cachedFolder struct { | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 	name              string | 
					
						
							|  |  |  | 	parent            *dataUsageHash | 
					
						
							|  |  |  | 	objectHealProbDiv uint32 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type folderScanner struct { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	root        string | 
					
						
							|  |  |  | 	getSize     getSizeFn | 
					
						
							|  |  |  | 	oldCache    dataUsageCache | 
					
						
							|  |  |  | 	newCache    dataUsageCache | 
					
						
							|  |  |  | 	updateCache dataUsageCache | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 	dataUsageScannerDebug bool | 
					
						
							|  |  |  | 	healObjectSelect      uint32 // Do a heal check on an object once every n cycles. Must divide into healFolderInclude
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	scanMode              madmin.HealScanMode | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | 	weSleep func() bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-27 00:34:56 +08:00
										 |  |  | 	disks       []StorageAPI | 
					
						
							|  |  |  | 	disksQuorum int | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// If set updates will be sent regularly to this channel.
 | 
					
						
							|  |  |  | 	// Will not be closed when returned.
 | 
					
						
							|  |  |  | 	updates    chan<- dataUsageEntry | 
					
						
							|  |  |  | 	lastUpdate time.Time | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	// updateCurrentPath should be called whenever a new path is scanned.
 | 
					
						
							|  |  |  | 	updateCurrentPath func(string) | 
					
						
							| 
									
										
										
										
											2021-10-03 00:31:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | // Cache structure and compaction:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A cache structure will be kept with a tree of usages.
 | 
					
						
							|  |  |  | // The cache is a tree structure where each keeps track of its children.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // An uncompacted branch contains a count of the files only directly at the
 | 
					
						
							|  |  |  | // branch level, and contains link to children branches or leaves.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The leaves are "compacted" based on a number of properties.
 | 
					
						
							|  |  |  | // A compacted leaf contains the totals of all files beneath it.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A leaf is only scanned once every dataUsageUpdateDirCycles,
 | 
					
						
							|  |  |  | // rarer if the bloom filter for the path is clean and no lifecycles are applied.
 | 
					
						
							|  |  |  | // Skipped leaves have their totals transferred from the previous cycle.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // When selected there is a one in healObjectSelectProb that any object will be chosen for heal scan.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Compaction happens when either:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 1) The folder (and subfolders) contains less than dataScannerCompactLeastObject objects.
 | 
					
						
							|  |  |  | // 2) The folder itself contains more than dataScannerCompactAtFolders folders.
 | 
					
						
							|  |  |  | // 3) The folder only contains objects and no subfolders.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A bucket root will never be compacted.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Furthermore if a has more than dataScannerCompactAtChildren recursive children (uncompacted folders)
 | 
					
						
							|  |  |  | // the tree will be recursively scanned and the branches with the least number of objects will be
 | 
					
						
							|  |  |  | // compacted until the limit is reached.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This ensures that any branch will never contain an unreasonable amount of other branches,
 | 
					
						
							|  |  |  | // and also that small branches with few objects don't take up unreasonable amounts of space.
 | 
					
						
							|  |  |  | // This keeps the cache size at a reasonable size for all buckets.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Whenever a branch is scanned, it is assumed that it will be un-compacted
 | 
					
						
							|  |  |  | // before it hits any of the above limits.
 | 
					
						
							|  |  |  | // This will make the branch rebalance itself when scanned if the distribution of objects has changed.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | // scanDataFolder will scanner the basepath+cache.Info.Name and return an updated cache.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | // The returned cache will always be valid, but may not be updated from the existing.
 | 
					
						
							| 
									
										
										
										
											2021-02-18 04:04:11 +08:00
										 |  |  | // Before each operation sleepDuration is called which can be used to temporarily halt the scanner.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | // If the supplied context is canceled the function will return at the first chance.
 | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | func scanDataFolder(ctx context.Context, disks []StorageAPI, basePath string, cache dataUsageCache, getSize getSizeFn, scanMode madmin.HealScanMode, weSleep func() bool) (dataUsageCache, error) { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	switch cache.Info.Name { | 
					
						
							|  |  |  | 	case "", dataUsageRoot: | 
					
						
							|  |  |  | 		return cache, errors.New("internal error: root scan attempted") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	updatePath, closeDisk := globalScannerMetrics.currentPathUpdater(basePath, cache.Info.Name) | 
					
						
							|  |  |  | 	defer closeDisk() | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	s := folderScanner{ | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 		root:                  basePath, | 
					
						
							|  |  |  | 		getSize:               getSize, | 
					
						
							|  |  |  | 		oldCache:              cache, | 
					
						
							|  |  |  | 		newCache:              dataUsageCache{Info: cache.Info}, | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 		updateCache:           dataUsageCache{Info: cache.Info}, | 
					
						
							| 
									
										
										
										
											2023-02-24 11:33:31 +08:00
										 |  |  | 		dataUsageScannerDebug: false, | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 		healObjectSelect:      0, | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 		scanMode:              scanMode, | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | 		weSleep:               weSleep, | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 		updates:               cache.Info.updates, | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		updateCurrentPath:     updatePath, | 
					
						
							| 
									
										
										
										
											2023-07-18 00:52:05 +08:00
										 |  |  | 		disks:                 disks, | 
					
						
							|  |  |  | 		disksQuorum:           len(disks) / 2, | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 	// Enable healing in XL mode.
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	if globalIsErasure && !cache.Info.SkipHealing { | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 		// Do a heal check on an object once every n cycles. Must divide into healFolderInclude
 | 
					
						
							| 
									
										
										
										
											2020-09-12 15:08:12 +08:00
										 |  |  | 		s.healObjectSelect = healObjectSelectProb | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	done := ctx.Done() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	// Read top level in bucket.
 | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case <-done: | 
					
						
							|  |  |  | 		return cache, ctx.Err() | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	root := dataUsageEntry{} | 
					
						
							|  |  |  | 	folder := cachedFolder{name: cache.Info.Name, objectHealProbDiv: 1} | 
					
						
							|  |  |  | 	err := s.scanFolder(ctx, folder, &root) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// No useful information...
 | 
					
						
							|  |  |  | 		return cache, err | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	s.newCache.Info.LastUpdate = UTCNow() | 
					
						
							| 
									
										
										
										
											2021-08-25 23:25:26 +08:00
										 |  |  | 	s.newCache.Info.NextCycle = cache.Info.NextCycle | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	return s.newCache, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | // sendUpdate() should be called on a regular basis when the newCache contains more recent total than previously.
 | 
					
						
							|  |  |  | // May or may not send an update upstream.
 | 
					
						
							|  |  |  | func (f *folderScanner) sendUpdate() { | 
					
						
							|  |  |  | 	// Send at most an update every minute.
 | 
					
						
							|  |  |  | 	if f.updates == nil || time.Since(f.lastUpdate) < time.Minute { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if flat := f.updateCache.sizeRecursive(f.newCache.Info.Name); flat != nil { | 
					
						
							|  |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2023-06-13 00:17:11 +08:00
										 |  |  | 		case f.updates <- flat.clone(): | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		f.lastUpdate = time.Now() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | // scanFolder will scan the provided folder.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | // Files found in the folders will be added to f.newCache.
 | 
					
						
							|  |  |  | // If final is provided folders will be put into f.newFolders or f.existingFolders.
 | 
					
						
							|  |  |  | // If final is not provided the folders found are returned from the function.
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, into *dataUsageEntry) error { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	done := ctx.Done() | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 	scannerLogPrefix := color.Green("folder-scanner:") | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | 	noWait := func() {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	thisHash := hashPath(folder.name) | 
					
						
							|  |  |  | 	// Store initial compaction state.
 | 
					
						
							|  |  |  | 	wasCompacted := into.Compacted | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-done: | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			return ctx.Err() | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		var abandonedChildren dataUsageHashMap | 
					
						
							|  |  |  | 		if !into.Compacted { | 
					
						
							|  |  |  | 			abandonedChildren = f.oldCache.findChildrenCopy(thisHash) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-15 07:42:35 +08:00
										 |  |  | 		// If there are lifecycle rules for the prefix.
 | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 		_, prefix := path2BucketObjectWithBasePath(f.root, folder.name) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		var activeLifeCycle *lifecycle.Lifecycle | 
					
						
							| 
									
										
										
										
											2022-11-10 23:17:45 +08:00
										 |  |  | 		if f.oldCache.Info.lifeCycle != nil && f.oldCache.Info.lifeCycle.HasActiveRules(prefix) { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 			if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 				console.Debugf(scannerLogPrefix+" Prefix %q has active rules\n", prefix) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 			activeLifeCycle = f.oldCache.Info.lifeCycle | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-07-15 07:42:35 +08:00
										 |  |  | 		// If there are replication rules for the prefix.
 | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  | 		var replicationCfg replicationConfig | 
					
						
							|  |  |  | 		if !f.oldCache.Info.replication.Empty() && f.oldCache.Info.replication.Config.HasActiveRules(prefix, true) { | 
					
						
							|  |  |  | 			replicationCfg = f.oldCache.Info.replication | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if f.weSleep() { | 
					
						
							|  |  |  | 			scannerSleeper.Sleep(ctx, dataScannerSleepPerFolder) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		var existingFolders, newFolders []cachedFolder | 
					
						
							|  |  |  | 		var foundObjects bool | 
					
						
							| 
									
										
										
										
											2023-09-17 10:08:59 +08:00
										 |  |  | 		err := readDirFn(pathJoin(f.root, folder.name), func(entName string, typ os.FileMode) error { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			// Parse
 | 
					
						
							| 
									
										
										
										
											2023-09-17 10:08:59 +08:00
										 |  |  | 			entName = pathClean(pathJoin(folder.name, entName)) | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 			if entName == "" || entName == folder.name { | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 				if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 					console.Debugf(scannerLogPrefix+" no entity (%s,%s)\n", f.root, entName) | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 				return nil | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			bucket, prefix := path2BucketObjectWithBasePath(f.root, entName) | 
					
						
							|  |  |  | 			if bucket == "" { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 				if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 					console.Debugf(scannerLogPrefix+" no bucket (%s,%s)\n", f.root, entName) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-02-18 07:34:42 +08:00
										 |  |  | 				return errDoneForNow | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if isReservedOrInvalidBucket(bucket, false) { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 				if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 					console.Debugf(scannerLogPrefix+" invalid bucket: %v, entry: %v\n", bucket, entName) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-02-18 07:34:42 +08:00
										 |  |  | 				return errDoneForNow | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-done: | 
					
						
							| 
									
										
										
										
											2021-02-18 07:34:42 +08:00
										 |  |  | 				return errDoneForNow | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			default: | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if typ&os.ModeDir != 0 { | 
					
						
							|  |  |  | 				h := hashPath(entName) | 
					
						
							|  |  |  | 				_, exists := f.oldCache.Cache[h.Key()] | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 				if h == thisHash { | 
					
						
							|  |  |  | 					return nil | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 				this := cachedFolder{name: entName, parent: &thisHash, objectHealProbDiv: folder.objectHealProbDiv} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 				delete(abandonedChildren, h.Key()) // h.Key() already accounted for.
 | 
					
						
							|  |  |  | 				if exists { | 
					
						
							|  |  |  | 					existingFolders = append(existingFolders, this) | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 					f.updateCache.copyWithChildren(&f.oldCache, h, &thisHash) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 					newFolders = append(newFolders, this) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-10-23 04:36:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | 			wait := noWait | 
					
						
							|  |  |  | 			if f.weSleep() { | 
					
						
							|  |  |  | 				// Dynamic time delay.
 | 
					
						
							|  |  |  | 				wait = scannerSleeper.Timer(ctx) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Get file size, ignore errors.
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 			item := scannerItem{ | 
					
						
							| 
									
										
										
										
											2023-09-17 10:08:59 +08:00
										 |  |  | 				Path:        pathJoin(f.root, entName), | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  | 				Typ:         typ, | 
					
						
							|  |  |  | 				bucket:      bucket, | 
					
						
							|  |  |  | 				prefix:      path.Dir(prefix), | 
					
						
							|  |  |  | 				objectName:  path.Base(entName), | 
					
						
							|  |  |  | 				debug:       f.dataUsageScannerDebug, | 
					
						
							|  |  |  | 				lifeCycle:   activeLifeCycle, | 
					
						
							|  |  |  | 				replication: replicationCfg, | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			item.heal.enabled = thisHash.modAlt(f.oldCache.Info.NextCycle/folder.objectHealProbDiv, f.healObjectSelect/folder.objectHealProbDiv) && globalIsErasure | 
					
						
							|  |  |  | 			item.heal.bitrot = f.scanMode == madmin.HealDeepScan | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-19 18:40:52 +08:00
										 |  |  | 			// if the drive belongs to an erasure set
 | 
					
						
							|  |  |  | 			// that is already being healed, skip the
 | 
					
						
							|  |  |  | 			// healing attempt on this drive.
 | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 			item.heal.enabled = item.heal.enabled && f.healObjectSelect > 0 | 
					
						
							| 
									
										
										
										
											2021-01-19 18:40:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			sz, err := f.getSize(item) | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 			if err != nil && err != errIgnoreFileContrib { | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 				wait() // wait to proceed to next entry.
 | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 				if err != errSkipFile && f.dataUsageScannerDebug { | 
					
						
							|  |  |  | 					console.Debugf(scannerLogPrefix+" getSize \"%v/%v\" returned err: %v\n", bucket, item.objectPath(), err) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-09-16 00:24:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 			// successfully read means we have a valid object.
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			foundObjects = true | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 			// Remove filename i.e is the meta file to construct object name
 | 
					
						
							|  |  |  | 			item.transformMetaDir() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Object already accounted for, remove from heal map,
 | 
					
						
							|  |  |  | 			// simply because getSize() function already heals the
 | 
					
						
							|  |  |  | 			// object.
 | 
					
						
							| 
									
										
										
										
											2023-09-17 10:08:59 +08:00
										 |  |  | 			delete(abandonedChildren, pathJoin(item.bucket, item.objectPath())) | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 			if err != errIgnoreFileContrib { | 
					
						
							|  |  |  | 				into.addSizes(sz) | 
					
						
							|  |  |  | 				into.Objects++ | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 			wait() // wait to proceed to next entry.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		if foundObjects && globalIsErasure { | 
					
						
							|  |  |  | 			// If we found an object in erasure mode, we skip subdirs (only datadirs)...
 | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// If we have many subfolders, compact ourself.
 | 
					
						
							| 
									
										
										
										
											2022-07-11 22:59:03 +08:00
										 |  |  | 		shouldCompact := f.newCache.Info.Name != folder.name && | 
					
						
							|  |  |  | 			len(existingFolders)+len(newFolders) >= dataScannerCompactAtFolders || | 
					
						
							|  |  |  | 			len(existingFolders)+len(newFolders) >= dataScannerForceCompactAtFolders | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 		if totalFolders := len(existingFolders) + len(newFolders); totalFolders > int(scannerExcessFolders.Load()) { | 
					
						
							|  |  |  | 			prefixName := strings.TrimSuffix(folder.name, "/") + "/" | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			sendEvent(eventArgs{ | 
					
						
							|  |  |  | 				EventName:  event.PrefixManyFolders, | 
					
						
							|  |  |  | 				BucketName: f.root, | 
					
						
							|  |  |  | 				Object: ObjectInfo{ | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 					Name: prefixName, | 
					
						
							|  |  |  | 					Size: int64(totalFolders), | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 				UserAgent: "Scanner", | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 				Host:      globalMinioHost, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 			auditLogInternal(context.Background(), AuditLogOptions{ | 
					
						
							|  |  |  | 				Event:   "scanner:manyprefixes", | 
					
						
							|  |  |  | 				APIName: "Scanner", | 
					
						
							|  |  |  | 				Bucket:  f.root, | 
					
						
							|  |  |  | 				Object:  prefixName, | 
					
						
							|  |  |  | 				Tags: map[string]interface{}{ | 
					
						
							|  |  |  | 					"x-minio-prefixes-total": strconv.Itoa(totalFolders), | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-07-11 22:59:03 +08:00
										 |  |  | 		if !into.Compacted && shouldCompact { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			into.Compacted = true | 
					
						
							|  |  |  | 			newFolders = append(newFolders, existingFolders...) | 
					
						
							|  |  |  | 			existingFolders = nil | 
					
						
							|  |  |  | 			if f.dataUsageScannerDebug { | 
					
						
							|  |  |  | 				console.Debugf(scannerLogPrefix+" Preemptively compacting: %v, entries: %v\n", folder.name, len(existingFolders)+len(newFolders)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		scanFolder := func(folder cachedFolder) { | 
					
						
							|  |  |  | 			if contextCanceled(ctx) { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			dst := into | 
					
						
							|  |  |  | 			if !into.Compacted { | 
					
						
							|  |  |  | 				dst = &dataUsageEntry{Compacted: false} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err := f.scanFolder(ctx, folder, dst); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !into.Compacted { | 
					
						
							| 
									
										
										
										
											2021-08-05 00:14:14 +08:00
										 |  |  | 				h := dataUsageHash(folder.name) | 
					
						
							|  |  |  | 				into.addChild(h) | 
					
						
							|  |  |  | 				// We scanned a folder, optionally send update.
 | 
					
						
							|  |  |  | 				f.updateCache.deleteRecursive(h) | 
					
						
							|  |  |  | 				f.updateCache.copyWithChildren(&f.newCache, h, folder.parent) | 
					
						
							|  |  |  | 				f.sendUpdate() | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-05 00:14:14 +08:00
										 |  |  | 		// Transfer existing
 | 
					
						
							|  |  |  | 		if !into.Compacted { | 
					
						
							|  |  |  | 			for _, folder := range existingFolders { | 
					
						
							|  |  |  | 				h := hashPath(folder.name) | 
					
						
							|  |  |  | 				f.updateCache.copyWithChildren(&f.oldCache, h, folder.parent) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		// Scan new...
 | 
					
						
							|  |  |  | 		for _, folder := range newFolders { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 			h := hashPath(folder.name) | 
					
						
							|  |  |  | 			// Add new folders to the update tree so totals update for these.
 | 
					
						
							|  |  |  | 			if !into.Compacted { | 
					
						
							|  |  |  | 				var foundAny bool | 
					
						
							|  |  |  | 				parent := thisHash | 
					
						
							|  |  |  | 				for parent != hashPath(f.updateCache.Info.Name) { | 
					
						
							|  |  |  | 					e := f.updateCache.find(parent.Key()) | 
					
						
							|  |  |  | 					if e == nil || e.Compacted { | 
					
						
							|  |  |  | 						foundAny = true | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-12-07 10:17:03 +08:00
										 |  |  | 					next := f.updateCache.searchParent(parent) | 
					
						
							|  |  |  | 					if next == nil { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 						foundAny = true | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-12-07 10:17:03 +08:00
										 |  |  | 					parent = *next | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if !foundAny { | 
					
						
							|  |  |  | 					// Add non-compacted empty entry.
 | 
					
						
							|  |  |  | 					f.updateCache.replaceHashed(h, &thisHash, dataUsageEntry{}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			f.updateCurrentPath(folder.name) | 
					
						
							|  |  |  | 			stopFn := globalScannerMetrics.log(scannerMetricScanFolder, f.root, folder.name) | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			scanFolder(folder) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			stopFn(map[string]string{"type": "new"}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 			// Add new folders if this is new and we don't have existing.
 | 
					
						
							|  |  |  | 			if !into.Compacted { | 
					
						
							|  |  |  | 				parent := f.updateCache.find(thisHash.Key()) | 
					
						
							|  |  |  | 				if parent != nil && !parent.Compacted { | 
					
						
							|  |  |  | 					f.updateCache.deleteRecursive(h) | 
					
						
							|  |  |  | 					f.updateCache.copyWithChildren(&f.newCache, h, &thisHash) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Scan existing...
 | 
					
						
							|  |  |  | 		for _, folder := range existingFolders { | 
					
						
							|  |  |  | 			h := hashPath(folder.name) | 
					
						
							|  |  |  | 			// Check if we should skip scanning folder...
 | 
					
						
							|  |  |  | 			// We can only skip if we are not indexing into a compacted destination
 | 
					
						
							|  |  |  | 			// and the entry itself is compacted.
 | 
					
						
							|  |  |  | 			if !into.Compacted && f.oldCache.isCompacted(h) { | 
					
						
							|  |  |  | 				if !h.mod(f.oldCache.Info.NextCycle, dataUsageUpdateDirCycles) { | 
					
						
							| 
									
										
										
										
											2023-02-24 11:33:31 +08:00
										 |  |  | 					// Transfer and add as child...
 | 
					
						
							|  |  |  | 					f.newCache.copyWithChildren(&f.oldCache, h, folder.parent) | 
					
						
							|  |  |  | 					into.addChild(h) | 
					
						
							|  |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			f.updateCurrentPath(folder.name) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			stopFn := globalScannerMetrics.log(scannerMetricScanFolder, f.root, folder.name) | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			scanFolder(folder) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			stopFn(map[string]string{"type": "existing"}) | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Scan for healing
 | 
					
						
							|  |  |  | 		if f.healObjectSelect == 0 || len(abandonedChildren) == 0 { | 
					
						
							|  |  |  | 			// If we are not heal scanning, return now.
 | 
					
						
							|  |  |  | 			break | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 00:52:05 +08:00
										 |  |  | 		if len(f.disks) == 0 || f.disksQuorum == 0 { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			break | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bgSeq, found := globalBackgroundHealState.getHealSequenceByToken(bgHealingUUID) | 
					
						
							|  |  |  | 		if !found { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			break | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		// Whatever remains in 'abandonedChildren' are folders at this level
 | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 		// that existed in the previous run but wasn't found now.
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// This may be because of 2 reasons:
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 1) The folder/object was deleted.
 | 
					
						
							|  |  |  | 		// 2) We come from another disk and this disk missed the write.
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// We therefore perform a heal check.
 | 
					
						
							|  |  |  | 		// If that doesn't bring it back we remove the folder and assume it was deleted.
 | 
					
						
							|  |  |  | 		// This means that the next run will not look for it.
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 		// How to resolve results.
 | 
					
						
							|  |  |  | 		resolver := metadataResolutionParams{ | 
					
						
							| 
									
										
										
										
											2022-01-27 00:34:56 +08:00
										 |  |  | 			dirQuorum: f.disksQuorum, | 
					
						
							|  |  |  | 			objQuorum: f.disksQuorum, | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 			bucket:    "", | 
					
						
							| 
									
										
										
										
											2021-12-03 03:29:16 +08:00
										 |  |  | 			strict:    false, | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 		healObjectsPrefix := color.Green("healObjects:") | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		for k := range abandonedChildren { | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 			bucket, prefix := path2BucketObject(k) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			stopFn := globalScannerMetrics.time(scannerMetricCheckMissing) | 
					
						
							|  |  |  | 			f.updateCurrentPath(k) | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 			if bucket != resolver.bucket { | 
					
						
							|  |  |  | 				// Bucket might be missing as well with abandoned children.
 | 
					
						
							|  |  |  | 				// make sure it is created first otherwise healing won't proceed
 | 
					
						
							|  |  |  | 				// for objects.
 | 
					
						
							| 
									
										
										
										
											2023-07-18 00:52:05 +08:00
										 |  |  | 				bgSeq.queueHealTask(healSource{ | 
					
						
							|  |  |  | 					bucket: bucket, | 
					
						
							|  |  |  | 				}, madmin.HealItemBucket) | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 			resolver.bucket = bucket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			foundObjs := false | 
					
						
							|  |  |  | 			ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2021-03-07 01:25:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 			err := listPathRaw(ctx, listPathRawOptions{ | 
					
						
							|  |  |  | 				disks:          f.disks, | 
					
						
							|  |  |  | 				bucket:         bucket, | 
					
						
							|  |  |  | 				path:           prefix, | 
					
						
							|  |  |  | 				recursive:      true, | 
					
						
							|  |  |  | 				reportNotFound: true, | 
					
						
							| 
									
										
										
										
											2022-01-27 00:34:56 +08:00
										 |  |  | 				minDisks:       f.disksQuorum, | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 				agreed: func(entry metaCacheEntry) { | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 					f.updateCurrentPath(entry.name) | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 					if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 						console.Debugf(healObjectsPrefix+" got agreement: %v\n", entry.name) | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				// Some disks have data for this.
 | 
					
						
							| 
									
										
										
										
											2022-07-08 04:45:34 +08:00
										 |  |  | 				partial: func(entries metaCacheEntries, errs []error) { | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					entry, ok := entries.resolve(&resolver) | 
					
						
							|  |  |  | 					if !ok { | 
					
						
							| 
									
										
										
										
											2024-01-18 15:03:17 +08:00
										 |  |  | 						// check if we can get one entry at least
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:55:17 +08:00
										 |  |  | 						// proceed to heal nonetheless, since
 | 
					
						
							|  |  |  | 						// this object might be dangling.
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 						entry, _ = entries.firstFound() | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2024-01-03 05:51:24 +08:00
										 |  |  | 					wait := noWait | 
					
						
							|  |  |  | 					if f.weSleep() { | 
					
						
							|  |  |  | 						// wait timer per object.
 | 
					
						
							|  |  |  | 						wait = scannerSleeper.Timer(ctx) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 					defer wait() | 
					
						
							|  |  |  | 					f.updateCurrentPath(entry.name) | 
					
						
							|  |  |  | 					stopFn := globalScannerMetrics.log(scannerMetricHealAbandonedObject, f.root, entry.name) | 
					
						
							|  |  |  | 					custom := make(map[string]string) | 
					
						
							|  |  |  | 					defer stopFn(custom) | 
					
						
							| 
									
										
										
										
											2020-10-23 04:36:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 					if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 						console.Debugf(healObjectsPrefix+" resolved to: %v, dir: %v\n", entry.name, entry.isDir()) | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					if entry.isDir() { | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2021-04-27 23:24:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					// We got an entry which we should be able to heal.
 | 
					
						
							|  |  |  | 					fiv, err := entry.fileInfoVersions(bucket) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						err := bgSeq.queueHealTask(healSource{ | 
					
						
							|  |  |  | 							bucket:    bucket, | 
					
						
							|  |  |  | 							object:    entry.name, | 
					
						
							|  |  |  | 							versionID: "", | 
					
						
							|  |  |  | 						}, madmin.HealItemObject) | 
					
						
							| 
									
										
										
										
											2022-12-15 08:39:18 +08:00
										 |  |  | 						if !isErrObjectNotFound(err) && !isErrVersionNotFound(err) { | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 							logger.LogOnceIf(ctx, err, entry.name) | 
					
						
							| 
									
										
										
										
											2022-12-15 08:39:18 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 						foundObjs = foundObjs || err == nil | 
					
						
							|  |  |  | 						return | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2021-04-27 23:24:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 					custom["versions"] = fmt.Sprint(len(fiv.Versions)) | 
					
						
							|  |  |  | 					var successVersions, failVersions int | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					for _, ver := range fiv.Versions { | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 						stopFn := globalScannerMetrics.timeSize(scannerMetricHealAbandonedVersion) | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 						err := bgSeq.queueHealTask(healSource{ | 
					
						
							|  |  |  | 							bucket:    bucket, | 
					
						
							|  |  |  | 							object:    fiv.Name, | 
					
						
							|  |  |  | 							versionID: ver.VersionID, | 
					
						
							|  |  |  | 						}, madmin.HealItemObject) | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 						stopFn(int(ver.Size)) | 
					
						
							| 
									
										
										
										
											2022-12-15 08:39:18 +08:00
										 |  |  | 						if !isErrObjectNotFound(err) && !isErrVersionNotFound(err) { | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 							logger.LogOnceIf(ctx, err, fiv.Name) | 
					
						
							| 
									
										
										
										
											2022-12-15 08:39:18 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 						if err == nil { | 
					
						
							|  |  |  | 							successVersions++ | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							failVersions++ | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 						foundObjs = foundObjs || err == nil | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-09-25 13:15:31 +08:00
										 |  |  | 					custom["success_versions"] = fmt.Sprint(successVersions) | 
					
						
							|  |  |  | 					custom["failed_versions"] = fmt.Sprint(failVersions) | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				// Too many disks failed.
 | 
					
						
							|  |  |  | 				finished: func(errs []error) { | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 					if f.dataUsageScannerDebug { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 						console.Debugf(healObjectsPrefix+" too many errors: %v\n", errs) | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					cancel() | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2020-10-23 04:36:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			stopFn() | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | 			if f.dataUsageScannerDebug && err != nil && err != errFileNotFound { | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 				console.Debugf(healObjectsPrefix+" checking returned value %v (%T)\n", err, err) | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Add unless healing returned an error.
 | 
					
						
							| 
									
										
										
										
											2020-12-02 04:07:39 +08:00
										 |  |  | 			if foundObjs { | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 				this := cachedFolder{name: k, parent: &thisHash, objectHealProbDiv: 1} | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 				stopFn := globalScannerMetrics.log(scannerMetricScanFolder, f.root, this.name, "HEALED") | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 				scanFolder(this) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 				stopFn(map[string]string{"type": "healed"}) | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !wasCompacted { | 
					
						
							|  |  |  | 		f.newCache.replaceHashed(thisHash, folder.parent, *into) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	if !into.Compacted && f.newCache.Info.Name != folder.name { | 
					
						
							|  |  |  | 		flat := f.newCache.sizeRecursive(thisHash.Key()) | 
					
						
							|  |  |  | 		flat.Compacted = true | 
					
						
							|  |  |  | 		var compact bool | 
					
						
							|  |  |  | 		if flat.Objects < dataScannerCompactLeastObject { | 
					
						
							|  |  |  | 			compact = true | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// Compact if we only have objects as children...
 | 
					
						
							|  |  |  | 			compact = true | 
					
						
							|  |  |  | 			for k := range into.Children { | 
					
						
							|  |  |  | 				if v, ok := f.newCache.Cache[k]; ok { | 
					
						
							|  |  |  | 					if len(v.Children) > 0 || v.Objects > 1 { | 
					
						
							|  |  |  | 						compact = false | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-19 18:40:52 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 		if compact { | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			stop := globalScannerMetrics.log(scannerMetricCompactFolder, folder.name) | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 			f.newCache.deleteRecursive(thisHash) | 
					
						
							|  |  |  | 			f.newCache.replaceHashed(thisHash, folder.parent, *flat) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			total := map[string]string{ | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:58 +08:00
										 |  |  | 				"objects": strconv.FormatUint(flat.Objects, 10), | 
					
						
							|  |  |  | 				"size":    strconv.FormatInt(flat.Size, 10), | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if flat.Versions > 0 { | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:58 +08:00
										 |  |  | 				total["versions"] = strconv.FormatUint(flat.Versions, 10) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			stop(total) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-29 17:57:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 	// Compact if too many children...
 | 
					
						
							|  |  |  | 	if !into.Compacted { | 
					
						
							|  |  |  | 		f.newCache.reduceChildrenOf(thisHash, dataScannerCompactAtChildren, f.newCache.Info.Name != folder.name) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-20 05:38:30 +08:00
										 |  |  | 	if _, ok := f.updateCache.Cache[thisHash.Key()]; !wasCompacted && ok { | 
					
						
							|  |  |  | 		// Replace if existed before.
 | 
					
						
							|  |  |  | 		if flat := f.newCache.sizeRecursive(thisHash.Key()); flat != nil { | 
					
						
							|  |  |  | 			f.updateCache.deleteRecursive(thisHash) | 
					
						
							|  |  |  | 			f.updateCache.replaceHashed(thisHash, folder.parent, *flat) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-12 09:36:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | // scannerItem represents each file while walking.
 | 
					
						
							|  |  |  | type scannerItem struct { | 
					
						
							| 
									
										
										
										
											2021-11-09 02:25:34 +08:00
										 |  |  | 	Path        string | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  | 	bucket      string // Bucket.
 | 
					
						
							|  |  |  | 	prefix      string // Only the prefix if any, does not have final object name.
 | 
					
						
							|  |  |  | 	objectName  string // Only the object name without prefixes.
 | 
					
						
							|  |  |  | 	replication replicationConfig | 
					
						
							| 
									
										
										
										
											2021-11-09 02:25:34 +08:00
										 |  |  | 	lifeCycle   *lifecycle.Lifecycle | 
					
						
							|  |  |  | 	Typ         fs.FileMode | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	heal        struct { | 
					
						
							|  |  |  | 		enabled bool | 
					
						
							|  |  |  | 		bitrot  bool | 
					
						
							|  |  |  | 	} // Has the object been selected for heal check?
 | 
					
						
							|  |  |  | 	debug bool | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:47:48 +08:00
										 |  |  | type sizeSummary struct { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	totalSize       int64 | 
					
						
							|  |  |  | 	versions        uint64 | 
					
						
							| 
									
										
										
										
											2023-07-19 01:49:40 +08:00
										 |  |  | 	deleteMarkers   uint64 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	replicatedSize  int64 | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	replicatedCount int64 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	pendingSize     int64 | 
					
						
							|  |  |  | 	failedSize      int64 | 
					
						
							|  |  |  | 	replicaSize     int64 | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	replicaCount    int64 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	pendingCount    uint64 | 
					
						
							|  |  |  | 	failedCount     uint64 | 
					
						
							|  |  |  | 	replTargetStats map[string]replTargetSizeSummary | 
					
						
							| 
									
										
										
										
											2021-10-24 09:38:33 +08:00
										 |  |  | 	tiers           map[string]tierStats | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // replTargetSizeSummary holds summary of replication stats by target
 | 
					
						
							|  |  |  | type replTargetSizeSummary struct { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	replicatedSize  int64 | 
					
						
							|  |  |  | 	replicatedCount int64 | 
					
						
							|  |  |  | 	pendingSize     int64 | 
					
						
							|  |  |  | 	failedSize      int64 | 
					
						
							|  |  |  | 	pendingCount    uint64 | 
					
						
							|  |  |  | 	failedCount     uint64 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:47:48 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | type getSizeFn func(item scannerItem) (sizeSummary, error) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // transformMetaDir will transform a directory to prefix/file.ext
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | func (i *scannerItem) transformMetaDir() { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	split := strings.Split(i.prefix, SlashSeparator) | 
					
						
							|  |  |  | 	if len(split) > 1 { | 
					
						
							| 
									
										
										
										
											2023-09-17 10:08:59 +08:00
										 |  |  | 		i.prefix = pathJoin(split[:len(split)-1]...) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		i.prefix = "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Object name is last element
 | 
					
						
							|  |  |  | 	i.objectName = split[len(split)-1] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	applyActionsLogPrefix        = color.Green("applyActions:") | 
					
						
							|  |  |  | 	applyVersionActionsLogPrefix = color.Green("applyVersionActions:") | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | func (i *scannerItem) applyHealing(ctx context.Context, o ObjectLayer, oi ObjectInfo) (size int64) { | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	if i.debug { | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 		if oi.VersionID != "" { | 
					
						
							|  |  |  | 			console.Debugf(applyActionsLogPrefix+" heal checking: %v/%v v(%s)\n", i.bucket, i.objectPath(), oi.VersionID) | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			console.Debugf(applyActionsLogPrefix+" heal checking: %v/%v\n", i.bucket, i.objectPath()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 	scanMode := madmin.HealNormalScan | 
					
						
							|  |  |  | 	if i.heal.bitrot { | 
					
						
							|  |  |  | 		scanMode = madmin.HealDeepScan | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 	healOpts := madmin.HealOpts{ | 
					
						
							|  |  |  | 		Remove:   healDeleteDangling, | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 		ScanMode: scanMode, | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-31 09:46:04 +08:00
										 |  |  | 	res, _ := o.HealObject(ctx, i.bucket, i.objectPath(), oi.VersionID, healOpts) | 
					
						
							|  |  |  | 	if res.ObjectSize > 0 { | 
					
						
							|  |  |  | 		return res.ObjectSize | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-31 09:46:04 +08:00
										 |  |  | 	return 0 | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | func (i *scannerItem) applyLifecycle(ctx context.Context, o ObjectLayer, oi ObjectInfo) (action lifecycle.Action, size int64) { | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 	size, err := oi.GetActualSize() | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | 	if i.debug { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-08-25 04:47:01 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	if i.lifeCycle == nil { | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 		return action, size | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 	versionID := oi.VersionID | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 	vcfg, _ := globalBucketVersioningSys.Get(i.bucket) | 
					
						
							| 
									
										
										
										
											2022-04-12 12:55:56 +08:00
										 |  |  | 	rCfg, _ := globalBucketObjectLockSys.Get(i.bucket) | 
					
						
							| 
									
										
										
										
											2023-10-06 20:55:15 +08:00
										 |  |  | 	replcfg, _ := getReplicationConfig(ctx, i.bucket) | 
					
						
							|  |  |  | 	lcEvt := evalActionFromLifecycle(ctx, *i.lifeCycle, rCfg, replcfg, oi) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	if i.debug { | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 		if versionID != "" { | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 			console.Debugf(applyActionsLogPrefix+" lifecycle: %q (version-id=%s), Initial scan: %v\n", i.objectPath(), versionID, lcEvt.Action) | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 			console.Debugf(applyActionsLogPrefix+" lifecycle: %q Initial scan: %v\n", i.objectPath(), lcEvt.Action) | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-03 00:31:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 	switch lcEvt.Action { | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 	// This version doesn't contribute towards sizeS only when it is permanently deleted.
 | 
					
						
							|  |  |  | 	// This can happen when,
 | 
					
						
							|  |  |  | 	// - ExpireObjectAllVersions flag is enabled
 | 
					
						
							|  |  |  | 	// - NoncurrentVersionExpiration is applicable
 | 
					
						
							|  |  |  | 	case lifecycle.DeleteVersionAction, lifecycle.DeleteAllVersionsAction: | 
					
						
							|  |  |  | 		size = 0 | 
					
						
							|  |  |  | 	case lifecycle.DeleteAction: | 
					
						
							|  |  |  | 		// On a non-versioned bucket, DeleteObject removes the only version permanently.
 | 
					
						
							|  |  |  | 		if !vcfg.PrefixEnabled(oi.Name) { | 
					
						
							|  |  |  | 			size = 0 | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	applyLifecycleAction(lcEvt, lcEventSrc_Scanner, oi) | 
					
						
							|  |  |  | 	return lcEvt.Action, size | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-15 01:41:44 +08:00
										 |  |  | // applyNewerNoncurrentVersionLimit removes noncurrent versions older than the most recent NewerNoncurrentVersions configured.
 | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | // Note: This function doesn't update sizeSummary since it always removes versions that it doesn't return.
 | 
					
						
							| 
									
										
										
										
											2024-02-28 15:02:14 +08:00
										 |  |  | func (i *scannerItem) applyNewerNoncurrentVersionLimit(ctx context.Context, _ ObjectLayer, fivs []FileInfo, expState *expiryState) ([]ObjectInfo, error) { | 
					
						
							| 
									
										
										
										
											2022-11-29 02:20:55 +08:00
										 |  |  | 	done := globalScannerMetrics.time(scannerMetricApplyNonCurrent) | 
					
						
							|  |  |  | 	defer done() | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 	rcfg, _ := globalBucketObjectLockSys.Get(i.bucket) | 
					
						
							|  |  |  | 	vcfg, _ := globalBucketVersioningSys.Get(i.bucket) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	versioned := vcfg != nil && vcfg.Versioned(i.objectPath()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objectInfos := make([]ObjectInfo, 0, len(fivs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if i.lifeCycle == nil { | 
					
						
							|  |  |  | 		for _, fi := range fivs { | 
					
						
							|  |  |  | 			objectInfos = append(objectInfos, fi.ToObjectInfo(i.bucket, i.objectPath(), versioned)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return objectInfos, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-03 03:56:33 +08:00
										 |  |  | 	event := i.lifeCycle.NoncurrentVersionsExpirationLimit(lifecycle.ObjectOpts{Name: i.objectPath()}) | 
					
						
							|  |  |  | 	lim := event.NewerNoncurrentVersions | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 	if lim == 0 || len(fivs) <= lim+1 { // fewer than lim _noncurrent_ versions
 | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 		for _, fi := range fivs { | 
					
						
							|  |  |  | 			objectInfos = append(objectInfos, fi.ToObjectInfo(i.bucket, i.objectPath(), versioned)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return objectInfos, nil | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	overflowVersions := fivs[lim+1:] | 
					
						
							| 
									
										
										
										
											2023-08-25 00:26:29 +08:00
										 |  |  | 	// Retain the current version + most recent lim noncurrent versions
 | 
					
						
							|  |  |  | 	for _, fi := range fivs[:lim+1] { | 
					
						
							|  |  |  | 		objectInfos = append(objectInfos, fi.ToObjectInfo(i.bucket, i.objectPath(), versioned)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-05-31 17:57:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 	toDel := make([]ObjectToDelete, 0, len(overflowVersions)) | 
					
						
							|  |  |  | 	for _, fi := range overflowVersions { | 
					
						
							| 
									
										
										
										
											2022-05-31 17:57:57 +08:00
										 |  |  | 		obj := fi.ToObjectInfo(i.bucket, i.objectPath(), versioned) | 
					
						
							| 
									
										
										
										
											2021-12-15 01:41:44 +08:00
										 |  |  | 		// skip versions with object locking enabled
 | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 		if rcfg.LockEnabled && enforceRetentionForDeletion(ctx, obj) { | 
					
						
							|  |  |  | 			if i.debug { | 
					
						
							|  |  |  | 				if obj.VersionID != "" { | 
					
						
							|  |  |  | 					console.Debugf(applyVersionActionsLogPrefix+" lifecycle: %s v(%s) is locked, not deleting\n", obj.Name, obj.VersionID) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					console.Debugf(applyVersionActionsLogPrefix+" lifecycle: %s is locked, not deleting\n", obj.Name) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-12-15 01:41:44 +08:00
										 |  |  | 			// add this version back to remaining versions for
 | 
					
						
							|  |  |  | 			// subsequent lifecycle policy applications
 | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 			objectInfos = append(objectInfos, obj) | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-12-15 01:41:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// NoncurrentDays not passed yet.
 | 
					
						
							| 
									
										
										
										
											2023-05-03 03:56:33 +08:00
										 |  |  | 		if time.Now().UTC().Before(lifecycle.ExpectedExpiryTime(obj.SuccessorModTime, event.NoncurrentDays)) { | 
					
						
							| 
									
										
										
										
											2021-12-15 01:41:44 +08:00
										 |  |  | 			// add this version back to remaining versions for
 | 
					
						
							|  |  |  | 			// subsequent lifecycle policy applications
 | 
					
						
							| 
									
										
										
										
											2023-07-15 07:42:35 +08:00
										 |  |  | 			objectInfos = append(objectInfos, obj) | 
					
						
							| 
									
										
										
										
											2021-12-15 01:41:44 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 		toDel = append(toDel, ObjectToDelete{ | 
					
						
							| 
									
										
										
										
											2022-01-03 17:28:52 +08:00
										 |  |  | 			ObjectV: ObjectV{ | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 				ObjectName: obj.Name, | 
					
						
							|  |  |  | 				VersionID:  obj.VersionID, | 
					
						
							| 
									
										
										
										
											2022-01-03 17:28:52 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-20 11:10:58 +08:00
										 |  |  | 	if len(toDel) > 0 { | 
					
						
							|  |  |  | 		expState.enqueueByNewerNoncurrent(i.bucket, toDel, event) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 	return objectInfos, nil | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // applyVersionActions will apply lifecycle checks on all versions of a scanned item. Returns versions that remain
 | 
					
						
							|  |  |  | // after applying lifecycle checks configured.
 | 
					
						
							| 
									
										
										
										
											2024-02-28 15:02:14 +08:00
										 |  |  | func (i *scannerItem) applyVersionActions(ctx context.Context, o ObjectLayer, fivs []FileInfo, expState *expiryState) ([]ObjectInfo, error) { | 
					
						
							|  |  |  | 	objInfos, err := i.applyNewerNoncurrentVersionLimit(ctx, o, fivs, expState) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-11-29 02:20:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 	// Check if we have many versions after applyNewerNoncurrentVersionLimit.
 | 
					
						
							| 
									
										
										
										
											2024-02-12 15:41:53 +08:00
										 |  |  | 	if len(objInfos) > int(scannerExcessObjectVersions.Load()) { | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 		// Notify object accessed via a GET request.
 | 
					
						
							|  |  |  | 		sendEvent(eventArgs{ | 
					
						
							|  |  |  | 			EventName:  event.ObjectManyVersions, | 
					
						
							|  |  |  | 			BucketName: i.bucket, | 
					
						
							|  |  |  | 			Object: ObjectInfo{ | 
					
						
							|  |  |  | 				Name: i.objectPath(), | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 			UserAgent:    "Scanner", | 
					
						
							| 
									
										
										
										
											2023-04-07 01:20:53 +08:00
										 |  |  | 			Host:         globalLocalNodeName, | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 			RespElements: map[string]string{"x-minio-versions": strconv.Itoa(len(objInfos))}, | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2024-02-12 15:41:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		auditLogInternal(context.Background(), AuditLogOptions{ | 
					
						
							|  |  |  | 			Event:   "scanner:manyversions", | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 			APIName: "Scanner", | 
					
						
							| 
									
										
										
										
											2024-02-12 15:41:53 +08:00
										 |  |  | 			Bucket:  i.bucket, | 
					
						
							|  |  |  | 			Object:  i.objectPath(), | 
					
						
							|  |  |  | 			Tags: map[string]interface{}{ | 
					
						
							| 
									
										
										
										
											2024-02-23 00:18:13 +08:00
										 |  |  | 				"x-minio-versions": strconv.Itoa(len(objInfos)), | 
					
						
							| 
									
										
										
										
											2024-02-12 15:41:53 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2023-02-22 01:33:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-07 05:10:01 +08:00
										 |  |  | 	return objInfos, nil | 
					
						
							| 
									
										
										
										
											2021-11-20 09:54:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | // applyActions will apply lifecycle checks on to a scanned item.
 | 
					
						
							|  |  |  | // The resulting size on disk will always be returned.
 | 
					
						
							|  |  |  | // The metadata will be compared to consensus on the object layer before any changes are applied.
 | 
					
						
							|  |  |  | // If no metadata is supplied, -1 is returned if no action is taken.
 | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | func (i *scannerItem) applyActions(ctx context.Context, o ObjectLayer, oi ObjectInfo, sizeS *sizeSummary) (objDeleted bool, size int64) { | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	done := globalScannerMetrics.time(scannerMetricILM) | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 	var action lifecycle.Action | 
					
						
							|  |  |  | 	action, size = i.applyLifecycle(ctx, o, oi) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	done() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 	// Note: objDeleted is true if and only if action ==
 | 
					
						
							|  |  |  | 	// lifecycle.DeleteAllVersionsAction
 | 
					
						
							|  |  |  | 	if action == lifecycle.DeleteAllVersionsAction { | 
					
						
							|  |  |  | 		return true, 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-31 17:15:08 +08:00
										 |  |  | 	// For instance, an applied lifecycle means we remove/transitioned an object
 | 
					
						
							|  |  |  | 	// from the current deployment, which means we don't have to call healing
 | 
					
						
							|  |  |  | 	// routine even if we are asked to do via heal flag.
 | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 	if action == lifecycle.NoneAction { | 
					
						
							| 
									
										
										
										
											2022-04-07 23:10:40 +08:00
										 |  |  | 		if i.heal.enabled { | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			done := globalScannerMetrics.time(scannerMetricHealCheck) | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 			size = i.applyHealing(ctx, o, oi) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			done() | 
					
						
							| 
									
										
										
										
											2023-09-23 03:07:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if healDeleteDangling { | 
					
						
							|  |  |  | 				done := globalScannerMetrics.time(scannerMetricCleanAbandoned) | 
					
						
							|  |  |  | 				err := o.CheckAbandonedParts(ctx, i.bucket, i.objectPath(), madmin.HealOpts{Remove: healDeleteDangling}) | 
					
						
							|  |  |  | 				done() | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 					logger.LogOnceIf(ctx, fmt.Errorf("unable to check object %s/%s for abandoned data: %w", i.bucket, i.objectPath(), err), i.objectPath()) | 
					
						
							| 
									
										
										
										
											2023-09-23 03:07:52 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-04-27 23:24:44 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-09-23 03:07:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-27 23:24:44 +08:00
										 |  |  | 		// replicate only if lifecycle rules are not applied.
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		done := globalScannerMetrics.time(scannerMetricCheckReplication) | 
					
						
							| 
									
										
										
										
											2021-08-27 05:06:04 +08:00
										 |  |  | 		i.healReplication(ctx, o, oi.Clone(), sizeS) | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		done() | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-29 00:39:21 +08:00
										 |  |  | 	return false, size | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-06 20:55:15 +08:00
										 |  |  | func evalActionFromLifecycle(ctx context.Context, lc lifecycle.Lifecycle, lr lock.Retention, rcfg *replication.Config, obj ObjectInfo) lifecycle.Event { | 
					
						
							| 
									
										
										
										
											2022-11-10 23:17:45 +08:00
										 |  |  | 	event := lc.Eval(obj.ToLifecycleOpts()) | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:12 +08:00
										 |  |  | 	if serverDebugLog { | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 		console.Debugf(applyActionsLogPrefix+" lifecycle: Secondary scan: %v\n", event.Action) | 
					
						
							| 
									
										
										
										
											2020-08-04 14:04:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 	if event.Action == lifecycle.NoneAction { | 
					
						
							|  |  |  | 		return event | 
					
						
							| 
									
										
										
										
											2020-08-04 14:04:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-09 06:42:10 +08:00
										 |  |  | 	if obj.IsLatest && event.Action == lifecycle.DeleteAllVersionsAction { | 
					
						
							|  |  |  | 		if lr.LockEnabled && enforceRetentionForDeletion(ctx, obj) { | 
					
						
							|  |  |  | 			return lifecycle.Event{Action: lifecycle.NoneAction} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 	switch event.Action { | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 	case lifecycle.DeleteVersionAction, lifecycle.DeleteRestoredVersionAction: | 
					
						
							| 
									
										
										
										
											2020-08-04 14:04:40 +08:00
										 |  |  | 		// Defensive code, should never happen
 | 
					
						
							|  |  |  | 		if obj.VersionID == "" { | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 			return lifecycle.Event{Action: lifecycle.NoneAction} | 
					
						
							| 
									
										
										
										
											2020-08-04 14:04:40 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 		if lr.LockEnabled && enforceRetentionForDeletion(ctx, obj) { | 
					
						
							| 
									
										
										
										
											2022-10-08 01:24:12 +08:00
										 |  |  | 			if serverDebugLog { | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 				if obj.VersionID != "" { | 
					
						
							|  |  |  | 					console.Debugf(applyActionsLogPrefix+" lifecycle: %s v(%s) is locked, not deleting\n", obj.Name, obj.VersionID) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					console.Debugf(applyActionsLogPrefix+" lifecycle: %s is locked, not deleting\n", obj.Name) | 
					
						
							| 
									
										
										
										
											2020-08-04 14:04:40 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 			return lifecycle.Event{Action: lifecycle.NoneAction} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-10-06 20:55:15 +08:00
										 |  |  | 		if rcfg != nil && !obj.VersionPurgeStatus.Empty() && rcfg.HasActiveRules(obj.Name, true) { | 
					
						
							|  |  |  | 			return lifecycle.Event{Action: lifecycle.NoneAction} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-05 11:56:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 01:46:53 +08:00
										 |  |  | 	return event | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | func applyTransitionRule(event lifecycle.Event, src lcEventSrc, obj ObjectInfo) bool { | 
					
						
							| 
									
										
										
										
											2021-08-25 03:24:00 +08:00
										 |  |  | 	if obj.DeleteMarker { | 
					
						
							|  |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-12-03 05:02:12 +08:00
										 |  |  | 	globalTransitionState.queueTransitionTask(obj, event, src) | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | func applyExpiryOnTransitionedObject(ctx context.Context, objLayer ObjectLayer, obj ObjectInfo, lcEvent lifecycle.Event, src lcEventSrc) bool { | 
					
						
							| 
									
										
										
										
											2024-02-23 07:00:32 +08:00
										 |  |  | 	var err error | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Note: DeleteAllVersions action is not supported for
 | 
					
						
							|  |  |  | 		// transitioned objects
 | 
					
						
							|  |  |  | 		globalScannerMetrics.timeILM(lcEvent.Action)(1) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = expireTransitionedObject(ctx, objLayer, &obj, lcEvent, src); err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 		if isErrObjectNotFound(err) || isErrVersionNotFound(err) { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-13 08:11:17 +08:00
										 |  |  | 		logger.LogOnceIf(ctx, err, obj.Name) | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 	// Notification already sent in *expireTransitionedObject*, just return 'true' here.
 | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | func applyExpiryOnNonTransitionedObjects(ctx context.Context, objLayer ObjectLayer, obj ObjectInfo, lcEvent lifecycle.Event, src lcEventSrc) bool { | 
					
						
							| 
									
										
										
										
											2023-04-12 10:22:32 +08:00
										 |  |  | 	traceFn := globalLifecycleSys.trace(obj) | 
					
						
							| 
									
										
										
										
											2021-08-28 08:06:47 +08:00
										 |  |  | 	opts := ObjectOptions{ | 
					
						
							|  |  |  | 		Expiration: ExpirationOptions{Expire: true}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  | 	if lcEvent.Action.DeleteVersioned() { | 
					
						
							| 
									
										
										
										
											2021-02-07 08:10:33 +08:00
										 |  |  | 		opts.VersionID = obj.VersionID | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-19 03:55:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	opts.Versioned = globalBucketVersioningSys.PrefixEnabled(obj.Bucket, obj.Name) | 
					
						
							|  |  |  | 	opts.VersionSuspended = globalBucketVersioningSys.PrefixSuspended(obj.Bucket, obj.Name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-29 13:12:28 +08:00
										 |  |  | 	if lcEvent.Action.DeleteAll() { | 
					
						
							|  |  |  | 		opts.DeletePrefix = true | 
					
						
							| 
									
										
										
										
											2024-03-28 11:18:15 +08:00
										 |  |  | 		// use prefix delete on exact object (this is an optimization to avoid fan-out calls)
 | 
					
						
							|  |  |  | 		opts.DeletePrefixObject = true | 
					
						
							| 
									
										
										
										
											2023-06-29 13:12:28 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-23 07:00:32 +08:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		dobj ObjectInfo | 
					
						
							|  |  |  | 		err  error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if lcEvent.Action != lifecycle.NoneAction { | 
					
						
							|  |  |  | 			numVersions := uint64(1) | 
					
						
							|  |  |  | 			if lcEvent.Action == lifecycle.DeleteAllVersionsAction { | 
					
						
							|  |  |  | 				numVersions = uint64(obj.NumVersions) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			globalScannerMetrics.timeILM(lcEvent.Action)(numVersions) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-22 01:21:35 +08:00
										 |  |  | 	dobj, err = objLayer.DeleteObject(ctx, obj.Bucket, encodeDirObject(obj.Name), opts) | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-01-18 05:58:41 +08:00
										 |  |  | 		if isErrObjectNotFound(err) || isErrVersionNotFound(err) { | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 			return false | 
					
						
							| 
									
										
										
										
											2021-01-18 05:58:41 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-14 04:05:54 +08:00
										 |  |  | 		// Assume it is still there.
 | 
					
						
							| 
									
										
										
										
											2023-06-25 11:29:13 +08:00
										 |  |  | 		logger.LogOnceIf(ctx, err, "non-transition-expiry") | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-15 00:07:58 +08:00
										 |  |  | 	if dobj.Name == "" { | 
					
						
							|  |  |  | 		dobj = obj | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | 	tags := newLifecycleAuditEvent(src, lcEvent).Tags() | 
					
						
							| 
									
										
										
										
											2021-04-24 00:51:12 +08:00
										 |  |  | 	// Send audit for the lifecycle delete operation
 | 
					
						
							| 
									
										
										
										
											2024-02-15 00:07:58 +08:00
										 |  |  | 	auditLogLifecycle(ctx, dobj, ILMExpiry, tags, traceFn) | 
					
						
							| 
									
										
										
										
											2021-04-24 00:51:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-17 12:22:12 +08:00
										 |  |  | 	eventName := event.ObjectRemovedDelete | 
					
						
							|  |  |  | 	if obj.DeleteMarker { | 
					
						
							|  |  |  | 		eventName = event.ObjectRemovedDeleteMarkerCreated | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-03-22 01:21:35 +08:00
										 |  |  | 	if lcEvent.Action.DeleteAll() { | 
					
						
							|  |  |  | 		eventName = event.ObjectRemovedDeleteAllVersions | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	// Notify object deleted event.
 | 
					
						
							|  |  |  | 	sendEvent(eventArgs{ | 
					
						
							| 
									
										
										
										
											2020-10-17 12:22:12 +08:00
										 |  |  | 		EventName:  eventName, | 
					
						
							| 
									
										
										
										
											2024-02-15 00:07:58 +08:00
										 |  |  | 		BucketName: dobj.Bucket, | 
					
						
							|  |  |  | 		Object:     dobj, | 
					
						
							| 
									
										
										
										
											2023-04-07 01:20:53 +08:00
										 |  |  | 		UserAgent:  "Internal: [ILM-Expiry]", | 
					
						
							|  |  |  | 		Host:       globalLocalNodeName, | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Apply object, object version, restored object or restored object version action on the given object
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | func applyExpiryRule(event lifecycle.Event, src lcEventSrc, obj ObjectInfo) bool { | 
					
						
							|  |  |  | 	globalExpiryState.enqueueByDays(obj, event, src) | 
					
						
							| 
									
										
										
										
											2021-08-28 08:06:47 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-06 06:15:53 +08:00
										 |  |  | // Perform actions (removal or transitioning of objects), return true the action is successfully performed
 | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | func applyLifecycleAction(event lifecycle.Event, src lcEventSrc, obj ObjectInfo) (success bool) { | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  | 	switch action := event.Action; action { | 
					
						
							|  |  |  | 	case lifecycle.DeleteVersionAction, lifecycle.DeleteAction, | 
					
						
							| 
									
										
										
										
											2023-07-09 06:42:10 +08:00
										 |  |  | 		lifecycle.DeleteRestoredAction, lifecycle.DeleteRestoredVersionAction, | 
					
						
							|  |  |  | 		lifecycle.DeleteAllVersionsAction: | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | 		success = applyExpiryRule(event, src, obj) | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 	case lifecycle.TransitionAction, lifecycle.TransitionVersionAction: | 
					
						
							| 
									
										
										
										
											2023-05-23 06:28:56 +08:00
										 |  |  | 		success = applyTransitionRule(event, src, obj) | 
					
						
							| 
									
										
										
										
											2021-02-02 01:52:11 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // objectPath returns the prefix and object name.
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | func (i *scannerItem) objectPath() string { | 
					
						
							| 
									
										
										
										
											2023-09-17 10:08:59 +08:00
										 |  |  | 	return pathJoin(i.prefix, i.objectName) | 
					
						
							| 
									
										
										
										
											2020-06-13 01:28:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | // healReplication will heal a scanned item that has failed replication.
 | 
					
						
							| 
									
										
										
										
											2021-02-27 07:11:42 +08:00
										 |  |  | func (i *scannerItem) healReplication(ctx context.Context, o ObjectLayer, oi ObjectInfo, sizeS *sizeSummary) { | 
					
						
							| 
									
										
										
										
											2022-08-10 06:00:24 +08:00
										 |  |  | 	if oi.VersionID == "" { | 
					
						
							| 
									
										
										
										
											2022-05-07 10:05:28 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-08-10 06:00:24 +08:00
										 |  |  | 	if i.replication.Config == nil { | 
					
						
							| 
									
										
										
										
											2021-06-02 10:59:11 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-13 14:51:33 +08:00
										 |  |  | 	roi := queueReplicationHeal(ctx, oi.Bucket, oi, i.replication, 0) | 
					
						
							| 
									
										
										
										
											2022-08-10 06:00:24 +08:00
										 |  |  | 	if oi.DeleteMarker || !oi.VersionPurgeStatus.Empty() { | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 	if sizeS.replTargetStats == nil && len(roi.TargetStatuses) > 0 { | 
					
						
							|  |  |  | 		sizeS.replTargetStats = make(map[string]replTargetSizeSummary) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for arn, tgtStatus := range roi.TargetStatuses { | 
					
						
							|  |  |  | 		tgtSizeS, ok := sizeS.replTargetStats[arn] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			tgtSizeS = replTargetSizeSummary{} | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 		switch tgtStatus { | 
					
						
							|  |  |  | 		case replication.Pending: | 
					
						
							|  |  |  | 			tgtSizeS.pendingCount++ | 
					
						
							|  |  |  | 			tgtSizeS.pendingSize += oi.Size | 
					
						
							|  |  |  | 			sizeS.pendingCount++ | 
					
						
							|  |  |  | 			sizeS.pendingSize += oi.Size | 
					
						
							|  |  |  | 		case replication.Failed: | 
					
						
							|  |  |  | 			tgtSizeS.failedSize += oi.Size | 
					
						
							|  |  |  | 			tgtSizeS.failedCount++ | 
					
						
							|  |  |  | 			sizeS.failedSize += oi.Size | 
					
						
							|  |  |  | 			sizeS.failedCount++ | 
					
						
							|  |  |  | 		case replication.Completed, replication.CompletedLegacy: | 
					
						
							|  |  |  | 			tgtSizeS.replicatedSize += oi.Size | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 			tgtSizeS.replicatedCount++ | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 			sizeS.replicatedSize += oi.Size | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 			sizeS.replicatedCount++ | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-04-12 04:25:32 +08:00
										 |  |  | 		sizeS.replTargetStats[arn] = tgtSizeS | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 00:56:10 +08:00
										 |  |  | 	if oi.ReplicationStatus == replication.Replica { | 
					
						
							| 
									
										
										
										
											2020-12-29 02:31:00 +08:00
										 |  |  | 		sizeS.replicaSize += oi.Size | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 		sizeS.replicaCount++ | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-20 10:43:58 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | type dynamicSleeper struct { | 
					
						
							|  |  |  | 	mu sync.RWMutex | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Sleep factor
 | 
					
						
							|  |  |  | 	factor float64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// maximum sleep cap,
 | 
					
						
							|  |  |  | 	// set to <= 0 to disable.
 | 
					
						
							|  |  |  | 	maxSleep time.Duration | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Don't sleep at all, if time taken is below this value.
 | 
					
						
							|  |  |  | 	// This is to avoid too small costly sleeps.
 | 
					
						
							|  |  |  | 	minSleep time.Duration | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// cycle will be closed
 | 
					
						
							|  |  |  | 	cycle chan struct{} | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// isScanner should be set when this is used by the scanner
 | 
					
						
							|  |  |  | 	// to record metrics.
 | 
					
						
							|  |  |  | 	isScanner bool | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newDynamicSleeper
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | func newDynamicSleeper(factor float64, maxWait time.Duration, isScanner bool) *dynamicSleeper { | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	return &dynamicSleeper{ | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		factor:    factor, | 
					
						
							|  |  |  | 		cycle:     make(chan struct{}), | 
					
						
							|  |  |  | 		maxSleep:  maxWait, | 
					
						
							|  |  |  | 		minSleep:  100 * time.Microsecond, | 
					
						
							|  |  |  | 		isScanner: isScanner, | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Timer returns a timer that has started.
 | 
					
						
							|  |  |  | // When the returned function is called it will wait.
 | 
					
						
							|  |  |  | func (d *dynamicSleeper) Timer(ctx context.Context) func() { | 
					
						
							|  |  |  | 	t := time.Now() | 
					
						
							|  |  |  | 	return func() { | 
					
						
							|  |  |  | 		doneAt := time.Now() | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			// Grab current values
 | 
					
						
							|  |  |  | 			d.mu.RLock() | 
					
						
							|  |  |  | 			minWait, maxWait := d.minSleep, d.maxSleep | 
					
						
							|  |  |  | 			factor := d.factor | 
					
						
							|  |  |  | 			cycle := d.cycle | 
					
						
							|  |  |  | 			d.mu.RUnlock() | 
					
						
							|  |  |  | 			elapsed := doneAt.Sub(t) | 
					
						
							|  |  |  | 			// Don't sleep for really small amount of time
 | 
					
						
							|  |  |  | 			wantSleep := time.Duration(float64(elapsed) * factor) | 
					
						
							|  |  |  | 			if wantSleep <= minWait { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if maxWait > 0 && wantSleep > maxWait { | 
					
						
							|  |  |  | 				wantSleep = maxWait | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			timer := time.NewTimer(wantSleep) | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ctx.Done(): | 
					
						
							|  |  |  | 				if !timer.Stop() { | 
					
						
							|  |  |  | 					<-timer.C | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-05-19 04:45:05 +08:00
										 |  |  | 				if d.isScanner { | 
					
						
							|  |  |  | 					globalScannerMetrics.incTime(scannerMetricYield, wantSleep) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			case <-timer.C: | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 				if d.isScanner { | 
					
						
							|  |  |  | 					globalScannerMetrics.incTime(scannerMetricYield, wantSleep) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			case <-cycle: | 
					
						
							|  |  |  | 				if !timer.Stop() { | 
					
						
							|  |  |  | 					// We expired.
 | 
					
						
							|  |  |  | 					<-timer.C | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 					if d.isScanner { | 
					
						
							|  |  |  | 						globalScannerMetrics.incTime(scannerMetricYield, wantSleep) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Sleep sleeps the specified time multiplied by the sleep factor.
 | 
					
						
							|  |  |  | // If the factor is updated the sleep will be done again with the new factor.
 | 
					
						
							|  |  |  | func (d *dynamicSleeper) Sleep(ctx context.Context, base time.Duration) { | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// Grab current values
 | 
					
						
							|  |  |  | 		d.mu.RLock() | 
					
						
							|  |  |  | 		minWait, maxWait := d.minSleep, d.maxSleep | 
					
						
							|  |  |  | 		factor := d.factor | 
					
						
							|  |  |  | 		cycle := d.cycle | 
					
						
							|  |  |  | 		d.mu.RUnlock() | 
					
						
							|  |  |  | 		// Don't sleep for really small amount of time
 | 
					
						
							|  |  |  | 		wantSleep := time.Duration(float64(base) * factor) | 
					
						
							|  |  |  | 		if wantSleep <= minWait { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if maxWait > 0 && wantSleep > maxWait { | 
					
						
							|  |  |  | 			wantSleep = maxWait | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		timer := time.NewTimer(wantSleep) | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			if !timer.Stop() { | 
					
						
							|  |  |  | 				<-timer.C | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 				if d.isScanner { | 
					
						
							|  |  |  | 					globalScannerMetrics.incTime(scannerMetricYield, wantSleep) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		case <-timer.C: | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			if d.isScanner { | 
					
						
							|  |  |  | 				globalScannerMetrics.incTime(scannerMetricYield, wantSleep) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		case <-cycle: | 
					
						
							|  |  |  | 			if !timer.Stop() { | 
					
						
							|  |  |  | 				// We expired.
 | 
					
						
							|  |  |  | 				<-timer.C | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 				if d.isScanner { | 
					
						
							|  |  |  | 					globalScannerMetrics.incTime(scannerMetricYield, wantSleep) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Update the current settings and cycle all waiting.
 | 
					
						
							| 
									
										
										
										
											2024-01-18 15:03:17 +08:00
										 |  |  | // Parameters are the same as in the constructor.
 | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | func (d *dynamicSleeper) Update(factor float64, maxWait time.Duration) error { | 
					
						
							|  |  |  | 	d.mu.Lock() | 
					
						
							|  |  |  | 	defer d.mu.Unlock() | 
					
						
							|  |  |  | 	if math.Abs(d.factor-factor) < 1e-10 && d.maxSleep == maxWait { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Update values and cycle waiting.
 | 
					
						
							| 
									
										
										
										
											2024-01-29 02:04:17 +08:00
										 |  |  | 	xioutil.SafeClose(d.cycle) | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	d.factor = factor | 
					
						
							|  |  |  | 	d.maxSleep = maxWait | 
					
						
							|  |  |  | 	d.cycle = make(chan struct{}) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-24 00:51:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 10:32:07 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2021-07-02 05:02:44 +08:00
										 |  |  | 	// ILMExpiry - audit trail for ILM expiry
 | 
					
						
							|  |  |  | 	ILMExpiry = "ilm:expiry" | 
					
						
							|  |  |  | 	// ILMFreeVersionDelete - audit trail for ILM free-version delete
 | 
					
						
							|  |  |  | 	ILMFreeVersionDelete = "ilm:free-version-delete" | 
					
						
							| 
									
										
										
										
											2021-07-31 03:45:25 +08:00
										 |  |  | 	// ILMTransition - audit trail for ILM transitioning.
 | 
					
						
							|  |  |  | 	ILMTransition = " ilm:transition" | 
					
						
							| 
									
										
										
										
											2021-07-01 10:32:07 +08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-06-29 14:58:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  | func auditLogLifecycle(ctx context.Context, oi ObjectInfo, event string, tags map[string]interface{}, traceFn func(event string)) { | 
					
						
							| 
									
										
										
										
											2021-07-01 10:32:07 +08:00
										 |  |  | 	var apiName string | 
					
						
							| 
									
										
										
										
											2022-07-13 01:43:32 +08:00
										 |  |  | 	switch event { | 
					
						
							| 
									
										
										
										
											2021-07-02 05:02:44 +08:00
										 |  |  | 	case ILMExpiry: | 
					
						
							|  |  |  | 		apiName = "ILMExpiry" | 
					
						
							|  |  |  | 	case ILMFreeVersionDelete: | 
					
						
							|  |  |  | 		apiName = "ILMFreeVersionDelete" | 
					
						
							| 
									
										
										
										
											2021-07-31 03:45:25 +08:00
										 |  |  | 	case ILMTransition: | 
					
						
							|  |  |  | 		apiName = "ILMTransition" | 
					
						
							| 
									
										
										
										
											2021-07-01 10:32:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-25 02:35:07 +08:00
										 |  |  | 	auditLogInternal(ctx, AuditLogOptions{ | 
					
						
							| 
									
										
										
										
											2022-07-13 01:43:32 +08:00
										 |  |  | 		Event:     event, | 
					
						
							| 
									
										
										
										
											2021-07-01 10:32:07 +08:00
										 |  |  | 		APIName:   apiName, | 
					
						
							| 
									
										
										
										
											2022-10-25 02:35:07 +08:00
										 |  |  | 		Bucket:    oi.Bucket, | 
					
						
							|  |  |  | 		Object:    oi.Name, | 
					
						
							| 
									
										
										
										
											2021-07-02 05:02:44 +08:00
										 |  |  | 		VersionID: oi.VersionID, | 
					
						
							| 
									
										
										
										
											2023-04-27 08:49:00 +08:00
										 |  |  | 		Tags:      tags, | 
					
						
							| 
									
										
										
										
											2021-06-29 14:58:08 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-04-12 10:22:32 +08:00
										 |  |  | 	traceFn(event) | 
					
						
							| 
									
										
										
										
											2021-04-24 00:51:12 +08:00
										 |  |  | } |