mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Protects the Peers RPC clients map from concurrent access to fix a data race condition.
This commit is contained in:
parent
b59bac670a
commit
6303f26330
@ -19,6 +19,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio-go/pkg/set"
|
||||
@ -26,22 +27,30 @@ import (
|
||||
|
||||
type s3Peers struct {
|
||||
// A map of peer server address (in `host:port` format) to RPC
|
||||
// client connections
|
||||
rpcClient map[string]*AuthRPCClient
|
||||
// client connections.
|
||||
rpcClients map[string]*AuthRPCClient
|
||||
|
||||
// slice of all peer addresses (in `host:port` format)
|
||||
mutex *sync.RWMutex
|
||||
|
||||
// Slice of all peer addresses (in `host:port` format).
|
||||
peers []string
|
||||
}
|
||||
|
||||
func initGlobalS3Peers(disks []string) {
|
||||
// get list of de-duplicated peers
|
||||
// Get list of de-duplicated peers.
|
||||
peers := getAllPeers(disks)
|
||||
globalS3Peers = s3Peers{make(map[string]*AuthRPCClient), nil}
|
||||
|
||||
// Initialize global state.
|
||||
globalS3Peers = s3Peers{
|
||||
rpcClients: make(map[string]*AuthRPCClient),
|
||||
mutex: &sync.RWMutex{},
|
||||
}
|
||||
// Initialize each peer connection.
|
||||
for _, peer := range peers {
|
||||
globalS3Peers.InitS3PeerClient(peer)
|
||||
}
|
||||
|
||||
// Additionally setup a local peer if one does not exist
|
||||
// Additionally setup a local peer if one does not exist.
|
||||
if globalS3Peers.GetPeerClient(globalMinioAddr) == nil {
|
||||
globalS3Peers.InitS3PeerClient(globalMinioAddr)
|
||||
peers = append(peers, globalMinioAddr)
|
||||
@ -55,16 +64,23 @@ func (s3p *s3Peers) GetPeers() []string {
|
||||
}
|
||||
|
||||
func (s3p *s3Peers) GetPeerClient(peer string) *AuthRPCClient {
|
||||
return s3p.rpcClient[peer]
|
||||
// Take a read lock
|
||||
s3p.mutex.RLock()
|
||||
defer s3p.mutex.RUnlock()
|
||||
return s3p.rpcClients[peer]
|
||||
}
|
||||
|
||||
// Initializes a new RPC connection (or closes and re-opens if it
|
||||
// already exists) to a peer. Note that peer address is in `host:port`
|
||||
// format.
|
||||
func (s3p *s3Peers) InitS3PeerClient(peer string) {
|
||||
if s3p.rpcClient[peer] != nil {
|
||||
s3p.rpcClient[peer].Close()
|
||||
delete(s3p.rpcClient, peer)
|
||||
// Take a write lock
|
||||
s3p.mutex.Lock()
|
||||
defer s3p.mutex.Unlock()
|
||||
|
||||
if s3p.rpcClients[peer] != nil {
|
||||
_ = s3p.rpcClients[peer].Close()
|
||||
delete(s3p.rpcClients, peer)
|
||||
}
|
||||
authCfg := &authConfig{
|
||||
accessKey: serverConfig.GetCredential().AccessKeyID,
|
||||
@ -73,16 +89,20 @@ func (s3p *s3Peers) InitS3PeerClient(peer string) {
|
||||
path: path.Join(reservedBucket, s3Path),
|
||||
loginMethod: "S3.LoginHandler",
|
||||
}
|
||||
s3p.rpcClient[peer] = newAuthClient(authCfg)
|
||||
s3p.rpcClients[peer] = newAuthClient(authCfg)
|
||||
}
|
||||
|
||||
func (s3p *s3Peers) Close() error {
|
||||
for _, v := range s3p.rpcClient {
|
||||
// Take a write lock
|
||||
s3p.mutex.Lock()
|
||||
defer s3p.mutex.Unlock()
|
||||
|
||||
for _, v := range s3p.rpcClients {
|
||||
if err := v.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s3p.rpcClient = nil
|
||||
s3p.rpcClients = nil
|
||||
s3p.peers = nil
|
||||
return nil
|
||||
}
|
||||
@ -121,33 +141,48 @@ func (s3p *s3Peers) SendRPC(peers []string, method string, args interface {
|
||||
SetToken(token string)
|
||||
SetTimestamp(tstamp time.Time)
|
||||
}) map[string]error {
|
||||
// result type
|
||||
// Take read lock for rpcClient map
|
||||
s3p.mutex.RLock()
|
||||
defer s3p.mutex.RUnlock()
|
||||
|
||||
// Result type
|
||||
type callResult struct {
|
||||
target string
|
||||
err error
|
||||
}
|
||||
// channel to collect results from goroutines
|
||||
|
||||
// Channel to collect results from goroutines
|
||||
resChan := make(chan callResult)
|
||||
// closure to make a single request.
|
||||
|
||||
// Closure to make a single request.
|
||||
callTarget := func(target string) {
|
||||
reply := &GenericReply{}
|
||||
err := s3p.rpcClient[target].Call(method, args, reply)
|
||||
client, ok := s3p.rpcClients[target]
|
||||
var err error
|
||||
if !ok {
|
||||
err = fmt.Errorf("Requested client was not initialized - %v",
|
||||
target)
|
||||
} else {
|
||||
err = client.Call(method, args, reply)
|
||||
}
|
||||
resChan <- callResult{target, err}
|
||||
}
|
||||
// map of errors
|
||||
|
||||
// Map of errors
|
||||
errsMap := make(map[string]error)
|
||||
// make network calls in parallel
|
||||
for _, target := range peers {
|
||||
go callTarget(target)
|
||||
}
|
||||
// wait on channel and collect all results
|
||||
// Wait on channel and collect all results
|
||||
for range peers {
|
||||
res := <-resChan
|
||||
if res.err != nil {
|
||||
errsMap[res.target] = res.err
|
||||
}
|
||||
}
|
||||
// return errors map
|
||||
|
||||
// Return errors map
|
||||
return errsMap
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user