| 
									
										
										
										
											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-10-29 00:18:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	"runtime/debug" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // localMetacacheMgr is the *local* manager for this peer.
 | 
					
						
							|  |  |  | // It should never be used directly since buckets are
 | 
					
						
							|  |  |  | // distributed deterministically.
 | 
					
						
							|  |  |  | // Therefore no cluster locks are required.
 | 
					
						
							|  |  |  | var localMetacacheMgr = &metacacheManager{ | 
					
						
							|  |  |  | 	buckets: make(map[string]*bucketMetacache), | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	trash:   make(map[string]metacache), | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type metacacheManager struct { | 
					
						
							|  |  |  | 	mu      sync.RWMutex | 
					
						
							|  |  |  | 	init    sync.Once | 
					
						
							|  |  |  | 	buckets map[string]*bucketMetacache | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	trash   map[string]metacache // Recently deleted lists.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-09 03:36:16 +08:00
										 |  |  | const metacacheMaxEntries = 5000 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // initManager will start async saving the cache.
 | 
					
						
							|  |  |  | func (m *metacacheManager) initManager() { | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	// Add a transient bucket.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	// Start saver when object layer is ready.
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 		objAPI := newObjectLayerFn() | 
					
						
							|  |  |  | 		for objAPI == nil { | 
					
						
							|  |  |  | 			time.Sleep(time.Second) | 
					
						
							|  |  |  | 			objAPI = newObjectLayerFn() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-02 03:23:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-31 01:58:37 +08:00
										 |  |  | 		if globalIsGateway { | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		t := time.NewTicker(time.Minute) | 
					
						
							| 
									
										
										
										
											2021-02-06 11:23:48 +08:00
										 |  |  | 		defer t.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 		var exit bool | 
					
						
							|  |  |  | 		for !exit { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-t.C: | 
					
						
							|  |  |  | 			case <-GlobalContext.Done(): | 
					
						
							|  |  |  | 				exit = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			m.mu.RLock() | 
					
						
							|  |  |  | 			for _, v := range m.buckets { | 
					
						
							|  |  |  | 				if !exit { | 
					
						
							|  |  |  | 					v.cleanup() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			m.mu.RUnlock() | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 			m.mu.Lock() | 
					
						
							|  |  |  | 			for k, v := range m.trash { | 
					
						
							|  |  |  | 				if time.Since(v.lastUpdate) > metacacheMaxRunningAge { | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 					v.delete(context.Background()) | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 					delete(m.trash, k) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			m.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | // updateCacheEntry will update non-transient state.
 | 
					
						
							|  |  |  | func (m *metacacheManager) updateCacheEntry(update metacache) (metacache, error) { | 
					
						
							|  |  |  | 	m.mu.RLock() | 
					
						
							|  |  |  | 	if meta, ok := m.trash[update.id]; ok { | 
					
						
							|  |  |  | 		m.mu.RUnlock() | 
					
						
							|  |  |  | 		return meta, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b, ok := m.buckets[update.bucket] | 
					
						
							| 
									
										
										
										
											2021-02-12 02:22:03 +08:00
										 |  |  | 	m.mu.RUnlock() | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	if ok { | 
					
						
							|  |  |  | 		return b.updateCacheEntry(update) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-12 02:22:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	// We should have either a trashed bucket or this
 | 
					
						
							|  |  |  | 	return metacache{}, errVolumeNotFound | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | // getBucket will get a bucket metacache or load it from disk if needed.
 | 
					
						
							|  |  |  | func (m *metacacheManager) getBucket(ctx context.Context, bucket string) *bucketMetacache { | 
					
						
							|  |  |  | 	m.init.Do(m.initManager) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	// Return a transient bucket for invalid or system buckets.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	m.mu.RLock() | 
					
						
							|  |  |  | 	b, ok := m.buckets[bucket] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							| 
									
										
										
										
											2021-09-09 02:06:45 +08:00
										 |  |  | 		m.mu.RUnlock() | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 		if b.bucket != bucket { | 
					
						
							|  |  |  | 			logger.Info("getBucket: cached bucket %s does not match this bucket %s", b.bucket, bucket) | 
					
						
							|  |  |  | 			debug.PrintStack() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return b | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-09 02:06:45 +08:00
										 |  |  | 	m.mu.RUnlock() | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	m.mu.Lock() | 
					
						
							| 
									
										
										
										
											2021-09-09 02:06:45 +08:00
										 |  |  | 	defer m.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	// See if someone else fetched it while we waited for the lock.
 | 
					
						
							|  |  |  | 	b, ok = m.buckets[bucket] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		if b.bucket != bucket { | 
					
						
							|  |  |  | 			logger.Info("getBucket: newly cached bucket %s does not match this bucket %s", b.bucket, bucket) | 
					
						
							|  |  |  | 			debug.PrintStack() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return b | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-09 02:06:45 +08:00
										 |  |  | 	// New bucket. If we fail return the transient bucket.
 | 
					
						
							|  |  |  | 	b = newBucketMetacache(bucket, true) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	m.buckets[bucket] = b | 
					
						
							|  |  |  | 	return b | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 00:46:18 +08:00
										 |  |  | // deleteBucketCache will delete the bucket cache if it exists.
 | 
					
						
							|  |  |  | func (m *metacacheManager) deleteBucketCache(bucket string) { | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	m.init.Do(m.initManager) | 
					
						
							| 
									
										
										
										
											2020-11-01 00:46:18 +08:00
										 |  |  | 	m.mu.Lock() | 
					
						
							|  |  |  | 	b, ok := m.buckets[bucket] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 		m.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2020-11-01 00:46:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	delete(m.buckets, bucket) | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	m.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Since deletes may take some time we try to do it without
 | 
					
						
							|  |  |  | 	// holding lock to m all the time.
 | 
					
						
							|  |  |  | 	b.mu.Lock() | 
					
						
							|  |  |  | 	defer b.mu.Unlock() | 
					
						
							|  |  |  | 	for k, v := range b.caches { | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 		if time.Since(v.lastUpdate) > metacacheMaxRunningAge { | 
					
						
							|  |  |  | 			v.delete(context.Background()) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 		v.error = "Bucket deleted" | 
					
						
							|  |  |  | 		v.status = scanStateError | 
					
						
							|  |  |  | 		m.mu.Lock() | 
					
						
							|  |  |  | 		m.trash[k] = v | 
					
						
							|  |  |  | 		m.mu.Unlock() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-01 00:46:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-31 00:33:16 +08:00
										 |  |  | // deleteAll will delete all caches.
 | 
					
						
							|  |  |  | func (m *metacacheManager) deleteAll() { | 
					
						
							| 
									
										
										
										
											2020-11-04 04:47:52 +08:00
										 |  |  | 	m.init.Do(m.initManager) | 
					
						
							| 
									
										
										
										
											2020-10-31 00:33:16 +08:00
										 |  |  | 	m.mu.Lock() | 
					
						
							|  |  |  | 	defer m.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	for bucket, b := range m.buckets { | 
					
						
							|  |  |  | 		b.deleteAll() | 
					
						
							| 
									
										
										
										
											2021-07-06 06:34:41 +08:00
										 |  |  | 		delete(m.buckets, bucket) | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // checkMetacacheState should be used if data is not updating.
 | 
					
						
							|  |  |  | // Should only be called if a failure occurred.
 | 
					
						
							|  |  |  | func (o listPathOptions) checkMetacacheState(ctx context.Context, rpc *peerRESTClient) error { | 
					
						
							|  |  |  | 	// We operate on a copy...
 | 
					
						
							|  |  |  | 	o.Create = false | 
					
						
							| 
									
										
										
										
											2021-07-06 06:34:41 +08:00
										 |  |  | 	c, err := rpc.GetMetacacheListing(ctx, o) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-06 06:34:41 +08:00
										 |  |  | 	cache := *c | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if cache.status == scanStateNone || cache.fileNotFound { | 
					
						
							|  |  |  | 		return errFileNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if cache.status == scanStateSuccess || cache.status == scanStateStarted { | 
					
						
							|  |  |  | 		if time.Since(cache.lastUpdate) > metacacheMaxRunningAge { | 
					
						
							|  |  |  | 			// We got a stale entry, mark error on handling server.
 | 
					
						
							|  |  |  | 			err := fmt.Errorf("timeout: list %s not updated", cache.id) | 
					
						
							|  |  |  | 			cache.error = err.Error() | 
					
						
							|  |  |  | 			cache.status = scanStateError | 
					
						
							| 
									
										
										
										
											2021-07-06 06:34:41 +08:00
										 |  |  | 			rpc.UpdateMetacacheListing(ctx, cache) | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if cache.error != "" { | 
					
						
							|  |  |  | 		return fmt.Errorf("async cache listing failed with: %s", cache.error) | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-25 14:24:38 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | } |