gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/cluster_test.go (about) 1 package rethinkdb 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "fmt" 8 "github.com/stretchr/testify/mock" 9 test "gopkg.in/check.v1" 10 "gopkg.in/rethinkdb/rethinkdb-go.v6/encoding" 11 p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2" 12 "io" 13 "net" 14 "time" 15 ) 16 17 type ClusterSuite struct{} 18 19 var _ = test.Suite(&ClusterSuite{}) 20 21 func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_Ok(c *test.C) { 22 host1 := Host{Name: "host1", Port: 28015} 23 node1 := "node1" 24 25 conn1 := &connMock{} 26 expectServerQuery(conn1, 1, node1) 27 conn1.onCloseReturn(nil) 28 conn2 := &connMock{} 29 conn2.onCloseReturn(nil) 30 31 dialMock := &mockDial{} 32 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 33 dialMock.On("Dial", host1.String()).Return(conn2, nil).Once() 34 35 opts := &ConnectOpts{} 36 seeds := []Host{host1} 37 cluster := &Cluster{ 38 hp: newHostPool(opts), 39 seeds: seeds, 40 opts: opts, 41 closed: clusterWorking, 42 connFactory: mockedConnectionFactory(dialMock), 43 } 44 45 err := cluster.run() 46 c.Assert(err, test.IsNil) 47 conn1.waitDial() 48 conn2.waitDial() 49 err = cluster.Close() 50 c.Assert(err, test.IsNil) 51 conn1.waitDone() 52 conn2.waitDone() 53 mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2) 54 } 55 56 func (s *ClusterSuite) TestCluster_NewMultiple_NoDiscover_Ok(c *test.C) { 57 host1 := Host{Name: "host1", Port: 28015} 58 host2 := Host{Name: "host2", Port: 28015} 59 node1 := "node1" 60 node2 := "node2" 61 62 conn1 := &connMock{} 63 expectServerQuery(conn1, 1, node1) 64 conn1.onCloseReturn(nil) 65 conn2 := &connMock{} 66 conn2.onCloseReturn(nil) 67 conn3 := &connMock{} 68 expectServerQuery(conn3, 1, node2) 69 conn3.onCloseReturn(nil) 70 conn4 := &connMock{} 71 conn4.onCloseReturn(nil) 72 73 dialMock := &mockDial{} 74 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 75 dialMock.On("Dial", host1.String()).Return(conn2, nil).Once() 76 dialMock.On("Dial", host2.String()).Return(conn3, nil).Once() 77 dialMock.On("Dial", host2.String()).Return(conn4, nil).Once() 78 79 opts := &ConnectOpts{} 80 seeds := []Host{host1, host2} 81 cluster := &Cluster{ 82 hp: newHostPool(opts), 83 seeds: seeds, 84 opts: opts, 85 closed: clusterWorking, 86 connFactory: mockedConnectionFactory(dialMock), 87 } 88 89 err := cluster.run() 90 c.Assert(err, test.IsNil) 91 conn1.waitDial() 92 conn2.waitDial() 93 conn3.waitDial() 94 conn4.waitDial() 95 err = cluster.Close() 96 c.Assert(err, test.IsNil) 97 conn1.waitDone() 98 conn2.waitDone() 99 conn3.waitDone() 100 conn4.waitDone() 101 mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2, conn3, conn4) 102 } 103 104 func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_DialFail(c *test.C) { 105 host1 := Host{Name: "host1", Port: 28015} 106 107 dialMock := &mockDial{} 108 dialMock.On("Dial", host1.String()).Return(nil, io.EOF).Once() 109 110 opts := &ConnectOpts{} 111 seeds := []Host{host1} 112 cluster := &Cluster{ 113 hp: newHostPool(opts), 114 seeds: seeds, 115 opts: opts, 116 closed: clusterWorking, 117 connFactory: mockedConnectionFactory(dialMock), 118 } 119 120 err := cluster.run() 121 c.Assert(err, test.Equals, io.EOF) 122 mock.AssertExpectationsForObjects(c, dialMock) 123 } 124 125 func (s *ClusterSuite) TestCluster_NewMultiple_NoDiscover_DialHalfFail(c *test.C) { 126 host1 := Host{Name: "host1", Port: 28015} 127 host2 := Host{Name: "host2", Port: 28015} 128 node1 := "node1" 129 130 conn1 := &connMock{} 131 expectServerQuery(conn1, 1, node1) 132 conn1.onCloseReturn(nil) 133 conn2 := &connMock{} 134 conn2.onCloseReturn(nil) 135 136 dialMock := &mockDial{} 137 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 138 dialMock.On("Dial", host1.String()).Return(conn2, nil).Once() 139 dialMock.On("Dial", host2.String()).Return(nil, io.EOF).Once() 140 141 opts := &ConnectOpts{} 142 seeds := []Host{host1, host2} 143 cluster := &Cluster{ 144 hp: newHostPool(opts), 145 seeds: seeds, 146 opts: opts, 147 closed: clusterWorking, 148 connFactory: mockedConnectionFactory(dialMock), 149 } 150 151 err := cluster.run() 152 c.Assert(err, test.IsNil) 153 conn1.waitDial() 154 conn2.waitDial() 155 err = cluster.Close() 156 c.Assert(err, test.IsNil) 157 conn1.waitDone() 158 conn2.waitDone() 159 mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2) 160 } 161 162 func (s *ClusterSuite) TestCluster_NewMultiple_NoDiscover_DialFail(c *test.C) { 163 host1 := Host{Name: "host1", Port: 28015} 164 host2 := Host{Name: "host2", Port: 28015} 165 166 dialMock := &mockDial{} 167 dialMock.On("Dial", host1.String()).Return(nil, io.EOF).Once() 168 dialMock.On("Dial", host2.String()).Return(nil, io.EOF).Once() 169 170 opts := &ConnectOpts{} 171 seeds := []Host{host1, host2} 172 cluster := &Cluster{ 173 hp: newHostPool(opts), 174 seeds: seeds, 175 opts: opts, 176 closed: clusterWorking, 177 connFactory: mockedConnectionFactory(dialMock), 178 } 179 180 err := cluster.run() 181 c.Assert(err, test.Equals, io.EOF) 182 mock.AssertExpectationsForObjects(c, dialMock) 183 } 184 185 func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_ServerFail(c *test.C) { 186 host1 := Host{Name: "host1", Port: 28015} 187 188 conn1 := &connMock{} 189 expectServerQueryFail(conn1, 1, io.EOF) 190 conn1.onCloseReturn(nil) 191 192 dialMock := &mockDial{} 193 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 194 195 opts := &ConnectOpts{} 196 seeds := []Host{host1} 197 cluster := &Cluster{ 198 hp: newHostPool(opts), 199 seeds: seeds, 200 opts: opts, 201 closed: clusterWorking, 202 connFactory: mockedConnectionFactory(dialMock), 203 } 204 205 err := cluster.run() 206 c.Assert(err, test.NotNil) 207 if _, ok := err.(RQLConnectionError); ok { 208 c.Assert(err, test.Equals, RQLConnectionError{rqlError(io.EOF.Error())}) 209 } else { 210 c.Assert(err, test.Equals, ErrConnectionClosed) 211 } 212 conn1.waitDone() 213 mock.AssertExpectationsForObjects(c, dialMock, conn1) 214 } 215 216 func (s *ClusterSuite) TestCluster_NewSingle_NoDiscover_PingFail(c *test.C) { 217 host1 := Host{Name: "host1", Port: 28015} 218 node1 := "node1" 219 220 conn1 := &connMock{} 221 expectServerQuery(conn1, 1, node1) 222 conn1.onCloseReturn(nil) 223 224 dialMock := &mockDial{} 225 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 226 dialMock.On("Dial", host1.String()).Return(nil, io.EOF).Once() 227 228 opts := &ConnectOpts{} 229 seeds := []Host{host1} 230 cluster := &Cluster{ 231 hp: newHostPool(opts), 232 seeds: seeds, 233 opts: opts, 234 closed: clusterWorking, 235 connFactory: mockedConnectionFactory(dialMock), 236 } 237 238 err := cluster.run() 239 c.Assert(err, test.Equals, io.EOF) 240 conn1.waitDone() 241 mock.AssertExpectationsForObjects(c, dialMock, conn1) 242 } 243 244 func (s *ClusterSuite) TestCluster_NewSingle_Discover_Ok(c *test.C) { 245 host1 := Host{Name: "host1", Port: 28015} 246 host2 := Host{Name: "1.1.1.1", Port: 2222} 247 host3 := Host{Name: "2.2.2.2", Port: 3333} 248 node1 := "node1" 249 node2 := "node2" 250 node3 := "node3" 251 252 conn1 := &connMock{} 253 expectServerQuery(conn1, 1, node1) 254 conn1.onCloseReturn(nil) 255 conn2 := &connMock{} 256 expectServerStatus(conn2, 1, []string{node1, node2, node3}, []Host{host1, host2, host3}) 257 conn2.onCloseReturn(nil) 258 conn3 := &connMock{} 259 conn3.onCloseReturn(nil) 260 conn4 := &connMock{} // doesn't need call Server() due to it's known through ServerStatus() 261 conn4.onCloseReturn(nil) 262 263 dialMock := &mockDial{} 264 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 265 dialMock.On("Dial", host1.String()).Return(conn2, nil).Once() 266 dialMock.On("Dial", host2.String()).Return(conn3, nil).Once() 267 dialMock.On("Dial", host3.String()).Return(conn4, nil).Once() 268 269 opts := &ConnectOpts{DiscoverHosts: true} 270 seeds := []Host{host1} 271 cluster := &Cluster{ 272 hp: newHostPool(opts), 273 seeds: seeds, 274 opts: opts, 275 closed: clusterWorking, 276 connFactory: mockedConnectionFactory(dialMock), 277 discoverInterval: 10 * time.Second, 278 } 279 280 err := cluster.run() 281 c.Assert(err, test.IsNil) 282 conn1.waitDial() 283 conn2.waitDial() 284 conn3.waitDial() 285 conn4.waitDial() 286 for !cluster.nodeExists(node2) || !cluster.nodeExists(node3) { // wait node to be added to list to be closed with cluster 287 time.Sleep(time.Millisecond) 288 } 289 err = cluster.Close() 290 c.Assert(err, test.IsNil) 291 conn1.waitDone() 292 conn2.waitDone() 293 conn3.waitDone() 294 conn4.waitDone() 295 mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2, conn3, conn4) 296 } 297 298 func (s *ClusterSuite) TestCluster_NewMultiple_Discover_Ok(c *test.C) { 299 host1 := Host{Name: "host1", Port: 28015} 300 host2 := Host{Name: "host2", Port: 28016} 301 host3 := Host{Name: "2.2.2.2", Port: 3333} 302 node1 := "node1" 303 node2 := "node2" 304 node3 := "node3" 305 306 conn1 := &connMock{} 307 expectServerQuery(conn1, 1, node1) 308 conn1.onCloseReturn(nil) 309 conn2 := &connMock{} 310 expectServerStatus(conn2, 1, []string{node1, node2, node3}, []Host{host1, host2, host3}) 311 conn2.onCloseReturn(nil) 312 conn3 := &connMock{} 313 expectServerQuery(conn3, 1, node2) 314 conn3.onCloseReturn(nil) 315 conn4 := &connMock{} 316 conn4.onCloseReturn(nil) 317 conn5 := &connMock{} // doesn't need call Server() due to it's known through ServerStatus() 318 conn5.onCloseReturn(nil) 319 320 dialMock := &mockDial{} 321 dialMock.On("Dial", host1.String()).Return(conn1, nil).Once() 322 dialMock.On("Dial", host1.String()).Return(conn2, nil).Once() 323 dialMock.On("Dial", host2.String()).Return(conn3, nil).Once() 324 dialMock.On("Dial", host2.String()).Return(conn4, nil).Once() 325 dialMock.On("Dial", host3.String()).Return(conn5, nil).Once() 326 327 opts := &ConnectOpts{DiscoverHosts: true} 328 seeds := []Host{host1, host2} 329 cluster := &Cluster{ 330 hp: newHostPool(opts), 331 seeds: seeds, 332 opts: opts, 333 closed: clusterWorking, 334 connFactory: mockedConnectionFactory(dialMock), 335 discoverInterval: 10 * time.Second, 336 } 337 338 err := cluster.run() 339 c.Assert(err, test.IsNil) 340 conn1.waitDial() 341 conn2.waitDial() 342 conn3.waitDial() 343 conn4.waitDial() 344 conn5.waitDial() 345 for !cluster.nodeExists(node3) { // wait node to be added to list to be closed with cluster 346 time.Sleep(time.Millisecond) 347 } 348 err = cluster.Close() 349 c.Assert(err, test.IsNil) 350 conn1.waitDone() 351 conn2.waitDone() 352 conn3.waitDone() 353 conn4.waitDone() 354 conn5.waitDone() 355 mock.AssertExpectationsForObjects(c, dialMock, conn1, conn2, conn3, conn4, conn5) 356 } 357 358 type mockDial struct { 359 mock.Mock 360 } 361 362 func mockedConnectionFactory(dial *mockDial) connFactory { 363 return func(host string, opts *ConnectOpts) (connection *Connection, err error) { 364 args := dial.MethodCalled("Dial", host) 365 err = args.Error(1) 366 if err != nil { 367 return nil, err 368 } 369 370 connection = newConnection(args.Get(0).(net.Conn), host, opts) 371 done := runConnection(connection) 372 373 m := args.Get(0).(*connMock) 374 m.setDone(done) 375 376 return connection, nil 377 } 378 } 379 380 func expectServerQuery(conn *connMock, token int64, nodeID string) { 381 writeChan := make(chan struct{}) 382 readChan := make(chan struct{}) 383 384 rawQ := makeServerQueryRaw(token) 385 conn.On("Write", rawQ).Return(0, nil, nil).Once().Run(func(args mock.Arguments) { 386 close(writeChan) 387 }) 388 389 rawR := makeServerResponseRaw(token, nodeID) 390 rawH := makeResponseHeaderRaw(token, len(rawR)) 391 392 conn.On("Read", respHeaderLen).Return(rawH, len(rawH), nil, nil).Once().Run(func(args mock.Arguments) { 393 <-writeChan 394 close(readChan) 395 }) 396 conn.On("Read", len(rawR)).Return(rawR, len(rawR), nil, nil).Once().Run(func(args mock.Arguments) { 397 <-readChan 398 }) 399 } 400 401 func expectServerQueryFail(conn *connMock, token int64, err error) { 402 writeChan := make(chan struct{}) 403 404 rawQ := makeServerQueryRaw(token) 405 conn.On("Write", rawQ).Return(0, nil, nil).Once().Run(func(args mock.Arguments) { 406 close(writeChan) 407 }) 408 409 conn.On("Read", respHeaderLen).Return(nil, 0, err, nil).Once().Run(func(args mock.Arguments) { 410 <-writeChan 411 }) 412 } 413 414 func makeServerQueryRaw(token int64) []byte { 415 buf := &bytes.Buffer{} 416 buf.Grow(respHeaderLen) 417 buf.Write(buf.Bytes()[:respHeaderLen]) 418 enc := json.NewEncoder(buf) 419 420 q := Query{ 421 Token: token, 422 Type: p.Query_SERVER_INFO, 423 } 424 425 err := enc.Encode(q.Build()) 426 if err != nil { 427 panic(fmt.Sprintf("must encode failed: %v", err)) 428 } 429 b := buf.Bytes() 430 binary.LittleEndian.PutUint64(b, uint64(q.Token)) 431 binary.LittleEndian.PutUint32(b[8:], uint32(len(b)-respHeaderLen)) 432 return b 433 } 434 435 func makeResponseHeaderRaw(token int64, respLen int) []byte { 436 buf1 := &bytes.Buffer{} 437 buf1.Grow(respHeaderLen) 438 buf1.Write(buf1.Bytes()[:respHeaderLen]) // reserve for header 439 b1 := buf1.Bytes() 440 binary.LittleEndian.PutUint64(b1, uint64(token)) 441 binary.LittleEndian.PutUint32(b1[8:], uint32(respLen)) 442 return b1 443 } 444 445 func makeServerResponseRaw(token int64, nodeID string) []byte { 446 buf2 := &bytes.Buffer{} 447 enc := json.NewEncoder(buf2) 448 449 coded, err := encoding.Encode(&ServerResponse{ID: nodeID}) 450 if err != nil { 451 panic(fmt.Sprintf("must encode response failed: %v", err)) 452 } 453 jresp, err := json.Marshal(coded) 454 if err != nil { 455 panic(fmt.Sprintf("must encode response failed: %v", err)) 456 } 457 458 resp := Response{Token: token, Type: p.Response_SERVER_INFO, Responses: []json.RawMessage{jresp}} 459 err = enc.Encode(resp) 460 if err != nil { 461 panic(fmt.Sprintf("must encode failed: %v", err)) 462 } 463 464 return buf2.Bytes() 465 } 466 467 func expectServerStatus(conn *connMock, token int64, nodeIDs []string, hosts []Host) { 468 writeChan := make(chan struct{}) 469 readHChan := make(chan struct{}) 470 readRChan := make(chan struct{}) 471 472 rawQ := makeServerStatusQueryRaw(token) 473 conn.On("Write", rawQ).Return(0, nil, nil).Once().Run(func(args mock.Arguments) { 474 close(writeChan) 475 }) 476 477 rawR := makeServerStatusResponseRaw(token, nodeIDs, hosts) 478 rawH := makeResponseHeaderRaw(token, len(rawR)) 479 480 conn.On("Read", respHeaderLen).Return(rawH, len(rawH), nil, nil).Once().Run(func(args mock.Arguments) { 481 <-writeChan 482 close(readHChan) 483 }) 484 conn.On("Read", len(rawR)).Return(rawR, len(rawR), nil, nil).Once().Run(func(args mock.Arguments) { 485 <-readHChan 486 close(readRChan) 487 }) 488 489 rawQ2 := makeContinueQueryRaw(token) 490 // maybe - connection may be closed until cursor fetchs next batch 491 conn.On("Write", rawQ2).Return(0, nil, nil).Maybe().Run(func(args mock.Arguments) { 492 <-readRChan 493 }) 494 } 495 496 func makeServerStatusQueryRaw(token int64) []byte { 497 buf := &bytes.Buffer{} 498 buf.Grow(respHeaderLen) 499 buf.Write(buf.Bytes()[:respHeaderLen]) // reserve for header 500 enc := json.NewEncoder(buf) 501 502 t := DB(SystemDatabase).Table(ServerStatusSystemTable).Changes(ChangesOpts{IncludeInitial: true}) 503 q, err := newQuery(t, map[string]interface{}{}, &ConnectOpts{}) 504 if err != nil { 505 panic(fmt.Sprintf("must newQuery failed: %v", err)) 506 } 507 q.Token = token 508 509 err = enc.Encode(q.Build()) 510 if err != nil { 511 panic(fmt.Sprintf("must encode failed: %v", err)) 512 } 513 514 b := buf.Bytes() 515 binary.LittleEndian.PutUint64(b, uint64(q.Token)) 516 binary.LittleEndian.PutUint32(b[8:], uint32(len(b)-respHeaderLen)) 517 return b 518 } 519 520 func makeServerStatusResponseRaw(token int64, nodeIDs []string, hosts []Host) []byte { 521 buf2 := &bytes.Buffer{} 522 enc := json.NewEncoder(buf2) 523 524 type change struct { 525 NewVal *nodeStatus `rethinkdb:"new_val"` 526 OldVal *nodeStatus `rethinkdb:"old_val"` 527 } 528 jresps := make([]json.RawMessage, len(nodeIDs)) 529 for i := range nodeIDs { 530 status := &nodeStatus{ID: nodeIDs[i], Network: nodeStatusNetwork{ 531 ReqlPort: int64(hosts[i].Port), 532 CanonicalAddresses: []nodeStatusNetworkAddr{ 533 {Host: hosts[i].Name}, 534 }, 535 }} 536 537 coded, err := encoding.Encode(&change{NewVal: status}) 538 if err != nil { 539 panic(fmt.Sprintf("must encode response failed: %v", err)) 540 } 541 jresps[i], err = json.Marshal(coded) 542 if err != nil { 543 panic(fmt.Sprintf("must encode response failed: %v", err)) 544 } 545 } 546 547 resp := Response{Token: token, Type: p.Response_SUCCESS_PARTIAL, Responses: jresps} 548 err := enc.Encode(resp) 549 if err != nil { 550 panic(fmt.Sprintf("must encode failed: %v", err)) 551 } 552 return buf2.Bytes() 553 } 554 555 func makeContinueQueryRaw(token int64) []byte { 556 buf := &bytes.Buffer{} 557 buf.Grow(respHeaderLen) 558 buf.Write(buf.Bytes()[:respHeaderLen]) // reserve for header 559 enc := json.NewEncoder(buf) 560 561 q := Query{Token: token, Type: p.Query_CONTINUE} 562 err := enc.Encode(q.Build()) 563 if err != nil { 564 panic(fmt.Sprintf("must encode failed: %v", err)) 565 } 566 567 b := buf.Bytes() 568 binary.LittleEndian.PutUint64(b, uint64(q.Token)) 569 binary.LittleEndian.PutUint32(b[8:], uint32(len(b)-respHeaderLen)) 570 return b 571 }