Improve Peer RPC error handling (Fixes #2992) (#2995)

* Check for RPC connection shutdown and try again just once.

* Refactor SendRPC to use sync.WaitGroup
This commit is contained in:
Aditya Manthramurthy 2016-10-18 21:26:58 -07:00 committed by Harshavardhana
parent 2208992e6a
commit c3bbadacbf
2 changed files with 59 additions and 38 deletions

View File

@ -19,12 +19,13 @@ package cmd
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/rpc"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
) )
type listenerConn struct { type listenerConn struct {
Client *AuthRPCClient TargetAddr string
ListenerARN string ListenerARN string
} }
@ -34,13 +35,14 @@ type listenerLogger struct {
} }
func newListenerLogger(listenerArn, targetAddr string) (*listenerLogger, error) { func newListenerLogger(listenerArn, targetAddr string) (*listenerLogger, error) {
client := globalS3Peers.GetPeerClient(targetAddr) if globalS3Peers.GetPeerClient(targetAddr) == nil {
if client == nil { return nil, fmt.Errorf(
return nil, fmt.Errorf("Peer %s was not initialized - bug!", "Peer %s was not initialized - bug!",
targetAddr) targetAddr,
)
} }
lc := listenerConn{ lc := listenerConn{
Client: client, TargetAddr: targetAddr,
ListenerARN: listenerArn, ListenerARN: listenerArn,
} }
@ -64,9 +66,23 @@ func (lc listenerConn) Fire(entry *logrus.Entry) error {
return nil return nil
} }
evArgs := EventArgs{Event: notificationEvent, Arn: lc.ListenerARN} // Fetch peer client object
client := globalS3Peers.GetPeerClient(lc.TargetAddr)
if client == nil {
return fmt.Errorf("Target %s client RPC object not available!", lc.TargetAddr)
}
// Send Event RPC call and return error
arg := EventArgs{Event: notificationEvent, Arn: lc.ListenerARN}
reply := GenericReply{} reply := GenericReply{}
err := lc.Client.Call("S3.Event", &evArgs, &reply) err := client.Call("S3.Event", &arg, &reply)
// In case connection is shutdown, retry once.
if err != nil {
if err.Error() == rpc.ErrShutdown.Error() {
err = client.Call("S3.Event", &arg, &reply)
}
}
return err return err
} }

View File

@ -19,6 +19,7 @@ package cmd
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/rpc"
"path" "path"
"sync" "sync"
"time" "time"
@ -143,44 +144,48 @@ func (s3p *s3Peers) SendRPC(peers []string, method string, args interface {
SetToken(token string) SetToken(token string)
SetTimestamp(tstamp time.Time) SetTimestamp(tstamp time.Time)
}) map[string]error { }) map[string]error {
// Result type
type callResult struct { // peer error responses array
target string errArr := make([]error, len(peers))
err error
// Start a wait group and make RPC requests to peers.
var wg sync.WaitGroup
for i, target := range peers {
wg.Add(1)
go func(ix int, target string) {
defer wg.Done()
reply := &GenericReply{}
// Get RPC client object safely.
client := s3p.GetPeerClient(target)
var err error
if client == nil {
err = fmt.Errorf("Requested client was not initialized - %v",
target)
} else {
err = client.Call(method, args, reply)
// Check for network errors and try
// again just once.
if err != nil {
if err.Error() == rpc.ErrShutdown.Error() {
err = client.Call(method, args, reply)
}
}
}
errArr[ix] = err
}(i, target)
} }
// Channel to collect results from goroutines // Wait for requests to complete.
resChan := make(chan callResult) wg.Wait()
// Closure to make a single request.
callTarget := func(target string) {
reply := &GenericReply{}
client := s3p.GetPeerClient(target)
var err error
if client == nil {
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) errsMap := make(map[string]error)
// make network calls in parallel for i, errVal := range errArr {
for _, target := range peers { if errVal != nil {
go callTarget(target) errsMap[peers[i]] = errVal
}
// 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 errsMap return errsMap
} }