github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/retryrpc/retryrpc_test.go (about)

     1  // Copyright (c) 2015-2021, NVIDIA CORPORATION.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package retryrpc
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/swiftstack/ProxyFS/bucketstats"
    12  	"github.com/swiftstack/ProxyFS/retryrpc/rpctest"
    13  )
    14  
    15  // Test basic retryrpc primitives
    16  //
    17  // This unit test exists here since it uses jrpcfs which would be a
    18  // circular dependency if the test was in retryrpc.
    19  func TestRetryRPC(t *testing.T) {
    20  
    21  	testServer(t)
    22  	testBtree(t)
    23  	testStatsAndBucketstats(t)
    24  }
    25  
    26  type MyType struct {
    27  	field1 int
    28  }
    29  
    30  type MyRequest struct {
    31  	Field1 int
    32  }
    33  
    34  type MyResponse struct {
    35  	Error error
    36  }
    37  
    38  func (m *MyType) ExportedFunction(request MyRequest, response *MyResponse) (err error) {
    39  	request.Field1 = 1
    40  	return
    41  }
    42  
    43  func (m *MyType) unexportedFunction(i int) {
    44  	m.field1 = i
    45  }
    46  
    47  func getNewServer(lt time.Duration, dontStartTrimmers bool) (rrSvr *Server, ip string, p int) {
    48  	var (
    49  		ipaddr = "127.0.0.1"
    50  		port   = 24456
    51  	)
    52  	config := &ServerConfig{LongTrim: lt, ShortTrim: 100 * time.Millisecond, IPAddr: "127.0.0.1",
    53  		Port: 24456, DeadlineIO: 5 * time.Second, dontStartTrimmers: dontStartTrimmers}
    54  
    55  	// Create a new RetryRPC Server.  Completed request will live on
    56  	// completedRequests for 10 seconds.
    57  	rrSvr = NewServer(config)
    58  	ip = ipaddr
    59  	p = port
    60  	return
    61  }
    62  
    63  // Test basic Server creation and deletion
    64  func testServer(t *testing.T) {
    65  	assert := assert.New(t)
    66  	zero := 0
    67  	assert.Equal(0, zero)
    68  
    69  	// Create new rpctest server - needed for calling
    70  	// RPCs
    71  	myJrpcfs := rpctest.NewServer()
    72  
    73  	rrSvr, ipaddr, port := getNewServer(10*time.Second, false)
    74  	assert.NotNil(rrSvr)
    75  
    76  	// Register the Server - sets up the methods supported by the
    77  	// server
    78  	err := rrSvr.Register(myJrpcfs)
    79  	assert.Nil(err)
    80  
    81  	// Start listening for requests on the ipaddr/port
    82  	startErr := rrSvr.Start()
    83  	assert.Nil(startErr, "startErr is not nil")
    84  
    85  	// Tell server to start accepting and processing requests
    86  	rrSvr.Run()
    87  
    88  	// Now - setup a client to send requests to the server
    89  	clientConfig := &ClientConfig{MyUniqueID: "client 1", IPAddr: ipaddr, Port: port, RootCAx509CertificatePEM: rrSvr.Creds.RootCAx509CertificatePEM,
    90  		Callbacks: nil, DeadlineIO: 5 * time.Second}
    91  	rrClnt, newErr := NewClient(clientConfig)
    92  	assert.NotNil(rrClnt)
    93  	assert.Nil(newErr)
    94  
    95  	// Send an RPC which should return success
    96  	pingRequest := &rpctest.PingReq{Message: "Ping Me!"}
    97  	pingReply := &rpctest.PingReply{}
    98  	sendErr := rrClnt.Send("RpcPing", pingRequest, pingReply)
    99  	assert.Nil(sendErr)
   100  	assert.Equal("pong 8 bytes", pingReply.Message)
   101  	assert.Equal(1, rrSvr.CompletedCnt())
   102  
   103  	// Send an RPC which should return an error
   104  	pingRequest = &rpctest.PingReq{Message: "Ping Me!"}
   105  	pingReply = &rpctest.PingReply{}
   106  	sendErr = rrClnt.Send("RpcPingWithError", pingRequest, pingReply)
   107  	assert.NotNil(sendErr)
   108  
   109  	assert.Equal(2, rrSvr.CompletedCnt())
   110  
   111  	// Send an RPC which should return an error
   112  	pingRequest = &rpctest.PingReq{Message: "Ping Me!"}
   113  	pingReply = &rpctest.PingReply{}
   114  	sendErr = rrClnt.Send("RpcInvalidMethod", pingRequest, pingReply)
   115  	assert.NotNil(sendErr)
   116  
   117  	assert.Equal(3, rrSvr.CompletedCnt())
   118  
   119  	// Stop the client before exiting
   120  	rrClnt.Close()
   121  
   122  	// Stop the server before exiting
   123  	rrSvr.Close()
   124  }
   125  
   126  func testBtree(t *testing.T) {
   127  	assert := assert.New(t)
   128  
   129  	rrSvr, ipaddr, port := getNewServer(10*time.Second, false)
   130  	assert.NotNil(rrSvr)
   131  
   132  	// Setup a client - we only will be targeting the btree
   133  	clientConfig := &ClientConfig{MyUniqueID: "client 1", IPAddr: ipaddr, Port: port, RootCAx509CertificatePEM: rrSvr.Creds.RootCAx509CertificatePEM,
   134  		Callbacks: nil, DeadlineIO: 5 * time.Second}
   135  	client, newErr := NewClient(clientConfig)
   136  	assert.NotNil(client)
   137  	assert.Nil(newErr)
   138  
   139  	// Simulate requests completing out of order
   140  	client.updateHighestConsecutiveNum(requestID(10))
   141  	client.updateHighestConsecutiveNum(requestID(5))
   142  	client.updateHighestConsecutiveNum(requestID(11))
   143  
   144  	client.setHighestConsecutive()
   145  	assert.Equal(requestID(0), client.highestConsecutive)
   146  
   147  	// Now fillin first gap
   148  	client.updateHighestConsecutiveNum(requestID(4))
   149  	client.updateHighestConsecutiveNum(requestID(3))
   150  	client.updateHighestConsecutiveNum(requestID(2))
   151  	client.updateHighestConsecutiveNum(requestID(1))
   152  	assert.Equal(int(3), client.bt.Len())
   153  
   154  	client.setHighestConsecutive()
   155  	assert.Equal(int(3), client.bt.Len())
   156  	assert.Equal(requestID(5), client.highestConsecutive)
   157  
   158  	// Now fillin next set of gaps
   159  	client.updateHighestConsecutiveNum(requestID(6))
   160  	client.updateHighestConsecutiveNum(requestID(7))
   161  	client.updateHighestConsecutiveNum(requestID(8))
   162  	client.updateHighestConsecutiveNum(requestID(9))
   163  	assert.Equal(int(1), client.bt.Len())
   164  
   165  	client.setHighestConsecutive()
   166  	assert.Equal(int(1), client.bt.Len())
   167  	assert.Equal(requestID(11), client.highestConsecutive)
   168  }
   169  
   170  // Per pfsagent statistics
   171  type clientStats struct {
   172  	AddCompleted           bucketstats.Total           // Number added to completed list
   173  	RmCompleted            bucketstats.Total           // Number removed from completed list
   174  	RPCLenUsec             bucketstats.BucketLog2Round // Average times of RPCs
   175  	LongestRPCMethod       string                      // Method of longest RPC
   176  	ReplySize              bucketstats.BucketLog2Round // Largest RPC reply size completed
   177  	LargestReplySizeMethod string                      // Method of largest RPC reply size completed
   178  	RPCcompleted           bucketstats.Total           // Number of RPCs which completed - incremented after call returns
   179  	RPCretried             bucketstats.Total           // Number of RPCs which were just pulled from completed list
   180  	RPCattempted           bucketstats.Total           // Number of RPCs attempted - may be completed or in process
   181  	RPCinprocess           bucketstats.Total           // Number of RPCs presently calling RPC - decremented when completed
   182  }
   183  
   184  // Test use of bucketstats package
   185  func testStatsAndBucketstats(t *testing.T) {
   186  	var (
   187  		myClient1       clientStats
   188  		myUniqueClient1 = "1111111"
   189  
   190  		myClient2       clientStats
   191  		myUniqueClient2 = "2222222"
   192  	)
   193  
   194  	// Register from bucketstats from pfsagent #1
   195  	bucketstats.Register("proxyfs.retryrpc", myUniqueClient1, &myClient1)
   196  
   197  	// Register from bucketstats from pfsagent #2
   198  	bucketstats.Register("proxyfs.retryrpc", myUniqueClient2, &myClient2)
   199  
   200  	// Completed list stats
   201  	myClient1.AddCompleted.Add(1)
   202  	myClient1.RmCompleted.Add(1)
   203  
   204  	// RPC counts
   205  	myClient1.RPCcompleted.Add(1)
   206  	myClient1.RPCretried.Add(1)
   207  	myClient1.RPCattempted.Add(1)
   208  	myClient1.RPCinprocess.Add(1)
   209  
   210  	// Track duration of all RPCs in a graph
   211  	start := time.Now()
   212  	time.Sleep(10 * time.Millisecond)
   213  	myClient1.RPCLenUsec.Add(uint64(time.Since(start) / time.Microsecond))
   214  	myClient1.ReplySize.Add(8192)
   215  
   216  	// Example of pfsagent #2
   217  	myClient2.RPCcompleted.Add(1)
   218  	myClient2.RPCretried.Add(1)
   219  	myClient2.RPCattempted.Add(1)
   220  	myClient2.RPCinprocess.Add(1)
   221  
   222  	// Dump stats
   223  	/* DEBUG ONLY -
   224  	fmt.Printf("pfsagent #1: %s\n", bucketstats.SprintStats(bucketstats.StatFormatParsable1, "proxyfs.retryrpc", myUniqueClient1))
   225  	fmt.Printf("pfsagent #2: %s\n", bucketstats.SprintStats(bucketstats.StatFormatParsable1, "proxyfs.retryrpc", myUniqueClient2))
   226  	*/
   227  
   228  	// Unregister clients from bucketstats
   229  	bucketstats.UnRegister("proxyfs.retryrpc", myUniqueClient1)
   230  	bucketstats.UnRegister("proxyfs.retryrpc", myUniqueClient2)
   231  }