mirror of
https://github.com/minio/minio.git
synced 2025-11-07 12:52:58 -05:00
migrate all bucket metadata into a single file (#9586)
this is a major overhaul by migrating off all bucket metadata related configs into a single object '.metadata.bin' this allows us for faster bootups across 1000's of buckets and as well as keeps the code simple enough for future work and additions. Additionally also fixes #9396, #9394
This commit is contained in:
@@ -19,12 +19,9 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"math"
|
||||
"net/http"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/auth"
|
||||
@@ -34,50 +31,36 @@ import (
|
||||
|
||||
// BucketObjectLockSys - map of bucket and retention configuration.
|
||||
type BucketObjectLockSys struct {
|
||||
sync.RWMutex
|
||||
retentionMap map[string]*objectlock.Retention
|
||||
}
|
||||
|
||||
// Set - set retention configuration.
|
||||
func (sys *BucketObjectLockSys) Set(bucketName string, retention *objectlock.Retention) {
|
||||
if globalIsGateway {
|
||||
// no-op
|
||||
return
|
||||
}
|
||||
|
||||
sys.Lock()
|
||||
sys.retentionMap[bucketName] = retention
|
||||
sys.Unlock()
|
||||
retentionMap map[string]objectlock.Retention
|
||||
}
|
||||
|
||||
// Get - Get retention configuration.
|
||||
func (sys *BucketObjectLockSys) Get(bucketName string) (r *objectlock.Retention, ok bool) {
|
||||
func (sys *BucketObjectLockSys) Get(bucketName string) (r objectlock.Retention, err error) {
|
||||
if globalIsGateway {
|
||||
// When gateway is enabled, no cached value
|
||||
// is used to validate bucket object lock configuration
|
||||
objAPI := newObjectLayerWithoutSafeModeFn()
|
||||
if objAPI == nil {
|
||||
return
|
||||
return r, errServerNotInitialized
|
||||
}
|
||||
|
||||
lc, err := objAPI.GetBucketObjectLockConfig(GlobalContext, bucketName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return lc.ToRetention(), true
|
||||
return r, nil
|
||||
}
|
||||
|
||||
sys.RLock()
|
||||
defer sys.RUnlock()
|
||||
r, ok = sys.retentionMap[bucketName]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// Remove - removes retention sysuration.
|
||||
func (sys *BucketObjectLockSys) Remove(bucketName string) {
|
||||
sys.Lock()
|
||||
delete(sys.retentionMap, bucketName)
|
||||
sys.Unlock()
|
||||
r, ok := sys.retentionMap[bucketName]
|
||||
if ok {
|
||||
return r, nil
|
||||
}
|
||||
configData, err := globalBucketMetadataSys.GetConfig(bucketName, objectLockConfig)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
return r, nil
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
config, err := objectlock.ParseObjectLockConfig(bytes.NewReader(configData))
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return config.ToRetention(), nil
|
||||
}
|
||||
|
||||
// Similar to enforceRetentionBypassForDelete but for WebUI
|
||||
@@ -345,11 +328,16 @@ func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, obj
|
||||
retentionRequested := objectlock.IsObjectLockRetentionRequested(r.Header)
|
||||
legalHoldRequested := objectlock.IsObjectLockLegalHoldRequested(r.Header)
|
||||
|
||||
retentionCfg, isWORMBucket := globalBucketObjectLockSys.Get(bucket)
|
||||
if !isWORMBucket {
|
||||
retentionCfg, err := globalBucketObjectLockSys.Get(bucket)
|
||||
if err != nil {
|
||||
return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration
|
||||
}
|
||||
|
||||
if !retentionCfg.LockEnabled {
|
||||
if legalHoldRequested || retentionRequested {
|
||||
return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration
|
||||
}
|
||||
|
||||
// If this not a WORM enabled bucket, we should return right here.
|
||||
return mode, retainDate, legalHold, ErrNone
|
||||
}
|
||||
@@ -406,7 +394,7 @@ func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, obj
|
||||
return rMode, rDate, legalHold, ErrNone
|
||||
}
|
||||
|
||||
if !retentionRequested && isWORMBucket {
|
||||
if !retentionRequested && retentionCfg.Validity > 0 {
|
||||
if retentionPermErr != ErrNone {
|
||||
return mode, retainDate, legalHold, retentionPermErr
|
||||
}
|
||||
@@ -419,7 +407,7 @@ func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, obj
|
||||
if objExists && retainDate.After(t) {
|
||||
return mode, retainDate, legalHold, ErrObjectLocked
|
||||
}
|
||||
if !legalHoldRequested && !retentionCfg.IsEmpty() {
|
||||
if !legalHoldRequested {
|
||||
// inherit retention from bucket configuration
|
||||
return retentionCfg.Mode, objectlock.RetentionDate{Time: t.Add(retentionCfg.Validity)}, legalHold, ErrNone
|
||||
}
|
||||
@@ -428,54 +416,10 @@ func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, obj
|
||||
return mode, retainDate, legalHold, ErrNone
|
||||
}
|
||||
|
||||
func readBucketObjectLockConfig(ctx context.Context, objAPI ObjectLayer, bucket string) (*objectlock.Config, error) {
|
||||
meta, err := loadBucketMetadata(ctx, objAPI, bucket)
|
||||
if err != nil && err != errMetaDataConverted {
|
||||
return nil, toObjectErr(err, bucket)
|
||||
}
|
||||
if !meta.LockEnabled {
|
||||
return nil, BucketObjectLockConfigNotFound{Bucket: bucket}
|
||||
}
|
||||
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
|
||||
configData, err := readConfig(ctx, objAPI, configFile)
|
||||
if err != nil {
|
||||
if err != errConfigNotFound {
|
||||
return nil, toObjectErr(err, bucket)
|
||||
}
|
||||
return objectlock.NewObjectLockConfig(), nil
|
||||
}
|
||||
cfg, err := objectlock.ParseObjectLockConfig(bytes.NewReader(configData))
|
||||
if err != nil {
|
||||
return nil, toObjectErr(err, bucket)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func saveBucketObjectLockConfig(ctx context.Context, objAPI ObjectLayer, bucket string, config *objectlock.Config) error {
|
||||
meta, err := loadBucketMetadata(ctx, objAPI, bucket)
|
||||
if err != nil && err != errMetaDataConverted {
|
||||
return toObjectErr(err, bucket)
|
||||
}
|
||||
if !meta.LockEnabled {
|
||||
return BucketObjectLockConfigNotFound{Bucket: bucket}
|
||||
}
|
||||
|
||||
data, err := xml.Marshal(config)
|
||||
if err != nil {
|
||||
return toObjectErr(err, bucket)
|
||||
}
|
||||
|
||||
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
|
||||
if err = saveConfig(ctx, objAPI, configFile, data); err != nil {
|
||||
return toObjectErr(err, bucket)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewBucketObjectLockSys returns initialized BucketObjectLockSys
|
||||
func NewBucketObjectLockSys() *BucketObjectLockSys {
|
||||
return &BucketObjectLockSys{
|
||||
retentionMap: make(map[string]*objectlock.Retention),
|
||||
retentionMap: make(map[string]objectlock.Retention),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,38 +442,22 @@ func (sys *BucketObjectLockSys) Init(buckets []BucketInfo, objAPI ObjectLayer) e
|
||||
func (sys *BucketObjectLockSys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
|
||||
for _, bucket := range buckets {
|
||||
ctx := logger.SetReqInfo(GlobalContext, &logger.ReqInfo{BucketName: bucket.Name})
|
||||
meta, err := loadBucketMetadata(ctx, objAPI, bucket.Name)
|
||||
if err != nil {
|
||||
if err != errMetaDataConverted {
|
||||
return err
|
||||
}
|
||||
|
||||
meta.Created = bucket.Created
|
||||
logger.LogIf(ctx, meta.save(ctx, objAPI))
|
||||
}
|
||||
if !meta.LockEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
configFile := path.Join(bucketConfigPrefix, bucket.Name, objectLockConfig)
|
||||
configData, err := readConfig(ctx, objAPI, configFile)
|
||||
configData, err := globalBucketMetadataSys.GetConfig(bucket.Name, objectLockConfig)
|
||||
if err != nil {
|
||||
if errors.Is(err, errConfigNotFound) {
|
||||
globalBucketObjectLockSys.Set(bucket.Name, &objectlock.Retention{})
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
retention := objectlock.Retention{}
|
||||
config, err := objectlock.ParseObjectLockConfig(bytes.NewReader(configData))
|
||||
if err != nil {
|
||||
return err
|
||||
logger.LogIf(ctx, err)
|
||||
sys.retentionMap[bucket.Name] = retention
|
||||
continue
|
||||
}
|
||||
retention := &objectlock.Retention{}
|
||||
if config.Rule != nil {
|
||||
retention = config.ToRetention()
|
||||
}
|
||||
globalBucketObjectLockSys.Set(bucket.Name, retention)
|
||||
sys.retentionMap[bucket.Name] = config.ToRetention()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user