feat: allow retaining parity SLA to be configurable (#19260)

at scale customers might start with failed drives,
causing skew in the overall usage ratio per EC set.

make this configurable such that customers can turn
this off as needed depending on how comfortable they
are.
This commit is contained in:
Harshavardhana 2024-03-14 03:38:33 -07:00 committed by GitHub
parent 5c32058ff3
commit ce1c640ce0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 41 deletions

View File

@ -401,17 +401,13 @@ func (er erasureObjects) newMultipartUpload(ctx context.Context, bucket string,
parityDrives = er.defaultParityCount parityDrives = er.defaultParityCount
} }
if globalStorageClass.AvailabilityOptimized() {
// If we have offline disks upgrade the number of erasure codes for this object. // If we have offline disks upgrade the number of erasure codes for this object.
parityOrig := parityDrives parityOrig := parityDrives
var offlineDrives int var offlineDrives int
for _, disk := range onlineDisks { for _, disk := range onlineDisks {
if disk == nil { if disk == nil || !disk.IsOnline() {
parityDrives++
offlineDrives++
continue
}
if !disk.IsOnline() {
parityDrives++ parityDrives++
offlineDrives++ offlineDrives++
continue continue
@ -432,6 +428,7 @@ func (er erasureObjects) newMultipartUpload(ctx context.Context, bucket string,
if parityOrig != parityDrives { if parityOrig != parityDrives {
userDefined[minIOErasureUpgraded] = strconv.Itoa(parityOrig) + "->" + strconv.Itoa(parityDrives) userDefined[minIOErasureUpgraded] = strconv.Itoa(parityOrig) + "->" + strconv.Itoa(parityDrives)
} }
}
dataDrives := len(onlineDisks) - parityDrives dataDrives := len(onlineDisks) - parityDrives

View File

@ -1297,25 +1297,21 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st
storageDisks := er.getDisks() storageDisks := er.getDisks()
parityDrives := len(storageDisks) / 2
if !opts.MaxParity {
// Get parity and data drive count based on storage class metadata // Get parity and data drive count based on storage class metadata
parityDrives = globalStorageClass.GetParityForSC(userDefined[xhttp.AmzStorageClass]) parityDrives := globalStorageClass.GetParityForSC(userDefined[xhttp.AmzStorageClass])
if parityDrives < 0 { if parityDrives < 0 {
parityDrives = er.defaultParityCount parityDrives = er.defaultParityCount
} }
if opts.MaxParity {
parityDrives = len(storageDisks) / 2
}
if !opts.MaxParity && globalStorageClass.AvailabilityOptimized() {
// If we have offline disks upgrade the number of erasure codes for this object. // If we have offline disks upgrade the number of erasure codes for this object.
parityOrig := parityDrives parityOrig := parityDrives
var offlineDrives int var offlineDrives int
for _, disk := range storageDisks { for _, disk := range storageDisks {
if disk == nil { if disk == nil || !disk.IsOnline() {
parityDrives++
offlineDrives++
continue
}
if !disk.IsOnline() {
parityDrives++ parityDrives++
offlineDrives++ offlineDrives++
continue continue

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015-2021 MinIO, Inc. // Copyright (c) 2015-2024 MinIO, Inc.
// //
// This file is part of MinIO Object Storage stack // This file is part of MinIO Object Storage stack
// //
@ -38,6 +38,12 @@ var (
Optional: true, Optional: true,
Type: "string", Type: "string",
}, },
config.HelpKV{
Key: ClassOptimize,
Description: `optimize parity calculation for standard storage class, set 'capacity' for capacity optimized (no additional parity)` + defaultHelpPostfix(ClassOptimize),
Optional: true,
Type: "string",
},
config.HelpKV{ config.HelpKV{
Key: config.Comment, Key: config.Comment,
Description: config.DefaultComment, Description: config.DefaultComment,

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015-2021 MinIO, Inc. // Copyright (c) 2015-2024 MinIO, Inc.
// //
// This file is part of MinIO Object Storage stack // This file is part of MinIO Object Storage stack
// //
@ -40,11 +40,14 @@ const (
const ( const (
ClassStandard = "standard" ClassStandard = "standard"
ClassRRS = "rrs" ClassRRS = "rrs"
ClassOptimize = "optimize"
// Reduced redundancy storage class environment variable // Reduced redundancy storage class environment variable
RRSEnv = "MINIO_STORAGE_CLASS_RRS" RRSEnv = "MINIO_STORAGE_CLASS_RRS"
// Standard storage class environment variable // Standard storage class environment variable
StandardEnv = "MINIO_STORAGE_CLASS_STANDARD" StandardEnv = "MINIO_STORAGE_CLASS_STANDARD"
// Optimize storage class environment variable
OptimizeEnv = "MINIO_STORAGE_CLASS_OPTIMIZE"
// Supported storage class scheme is EC // Supported storage class scheme is EC
schemePrefix = "EC" schemePrefix = "EC"
@ -67,6 +70,10 @@ var (
Key: ClassRRS, Key: ClassRRS,
Value: "EC:1", Value: "EC:1",
}, },
config.KV{
Key: ClassOptimize,
Value: "availability",
},
} }
) )
@ -82,6 +89,7 @@ var ConfigLock sync.RWMutex
type Config struct { type Config struct {
Standard StorageClass `json:"standard"` Standard StorageClass `json:"standard"`
RRS StorageClass `json:"rrs"` RRS StorageClass `json:"rrs"`
Optimize string `json:"optimize"`
initialized bool initialized bool
} }
@ -245,12 +253,40 @@ func (sCfg *Config) GetParityForSC(sc string) (parity int) {
} }
} }
// CapacityOptimized - returns true if the storage-class is capacity optimized
// meaning we will not use additional parities when drives are offline.
//
// Default is "availability" optimized, unless this is configured.
func (sCfg *Config) CapacityOptimized() bool {
ConfigLock.RLock()
defer ConfigLock.RUnlock()
if !sCfg.initialized {
return false
}
return sCfg.Optimize == "capacity"
}
// AvailabilityOptimized - returns true if the storage-class is availability
// optimized, meaning we will use additional parities when drives are offline
// to retain parity SLA.
//
// Default is "availability" optimized.
func (sCfg *Config) AvailabilityOptimized() bool {
ConfigLock.RLock()
defer ConfigLock.RUnlock()
if !sCfg.initialized {
return true
}
return sCfg.Optimize == "availability" || sCfg.Optimize == ""
}
// Update update storage-class with new config // Update update storage-class with new config
func (sCfg *Config) Update(newCfg Config) { func (sCfg *Config) Update(newCfg Config) {
ConfigLock.Lock() ConfigLock.Lock()
defer ConfigLock.Unlock() defer ConfigLock.Unlock()
sCfg.RRS = newCfg.RRS sCfg.RRS = newCfg.RRS
sCfg.Standard = newCfg.Standard sCfg.Standard = newCfg.Standard
sCfg.Optimize = newCfg.Optimize
sCfg.initialized = true sCfg.initialized = true
} }
@ -320,5 +356,6 @@ func LookupConfig(kvs config.KVS, setDriveCount int) (cfg Config, err error) {
} }
cfg.initialized = true cfg.initialized = true
cfg.Optimize = env.Get(OptimizeEnv, kvs.Get(ClassOptimize))
return cfg, nil return cfg, nil
} }