@ -22,9 +22,7 @@ import (
"fmt"
"fmt"
"time"
"time"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/env"
"github.com/minio/minio/pkg/event"
"github.com/minio/minio/pkg/event"
"github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/madmin"
)
)
@ -120,68 +118,17 @@ func enforceBucketQuota(ctx context.Context, bucket string, size int64) error {
return globalBucketQuotaSys . check ( ctx , bucket , size )
return globalBucketQuotaSys . check ( ctx , bucket , size )
}
}
const (
bgQuotaInterval = 1 * time . Hour
)
// initQuotaEnforcement starts the routine that deletes objects in bucket
// that exceeds the FIFO quota
func initQuotaEnforcement ( ctx context . Context , objAPI ObjectLayer ) {
if env . Get ( envDataUsageCrawlConf , config . EnableOn ) == config . EnableOn {
go startBucketQuotaEnforcement ( ctx , objAPI )
}
}
func startBucketQuotaEnforcement ( ctx context . Context , objAPI ObjectLayer ) {
for {
select {
case <- ctx . Done ( ) :
return
case <- time . NewTimer ( bgQuotaInterval ) . C :
enforceFIFOQuota ( ctx , objAPI )
}
}
}
// enforceFIFOQuota deletes objects in FIFO order until sufficient objects
// enforceFIFOQuota deletes objects in FIFO order until sufficient objects
// have been deleted so as to bring bucket usage within quota
// have been deleted so as to bring bucket usage within quota.
func enforceFIFOQuota ( ctx context . Context , objectAPI ObjectLayer ) {
func enforceFIFOQuotaBucket ( ctx context . Context , objectAPI ObjectLayer , bucket string , bui BucketUsageInfo ) {
// Turn off quota enforcement if data usage info is unavailable.
if env . Get ( envDataUsageCrawlConf , config . EnableOn ) == config . EnableOff {
return
}
buckets , err := objectAPI . ListBuckets ( ctx )
if err != nil {
logger . LogIf ( ctx , err )
return
}
dataUsageInfo , err := loadDataUsageFromBackend ( ctx , objectAPI )
if err != nil {
logger . LogIf ( ctx , err )
return
}
for _ , binfo := range buckets {
bucket := binfo . Name
bui , ok := dataUsageInfo . BucketsUsage [ bucket ]
if ! ok {
// bucket doesn't exist anymore, or we
// do not have any information to proceed.
continue
}
// Check if the current bucket has quota restrictions, if not skip it
// Check if the current bucket has quota restrictions, if not skip it
cfg , err := globalBucketQuotaSys . Get ( bucket )
cfg , err := globalBucketQuotaSys . Get ( bucket )
if err != nil {
if err != nil {
continue
return
}
}
if cfg . Type != madmin . FIFOQuota {
if cfg . Type != madmin . FIFOQuota {
continue
return
}
}
var toFree uint64
var toFree uint64
@ -189,8 +136,8 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) {
toFree = bui . Size - cfg . Quota
toFree = bui . Size - cfg . Quota
}
}
if toFree = = 0 {
if toFree < = 0 {
continue
return
}
}
// Allocate new results channel to receive ObjectInfo.
// Allocate new results channel to receive ObjectInfo.
@ -201,7 +148,7 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) {
// Walk through all objects
// Walk through all objects
if err := objectAPI . Walk ( ctx , bucket , "" , objInfoCh , ObjectOptions { WalkVersions : versioned } ) ; err != nil {
if err := objectAPI . Walk ( ctx , bucket , "" , objInfoCh , ObjectOptions { WalkVersions : versioned } ) ; err != nil {
logger . LogIf ( ctx , err )
logger . LogIf ( ctx , err )
continue
return
}
}
// reuse the fileScorer used by disk cache to score entries by
// reuse the fileScorer used by disk cache to score entries by
@ -211,7 +158,7 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) {
scorer , err := newFileScorer ( toFree , time . Now ( ) . Unix ( ) , 1 )
scorer , err := newFileScorer ( toFree , time . Now ( ) . Unix ( ) , 1 )
if err != nil {
if err != nil {
logger . LogIf ( ctx , err )
logger . LogIf ( ctx , err )
continue
return
}
}
rcfg , _ := globalBucketObjectLockSys . Get ( bucket )
rcfg , _ := globalBucketObjectLockSys . Get ( bucket )
@ -228,6 +175,21 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) {
scorer . addFileWithObjInfo ( obj , 1 )
scorer . addFileWithObjInfo ( obj , 1 )
}
}
// If we saw less than quota we are good.
if scorer . seenBytes <= cfg . Quota {
return
}
// Calculate how much we want to delete now.
toFreeNow := scorer . seenBytes - cfg . Quota
// We were less over quota than we thought. Adjust so we delete less.
// If we are more over, leave it for the next run to pick up.
if toFreeNow < toFree {
if ! scorer . adjustSaveBytes ( int64 ( toFreeNow ) - int64 ( toFree ) ) {
// We got below or at quota.
return
}
}
var objects [ ] ObjectToDelete
var objects [ ] ObjectToDelete
numKeys := len ( scorer . fileObjInfos ( ) )
numKeys := len ( scorer . fileObjInfos ( ) )
for i , obj := range scorer . fileObjInfos ( ) {
for i , obj := range scorer . fileObjInfos ( ) {
@ -264,5 +226,4 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) {
}
}
objects = nil
objects = nil
}
}
}
}
}