| 
									
										
										
										
											2024-01-31 04:43:25 +08:00
										 |  |  | // Copyright (c) 2015-2023 MinIO, Inc.
 | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // 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/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-11-26 01:10:22 +08:00
										 |  |  | 	"slices" | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v3" | 
					
						
							| 
									
										
										
										
											2024-02-08 06:24:24 +08:00
										 |  |  | 	"github.com/minio/minio/internal/grid" | 
					
						
							| 
									
										
										
										
											2024-05-25 07:05:23 +08:00
										 |  |  | 	"github.com/minio/pkg/v3/sync/errgroup" | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var errPeerOffline = errors.New("peer is offline") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | type peerS3Client interface { | 
					
						
							|  |  |  | 	ListBuckets(ctx context.Context, opts BucketOptions) ([]BucketInfo, error) | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 	HealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	GetBucketInfo(ctx context.Context, bucket string, opts BucketOptions) (BucketInfo, error) | 
					
						
							|  |  |  | 	MakeBucket(ctx context.Context, bucket string, opts MakeBucketOptions) error | 
					
						
							|  |  |  | 	DeleteBucket(ctx context.Context, bucket string, opts DeleteBucketOptions) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	GetHost() string | 
					
						
							|  |  |  | 	SetPools([]int) | 
					
						
							|  |  |  | 	GetPools() []int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type localPeerS3Client struct { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	node  Node | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	pools []int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *localPeerS3Client) GetHost() string { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	return l.node.Host | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *localPeerS3Client) SetPools(p []int) { | 
					
						
							|  |  |  | 	l.pools = make([]int, len(p)) | 
					
						
							|  |  |  | 	copy(l.pools, p) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l localPeerS3Client) GetPools() []int { | 
					
						
							|  |  |  | 	return l.pools | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l localPeerS3Client) ListBuckets(ctx context.Context, opts BucketOptions) ([]BucketInfo, error) { | 
					
						
							|  |  |  | 	return listBucketsLocal(ctx, opts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | func (l localPeerS3Client) HealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) { | 
					
						
							|  |  |  | 	return healBucketLocal(ctx, bucket, opts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | func (l localPeerS3Client) GetBucketInfo(ctx context.Context, bucket string, opts BucketOptions) (BucketInfo, error) { | 
					
						
							|  |  |  | 	return getBucketInfoLocal(ctx, bucket, opts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l localPeerS3Client) MakeBucket(ctx context.Context, bucket string, opts MakeBucketOptions) error { | 
					
						
							|  |  |  | 	return makeBucketLocal(ctx, bucket, opts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l localPeerS3Client) DeleteBucket(ctx context.Context, bucket string, opts DeleteBucketOptions) error { | 
					
						
							|  |  |  | 	return deleteBucketLocal(ctx, bucket, opts) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | // client to talk to peer Nodes.
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | type remotePeerS3Client struct { | 
					
						
							| 
									
										
										
										
											2024-03-09 03:08:18 +08:00
										 |  |  | 	node  Node | 
					
						
							|  |  |  | 	pools []int | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Function that returns the grid connection for this peer when initialized.
 | 
					
						
							|  |  |  | 	// Will return nil if the grid connection is not initialized yet.
 | 
					
						
							|  |  |  | 	gridConn func() *grid.Connection | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // S3PeerSys - S3 peer call system.
 | 
					
						
							|  |  |  | type S3PeerSys struct { | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	peerClients []peerS3Client // Excludes self
 | 
					
						
							|  |  |  | 	poolsCount  int | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewS3PeerSys - creates new S3 peer calls.
 | 
					
						
							|  |  |  | func NewS3PeerSys(endpoints EndpointServerPools) *S3PeerSys { | 
					
						
							|  |  |  | 	return &S3PeerSys{ | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 		peerClients: newPeerS3Clients(endpoints), | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 		poolsCount:  len(endpoints), | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | // HealBucket - heals buckets at node level
 | 
					
						
							|  |  |  | func (sys *S3PeerSys) HealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) { | 
					
						
							|  |  |  | 	g := errgroup.WithNErrs(len(sys.peerClients)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-18 07:19:09 +08:00
										 |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		client := client | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				return errPeerOffline | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			_, err := client.GetBucketInfo(ctx, bucket, BucketOptions{}) | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		}, idx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	errs := g.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var poolErrs []error | 
					
						
							|  |  |  | 	for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ { | 
					
						
							|  |  |  | 		perPoolErrs := make([]error, 0, len(sys.peerClients)) | 
					
						
							|  |  |  | 		for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if slices.Contains(client.GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				perPoolErrs = append(perPoolErrs, errs[i]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		quorum := len(perPoolErrs) / 2 | 
					
						
							|  |  |  | 		poolErrs = append(poolErrs, reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, quorum)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 22:29:18 +08:00
										 |  |  | 	if !opts.Recreate { | 
					
						
							|  |  |  | 		// when there is no force recreate look for pool
 | 
					
						
							|  |  |  | 		// errors to recreate the bucket on all pools.
 | 
					
						
							|  |  |  | 		opts.Remove = isAllBucketsNotFound(poolErrs) | 
					
						
							|  |  |  | 		opts.Recreate = !opts.Remove | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-18 07:19:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-18 08:50:07 +08:00
										 |  |  | 	g = errgroup.WithNErrs(len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 	healBucketResults := make([]madmin.HealResultItem, len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		client := client | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				return errPeerOffline | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			res, err := client.HealBucket(ctx, bucket, opts) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			healBucketResults[idx] = res | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		}, idx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-18 07:19:09 +08:00
										 |  |  | 	errs = g.Wait() | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ { | 
					
						
							|  |  |  | 		perPoolErrs := make([]error, 0, len(sys.peerClients)) | 
					
						
							|  |  |  | 		for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if slices.Contains(client.GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				perPoolErrs = append(perPoolErrs, errs[i]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		quorum := len(perPoolErrs) / 2 | 
					
						
							|  |  |  | 		if poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, quorum); poolErr != nil { | 
					
						
							|  |  |  | 			return madmin.HealResultItem{}, poolErr | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-04 07:12:25 +08:00
										 |  |  | 	if healBucketErr := reduceWriteQuorumErrs(ctx, errs, bucketOpIgnoredErrs, len(errs)/2+1); healBucketErr != nil { | 
					
						
							|  |  |  | 		return madmin.HealResultItem{}, toObjectErr(healBucketErr, bucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res := madmin.HealResultItem{ | 
					
						
							|  |  |  | 		Type:     madmin.HealItemBucket, | 
					
						
							|  |  |  | 		Bucket:   bucket, | 
					
						
							|  |  |  | 		SetCount: -1, // explicitly set an invalid value -1, for bucket heal scenario
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 	for i, err := range errs { | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							| 
									
										
										
										
											2024-12-04 07:12:25 +08:00
										 |  |  | 			res.Before.Drives = append(res.Before.Drives, healBucketResults[i].Before.Drives...) | 
					
						
							|  |  |  | 			res.After.Drives = append(res.After.Drives, healBucketResults[i].After.Drives...) | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-04 07:12:25 +08:00
										 |  |  | 	return res, nil | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | // ListBuckets lists buckets across all nodes and returns a consistent view:
 | 
					
						
							|  |  |  | //   - Return an error when a pool cannot return N/2+1 valid bucket information
 | 
					
						
							|  |  |  | //   - For each pool, check if the bucket exists in N/2+1 nodes before including it in the final result
 | 
					
						
							|  |  |  | func (sys *S3PeerSys) ListBuckets(ctx context.Context, opts BucketOptions) ([]BucketInfo, error) { | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 	g := errgroup.WithNErrs(len(sys.peerClients)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	nodeBuckets := make([][]BucketInfo, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		client := client | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				return errPeerOffline | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			localBuckets, err := client.ListBuckets(ctx, opts) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 			nodeBuckets[idx] = localBuckets | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}, idx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-21 08:50:21 +08:00
										 |  |  | 	errs := g.Wait() | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 	// The list of buckets in a map to avoid duplication
 | 
					
						
							|  |  |  | 	resultMap := make(map[string]BucketInfo) | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 	for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ { | 
					
						
							|  |  |  | 		perPoolErrs := make([]error, 0, len(sys.peerClients)) | 
					
						
							|  |  |  | 		for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if slices.Contains(client.GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				perPoolErrs = append(perPoolErrs, errs[i]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-10-21 08:50:21 +08:00
										 |  |  | 		quorum := len(perPoolErrs) / 2 | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 		if poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, quorum); poolErr != nil { | 
					
						
							|  |  |  | 			return nil, poolErr | 
					
						
							| 
									
										
										
										
											2023-01-05 00:53:58 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		bucketsMap := make(map[string]int) | 
					
						
							|  |  |  | 		for idx, buckets := range nodeBuckets { | 
					
						
							|  |  |  | 			if buckets == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !slices.Contains(sys.peerClients[idx].GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, bi := range buckets { | 
					
						
							|  |  |  | 				_, ok := resultMap[bi.Name] | 
					
						
							|  |  |  | 				if ok { | 
					
						
							|  |  |  | 					// Skip it, this bucket is found in another pool
 | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				bucketsMap[bi.Name]++ | 
					
						
							| 
									
										
										
										
											2023-10-21 08:50:21 +08:00
										 |  |  | 				if bucketsMap[bi.Name] >= quorum { | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 					resultMap[bi.Name] = bi | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-01-05 00:53:58 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 		// loop through buckets and see if some with lost quorum
 | 
					
						
							|  |  |  | 		// these could be stale buckets lying around, queue a heal
 | 
					
						
							|  |  |  | 		// of such a bucket. This is needed here as we identify such
 | 
					
						
							|  |  |  | 		// buckets here while listing buckets. As part of regular
 | 
					
						
							|  |  |  | 		// globalBucketMetadataSys.Init() call would get a valid
 | 
					
						
							|  |  |  | 		// buckets only and not the quourum lost ones like this, so
 | 
					
						
							|  |  |  | 		// explicit call
 | 
					
						
							|  |  |  | 		for bktName, count := range bucketsMap { | 
					
						
							|  |  |  | 			if count < quorum { | 
					
						
							|  |  |  | 				// Queue a bucket heal task
 | 
					
						
							| 
									
										
										
										
											2024-08-14 06:26:05 +08:00
										 |  |  | 				globalMRFState.addPartialOp(PartialOperation{ | 
					
						
							|  |  |  | 					Bucket: bktName, | 
					
						
							|  |  |  | 					Queued: time.Now(), | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 15:58:54 +08:00
										 |  |  | 	result := make([]BucketInfo, 0, len(resultMap)) | 
					
						
							|  |  |  | 	for _, bi := range resultMap { | 
					
						
							|  |  |  | 		result = append(result, bi) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sort.Slice(result, func(i, j int) bool { | 
					
						
							|  |  |  | 		return result[i].Name < result[j].Name | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-05 00:53:58 +08:00
										 |  |  | 	return result, nil | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | // GetBucketInfo returns bucket stat info about bucket on disk across all peers
 | 
					
						
							|  |  |  | func (sys *S3PeerSys) GetBucketInfo(ctx context.Context, bucket string, opts BucketOptions) (binfo BucketInfo, err error) { | 
					
						
							|  |  |  | 	g := errgroup.WithNErrs(len(sys.peerClients)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 06:44:16 +08:00
										 |  |  | 	bucketInfos := make([]BucketInfo, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		client := client | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				return errPeerOffline | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			bucketInfo, err := client.GetBucketInfo(ctx, bucket, opts) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-04-13 06:44:16 +08:00
										 |  |  | 			bucketInfos[idx] = bucketInfo | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}, idx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 06:44:16 +08:00
										 |  |  | 	errs := g.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-21 08:50:21 +08:00
										 |  |  | 	for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ { | 
					
						
							|  |  |  | 		perPoolErrs := make([]error, 0, len(sys.peerClients)) | 
					
						
							|  |  |  | 		for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if slices.Contains(client.GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				perPoolErrs = append(perPoolErrs, errs[i]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		quorum := len(perPoolErrs) / 2 | 
					
						
							|  |  |  | 		if poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, quorum); poolErr != nil { | 
					
						
							|  |  |  | 			return BucketInfo{}, poolErr | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-04-07 13:42:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 06:44:16 +08:00
										 |  |  | 	for i, err := range errs { | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			return bucketInfos[i], nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return BucketInfo{}, toObjectErr(errVolumeNotFound, bucket) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | func (client *remotePeerS3Client) ListBuckets(ctx context.Context, opts BucketOptions) ([]BucketInfo, error) { | 
					
						
							| 
									
										
										
										
											2024-05-23 07:07:14 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(ctx, globalDriveConfig.GetMaxTimeout()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-09 03:08:18 +08:00
										 |  |  | 	bi, err := listBucketsRPC.Call(ctx, client.gridConn(), &opts) | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2024-03-09 03:08:18 +08:00
										 |  |  | 		return nil, toStorageErr(err) | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-03-09 03:08:18 +08:00
										 |  |  | 	buckets := make([]BucketInfo, 0, len(bi.Value())) | 
					
						
							|  |  |  | 	for _, b := range bi.Value() { | 
					
						
							|  |  |  | 		if b != nil { | 
					
						
							|  |  |  | 			buckets = append(buckets, *b) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bi.Recycle() // BucketInfo has no internal pointers, so it's safe to recycle.
 | 
					
						
							|  |  |  | 	return buckets, nil | 
					
						
							| 
									
										
										
										
											2023-01-04 15:39:40 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | func (client *remotePeerS3Client) HealBucket(ctx context.Context, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	conn := client.gridConn() | 
					
						
							|  |  |  | 	if conn == nil { | 
					
						
							|  |  |  | 		return madmin.HealResultItem{}, nil | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	mss := grid.NewMSSWith(map[string]string{ | 
					
						
							|  |  |  | 		peerS3Bucket:        bucket, | 
					
						
							|  |  |  | 		peerS3BucketDeleted: strconv.FormatBool(opts.Remove), | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-23 07:07:14 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(ctx, globalDriveConfig.GetMaxTimeout()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-04 07:12:25 +08:00
										 |  |  | 	resp, err := healBucketRPC.Call(ctx, conn, mss) | 
					
						
							|  |  |  | 	return resp.ValueOrZero(), toStorageErr(err) | 
					
						
							| 
									
										
										
										
											2024-01-10 12:34:04 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | // GetBucketInfo returns bucket stat info from a peer
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | func (client *remotePeerS3Client) GetBucketInfo(ctx context.Context, bucket string, opts BucketOptions) (BucketInfo, error) { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	conn := client.gridConn() | 
					
						
							|  |  |  | 	if conn == nil { | 
					
						
							|  |  |  | 		return BucketInfo{}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mss := grid.NewMSSWith(map[string]string{ | 
					
						
							|  |  |  | 		peerS3Bucket:        bucket, | 
					
						
							|  |  |  | 		peerS3BucketDeleted: strconv.FormatBool(opts.Deleted), | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-23 07:07:14 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(ctx, globalDriveConfig.GetMaxTimeout()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 06:54:46 +08:00
										 |  |  | 	volInfo, err := headBucketRPC.Call(ctx, conn, mss) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2024-02-08 06:24:24 +08:00
										 |  |  | 		return BucketInfo{}, toStorageErr(err) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	return BucketInfo{ | 
					
						
							|  |  |  | 		Name:    volInfo.Name, | 
					
						
							|  |  |  | 		Created: volInfo.Created, | 
					
						
							| 
									
										
										
										
											2025-01-30 03:42:55 +08:00
										 |  |  | 		Deleted: volInfo.Deleted, | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MakeBucket creates bucket across all peers
 | 
					
						
							|  |  |  | func (sys *S3PeerSys) MakeBucket(ctx context.Context, bucket string, opts MakeBucketOptions) error { | 
					
						
							|  |  |  | 	g := errgroup.WithNErrs(len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				return errPeerOffline | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return client.MakeBucket(ctx, bucket, opts) | 
					
						
							|  |  |  | 		}, idx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	errs := g.Wait() | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ { | 
					
						
							|  |  |  | 		perPoolErrs := make([]error, 0, len(sys.peerClients)) | 
					
						
							|  |  |  | 		for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if slices.Contains(client.GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				perPoolErrs = append(perPoolErrs, errs[i]) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-07-26 07:58:31 +08:00
										 |  |  | 		if poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, len(perPoolErrs)/2+1); poolErr != nil { | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 			return toObjectErr(poolErr, bucket) | 
					
						
							| 
									
										
										
										
											2023-04-19 22:47:42 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MakeBucket creates a bucket on a peer
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | func (client *remotePeerS3Client) MakeBucket(ctx context.Context, bucket string, opts MakeBucketOptions) error { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	conn := client.gridConn() | 
					
						
							|  |  |  | 	if conn == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	mss := grid.NewMSSWith(map[string]string{ | 
					
						
							|  |  |  | 		peerS3Bucket:            bucket, | 
					
						
							|  |  |  | 		peerS3BucketForceCreate: strconv.FormatBool(opts.ForceCreate), | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-23 07:07:14 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(ctx, globalDriveConfig.GetMaxTimeout()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 06:54:46 +08:00
										 |  |  | 	_, err := makeBucketRPC.Call(ctx, conn, mss) | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	return toStorageErr(err) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteBucket deletes bucket across all peers
 | 
					
						
							|  |  |  | func (sys *S3PeerSys) DeleteBucket(ctx context.Context, bucket string, opts DeleteBucketOptions) error { | 
					
						
							|  |  |  | 	g := errgroup.WithNErrs(len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		g.Go(func() error { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				return errPeerOffline | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return client.DeleteBucket(ctx, bucket, opts) | 
					
						
							|  |  |  | 		}, idx) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	errs := g.Wait() | 
					
						
							| 
									
										
										
										
											2023-04-26 05:16:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	for poolIdx := 0; poolIdx < sys.poolsCount; poolIdx++ { | 
					
						
							|  |  |  | 		perPoolErrs := make([]error, 0, len(sys.peerClients)) | 
					
						
							|  |  |  | 		for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if slices.Contains(client.GetPools(), poolIdx) { | 
					
						
							|  |  |  | 				perPoolErrs = append(perPoolErrs, errs[i]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-09 07:22:16 +08:00
										 |  |  | 		poolErr := reduceWriteQuorumErrs(ctx, perPoolErrs, bucketOpIgnoredErrs, len(perPoolErrs)/2+1) | 
					
						
							|  |  |  | 		if poolErr != nil && !errors.Is(poolErr, errVolumeNotFound) { | 
					
						
							|  |  |  | 			if !opts.NoRecreate { | 
					
						
							|  |  |  | 				// re-create successful deletes, since we are return an error.
 | 
					
						
							|  |  |  | 				sys.MakeBucket(ctx, bucket, MakeBucketOptions{}) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 			return toObjectErr(poolErr, bucket) | 
					
						
							| 
									
										
										
										
											2023-04-26 05:16:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteBucket deletes bucket on a peer
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | func (client *remotePeerS3Client) DeleteBucket(ctx context.Context, bucket string, opts DeleteBucketOptions) error { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	conn := client.gridConn() | 
					
						
							|  |  |  | 	if conn == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	mss := grid.NewMSSWith(map[string]string{ | 
					
						
							|  |  |  | 		peerS3Bucket:            bucket, | 
					
						
							|  |  |  | 		peerS3BucketForceDelete: strconv.FormatBool(opts.Force), | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-23 07:07:14 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(ctx, globalDriveConfig.GetMaxTimeout()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 06:54:46 +08:00
										 |  |  | 	_, err := deleteBucketRPC.Call(ctx, conn, mss) | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	return toStorageErr(err) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | func (client remotePeerS3Client) GetHost() string { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 	return client.node.Host | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (client remotePeerS3Client) GetPools() []int { | 
					
						
							|  |  |  | 	return client.pools | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (client *remotePeerS3Client) SetPools(p []int) { | 
					
						
							|  |  |  | 	client.pools = make([]int, len(p)) | 
					
						
							|  |  |  | 	copy(client.pools, p) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | // newPeerS3Clients creates new peer clients.
 | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | func newPeerS3Clients(endpoints EndpointServerPools) (peers []peerS3Client) { | 
					
						
							|  |  |  | 	nodes := endpoints.GetNodes() | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 	peers = make([]peerS3Client, len(nodes)) | 
					
						
							|  |  |  | 	for i, node := range nodes { | 
					
						
							|  |  |  | 		if node.IsLocal { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 			peers[i] = &localPeerS3Client{node: node} | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 			peers[i] = newPeerS3Client(node) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-06-24 02:48:23 +08:00
										 |  |  | 		peers[i].SetPools(node.Pools) | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return peers | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns a peer S3 client.
 | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | func newPeerS3Client(node Node) peerS3Client { | 
					
						
							|  |  |  | 	var gridConn atomic.Pointer[grid.Connection] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &remotePeerS3Client{ | 
					
						
							| 
									
										
										
										
											2024-03-09 03:08:18 +08:00
										 |  |  | 		node: node, | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 		gridConn: func() *grid.Connection { | 
					
						
							|  |  |  | 			// Lazy initialization of grid connection.
 | 
					
						
							|  |  |  | 			// When we create this peer client, the grid connection is likely not yet initialized.
 | 
					
						
							|  |  |  | 			if node.GridHost == "" { | 
					
						
							| 
									
										
										
										
											2024-04-04 20:04:40 +08:00
										 |  |  | 				bugLogIf(context.Background(), fmt.Errorf("gridHost is empty for peer %s", node.Host), node.Host+":gridHost") | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			gc := gridConn.Load() | 
					
						
							|  |  |  | 			if gc != nil { | 
					
						
							|  |  |  | 				return gc | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			gm := globalGrid.Load() | 
					
						
							|  |  |  | 			if gm == nil { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			gc = gm.Connection(node.GridHost) | 
					
						
							|  |  |  | 			if gc == nil { | 
					
						
							| 
									
										
										
										
											2024-04-04 20:04:40 +08:00
										 |  |  | 				bugLogIf(context.Background(), fmt.Errorf("gridHost %s not found for peer %s", node.GridHost, node.Host), node.Host+":gridHost") | 
					
						
							| 
									
										
										
										
											2024-02-03 06:54:54 +08:00
										 |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			gridConn.Store(gc) | 
					
						
							|  |  |  | 			return gc | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-04 00:16:39 +08:00
										 |  |  | } |