storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/dsync/rpc-client-impl_test.go (about) 1 /* 2 * Minio Cloud Storage, (C) 2016 Minio, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dsync_test 18 19 import ( 20 "context" 21 "net/rpc" 22 "sync" 23 24 . "storj.io/minio/pkg/dsync" 25 ) 26 27 // ReconnectRPCClient is a wrapper type for rpc.Client which provides reconnect on first failure. 28 type ReconnectRPCClient struct { 29 mutex sync.Mutex 30 rpc *rpc.Client 31 addr string 32 endpoint string 33 } 34 35 // newClient constructs a ReconnectRPCClient object with addr and endpoint initialized. 36 // It _doesn't_ connect to the remote endpoint. See Call method to see when the 37 // connect happens. 38 func newClient(addr, endpoint string) NetLocker { 39 return &ReconnectRPCClient{ 40 addr: addr, 41 endpoint: endpoint, 42 } 43 } 44 45 // Close closes the underlying socket file descriptor. 46 func (rpcClient *ReconnectRPCClient) IsOnline() bool { 47 rpcClient.mutex.Lock() 48 defer rpcClient.mutex.Unlock() 49 // If rpc client has not connected yet there is nothing to close. 50 return rpcClient.rpc != nil 51 } 52 53 func (rpcClient *ReconnectRPCClient) IsLocal() bool { 54 return false 55 } 56 57 // Close closes the underlying socket file descriptor. 58 func (rpcClient *ReconnectRPCClient) Close() error { 59 rpcClient.mutex.Lock() 60 defer rpcClient.mutex.Unlock() 61 // If rpc client has not connected yet there is nothing to close. 62 if rpcClient.rpc == nil { 63 return nil 64 } 65 // Reset rpcClient.rpc to allow for subsequent calls to use a new 66 // (socket) connection. 67 clnt := rpcClient.rpc 68 rpcClient.rpc = nil 69 return clnt.Close() 70 } 71 72 // Call makes a RPC call to the remote endpoint using the default codec, namely encoding/gob. 73 func (rpcClient *ReconnectRPCClient) Call(serviceMethod string, args interface{}, reply interface{}) (err error) { 74 rpcClient.mutex.Lock() 75 defer rpcClient.mutex.Unlock() 76 dialCall := func() error { 77 // If the rpc.Client is nil, we attempt to (re)connect with the remote endpoint. 78 if rpcClient.rpc == nil { 79 clnt, derr := rpc.DialHTTPPath("tcp", rpcClient.addr, rpcClient.endpoint) 80 if derr != nil { 81 return derr 82 } 83 rpcClient.rpc = clnt 84 } 85 // If the RPC fails due to a network-related error, then we reset 86 // rpc.Client for a subsequent reconnect. 87 return rpcClient.rpc.Call(serviceMethod, args, reply) 88 } 89 if err = dialCall(); err == rpc.ErrShutdown { 90 rpcClient.rpc.Close() 91 rpcClient.rpc = nil 92 err = dialCall() 93 } 94 return err 95 } 96 97 func (rpcClient *ReconnectRPCClient) RLock(ctx context.Context, args LockArgs) (status bool, err error) { 98 err = rpcClient.Call("Dsync.RLock", &args, &status) 99 return status, err 100 } 101 102 func (rpcClient *ReconnectRPCClient) Lock(ctx context.Context, args LockArgs) (status bool, err error) { 103 err = rpcClient.Call("Dsync.Lock", &args, &status) 104 return status, err 105 } 106 107 func (rpcClient *ReconnectRPCClient) RUnlock(args LockArgs) (status bool, err error) { 108 err = rpcClient.Call("Dsync.RUnlock", &args, &status) 109 return status, err 110 } 111 112 func (rpcClient *ReconnectRPCClient) Unlock(args LockArgs) (status bool, err error) { 113 err = rpcClient.Call("Dsync.Unlock", &args, &status) 114 return status, err 115 } 116 117 func (rpcClient *ReconnectRPCClient) Refresh(ctx context.Context, args LockArgs) (refreshed bool, err error) { 118 err = rpcClient.Call("Dsync.Refresh", &args, &refreshed) 119 return refreshed, err 120 } 121 122 func (rpcClient *ReconnectRPCClient) ForceUnlock(ctx context.Context, args LockArgs) (reply bool, err error) { 123 err = rpcClient.Call("Dsync.ForceUnlock", &args, &reply) 124 return reply, err 125 } 126 127 func (rpcClient *ReconnectRPCClient) String() string { 128 return "http://" + rpcClient.addr + "/" + rpcClient.endpoint 129 }