github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/rpc_test.go (about) 1 package consul 2 3 import ( 4 "bytes" 5 "os" 6 "testing" 7 "time" 8 9 "github.com/hashicorp/consul/agent/consul/state" 10 "github.com/hashicorp/consul/agent/structs" 11 "github.com/hashicorp/consul/testrpc" 12 "github.com/hashicorp/consul/testutil/retry" 13 "github.com/hashicorp/go-memdb" 14 "github.com/hashicorp/net-rpc-msgpackrpc" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func TestRPC_NoLeader_Fail(t *testing.T) { 20 t.Parallel() 21 dir1, s1 := testServerWithConfig(t, func(c *Config) { 22 c.RPCHoldTimeout = 1 * time.Millisecond 23 }) 24 defer os.RemoveAll(dir1) 25 defer s1.Shutdown() 26 codec := rpcClient(t, s1) 27 defer codec.Close() 28 29 arg := structs.RegisterRequest{ 30 Datacenter: "dc1", 31 Node: "foo", 32 Address: "127.0.0.1", 33 } 34 var out struct{} 35 36 // Make sure we eventually fail with a no leader error, which we should 37 // see given the short timeout. 38 err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out) 39 if err == nil || err.Error() != structs.ErrNoLeader.Error() { 40 t.Fatalf("bad: %v", err) 41 } 42 43 // Now make sure it goes through. 44 testrpc.WaitForTestAgent(t, s1.RPC, "dc1") 45 err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out) 46 if err != nil { 47 t.Fatalf("bad: %v", err) 48 } 49 } 50 51 func TestRPC_NoLeader_Fail_on_stale_read(t *testing.T) { 52 t.Parallel() 53 dir1, s1 := testServerWithConfig(t, func(c *Config) { 54 c.RPCHoldTimeout = 1 * time.Millisecond 55 }) 56 defer os.RemoveAll(dir1) 57 defer s1.Shutdown() 58 codec := rpcClient(t, s1) 59 defer codec.Close() 60 61 arg := structs.RegisterRequest{ 62 Datacenter: "dc1", 63 Node: "foo", 64 Address: "127.0.0.1", 65 } 66 var out struct{} 67 68 // Make sure we eventually fail with a no leader error, which we should 69 // see given the short timeout. 70 err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out) 71 if err == nil || err.Error() != structs.ErrNoLeader.Error() { 72 t.Fatalf("bad: %v", err) 73 } 74 75 // Until leader has never been known, stale should fail 76 getKeysReq := structs.KeyListRequest{ 77 Datacenter: "dc1", 78 Prefix: "", 79 Seperator: "/", 80 QueryOptions: structs.QueryOptions{AllowStale: true}, 81 } 82 var keyList structs.IndexedKeyList 83 if err := msgpackrpc.CallWithCodec(codec, "KVS.ListKeys", &getKeysReq, &keyList); err.Error() != structs.ErrNoLeader.Error() { 84 t.Fatalf("expected %v but got err: %v", structs.ErrNoLeader, err) 85 } 86 87 testrpc.WaitForTestAgent(t, s1.RPC, "dc1") 88 if err := msgpackrpc.CallWithCodec(codec, "KVS.ListKeys", &getKeysReq, &keyList); err != nil { 89 t.Fatalf("Did not expect any error but got err: %v", err) 90 } 91 } 92 93 func TestRPC_NoLeader_Retry(t *testing.T) { 94 t.Parallel() 95 dir1, s1 := testServerWithConfig(t, func(c *Config) { 96 c.RPCHoldTimeout = 10 * time.Second 97 }) 98 defer os.RemoveAll(dir1) 99 defer s1.Shutdown() 100 codec := rpcClient(t, s1) 101 defer codec.Close() 102 103 arg := structs.RegisterRequest{ 104 Datacenter: "dc1", 105 Node: "foo", 106 Address: "127.0.0.1", 107 } 108 var out struct{} 109 110 // This isn't sure-fire but tries to check that we don't have a 111 // leader going into the RPC, so we exercise the retry logic. 112 if ok, _ := s1.getLeader(); ok { 113 t.Fatalf("should not have a leader yet") 114 } 115 116 // The timeout is long enough to ride out any reasonable leader 117 // election. 118 err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out) 119 if err != nil { 120 t.Fatalf("bad: %v", err) 121 } 122 } 123 124 type MockSink struct { 125 *bytes.Buffer 126 cancel bool 127 } 128 129 func (m *MockSink) ID() string { 130 return "Mock" 131 } 132 133 func (m *MockSink) Cancel() error { 134 m.cancel = true 135 return nil 136 } 137 138 func (m *MockSink) Close() error { 139 return nil 140 } 141 142 func TestRPC_blockingQuery(t *testing.T) { 143 t.Parallel() 144 dir, s := testServer(t) 145 defer os.RemoveAll(dir) 146 defer s.Shutdown() 147 148 require := require.New(t) 149 assert := assert.New(t) 150 151 // Perform a non-blocking query. Note that it's significant that the meta has 152 // a zero index in response - the implied opts.MinQueryIndex is also zero but 153 // this should not block still. 154 { 155 var opts structs.QueryOptions 156 var meta structs.QueryMeta 157 var calls int 158 fn := func(ws memdb.WatchSet, state *state.Store) error { 159 calls++ 160 return nil 161 } 162 if err := s.blockingQuery(&opts, &meta, fn); err != nil { 163 t.Fatalf("err: %v", err) 164 } 165 if calls != 1 { 166 t.Fatalf("bad: %d", calls) 167 } 168 } 169 170 // Perform a blocking query that gets woken up and loops around once. 171 { 172 opts := structs.QueryOptions{ 173 MinQueryIndex: 3, 174 } 175 var meta structs.QueryMeta 176 var calls int 177 fn := func(ws memdb.WatchSet, state *state.Store) error { 178 if calls == 0 { 179 meta.Index = 3 180 181 fakeCh := make(chan struct{}) 182 close(fakeCh) 183 ws.Add(fakeCh) 184 } else { 185 meta.Index = 4 186 } 187 calls++ 188 return nil 189 } 190 if err := s.blockingQuery(&opts, &meta, fn); err != nil { 191 t.Fatalf("err: %v", err) 192 } 193 if calls != 2 { 194 t.Fatalf("bad: %d", calls) 195 } 196 } 197 198 // Perform a blocking query that returns a zero index from blocking func (e.g. 199 // no state yet). This should still return an empty response immediately, but 200 // with index of 1 and then block on the next attempt. In one sense zero index 201 // is not really a valid response from a state method that is not an error but 202 // in practice a lot of state store operations do return it unless they 203 // explicitly special checks to turn 0 into 1. Often this is not caught or 204 // covered by tests but eventually when hit in the wild causes blocking 205 // clients to busy loop and burn CPU. This test ensure that blockingQuery 206 // systematically does the right thing to prevent future bugs like that. 207 { 208 opts := structs.QueryOptions{ 209 MinQueryIndex: 0, 210 } 211 var meta structs.QueryMeta 212 var calls int 213 fn := func(ws memdb.WatchSet, state *state.Store) error { 214 if opts.MinQueryIndex > 0 { 215 // If client requested blocking, block forever. This is simulating 216 // waiting for the watched resource to be initialized/written to giving 217 // it a non-zero index. Note the timeout on the query options is relied 218 // on to stop the test taking forever. 219 fakeCh := make(chan struct{}) 220 ws.Add(fakeCh) 221 } 222 meta.Index = 0 223 calls++ 224 return nil 225 } 226 require.NoError(s.blockingQuery(&opts, &meta, fn)) 227 assert.Equal(1, calls) 228 assert.Equal(uint64(1), meta.Index, 229 "expect fake index of 1 to force client to block on next update") 230 231 // Simulate client making next request 232 opts.MinQueryIndex = 1 233 opts.MaxQueryTime = 20 * time.Millisecond // Don't wait too long 234 235 // This time we should block even though the func returns index 0 still 236 t0 := time.Now() 237 require.NoError(s.blockingQuery(&opts, &meta, fn)) 238 t1 := time.Now() 239 assert.Equal(2, calls) 240 assert.Equal(uint64(1), meta.Index, 241 "expect fake index of 1 to force client to block on next update") 242 assert.True(t1.Sub(t0) > 20*time.Millisecond, 243 "should have actually blocked waiting for timeout") 244 245 } 246 247 // Perform a query that blocks and gets interrupted when the state store 248 // is abandoned. 249 { 250 opts := structs.QueryOptions{ 251 MinQueryIndex: 3, 252 } 253 var meta structs.QueryMeta 254 var calls int 255 fn := func(ws memdb.WatchSet, state *state.Store) error { 256 if calls == 0 { 257 meta.Index = 3 258 259 snap, err := s.fsm.Snapshot() 260 if err != nil { 261 t.Fatalf("err: %v", err) 262 } 263 defer snap.Release() 264 265 buf := bytes.NewBuffer(nil) 266 sink := &MockSink{buf, false} 267 if err := snap.Persist(sink); err != nil { 268 t.Fatalf("err: %v", err) 269 } 270 271 if err := s.fsm.Restore(sink); err != nil { 272 t.Fatalf("err: %v", err) 273 } 274 } 275 calls++ 276 return nil 277 } 278 if err := s.blockingQuery(&opts, &meta, fn); err != nil { 279 t.Fatalf("err: %v", err) 280 } 281 if calls != 1 { 282 t.Fatalf("bad: %d", calls) 283 } 284 } 285 } 286 287 func TestRPC_ReadyForConsistentReads(t *testing.T) { 288 t.Parallel() 289 dir, s := testServerWithConfig(t, func(c *Config) { 290 c.RPCHoldTimeout = 2 * time.Millisecond 291 }) 292 defer os.RemoveAll(dir) 293 defer s.Shutdown() 294 295 testrpc.WaitForLeader(t, s.RPC, "dc1") 296 297 if !s.isReadyForConsistentReads() { 298 t.Fatal("Server should be ready for consistent reads") 299 } 300 301 s.resetConsistentReadReady() 302 err := s.consistentRead() 303 if err.Error() != "Not ready to serve consistent reads" { 304 t.Fatal("Server should NOT be ready for consistent reads") 305 } 306 307 go func() { 308 time.Sleep(100 * time.Millisecond) 309 s.setConsistentReadReady() 310 }() 311 312 retry.Run(t, func(r *retry.R) { 313 if err := s.consistentRead(); err != nil { 314 r.Fatalf("Expected server to be ready for consistent reads, got error %v", err) 315 } 316 }) 317 }