github.com/matrixorigin/matrixone@v0.7.0/pkg/common/morpc/backend.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package morpc 16 17 import ( 18 "context" 19 "fmt" 20 "runtime" 21 "sync" 22 "sync/atomic" 23 "time" 24 25 "github.com/fagongzi/goetty/v2" 26 "github.com/google/uuid" 27 "github.com/matrixorigin/matrixone/pkg/common/moerr" 28 "github.com/matrixorigin/matrixone/pkg/common/stopper" 29 "github.com/matrixorigin/matrixone/pkg/logutil" 30 "github.com/matrixorigin/matrixone/pkg/util/errutil" 31 "go.uber.org/zap" 32 ) 33 34 var ( 35 stateRunning = int32(0) 36 stateStopped = int32(1) 37 38 backendClosed = moerr.NewBackendClosedNoCtx() 39 messageSkipped = moerr.NewInvalidStateNoCtx("request is skipped") 40 ) 41 42 // WithBackendLogger set the backend logger 43 func WithBackendLogger(logger *zap.Logger) BackendOption { 44 return func(rb *remoteBackend) { 45 rb.logger = logger 46 } 47 } 48 49 // WithBackendBufferSize set the buffer size of the wait send chan. 50 // Default is 1024. 51 func WithBackendBufferSize(size int) BackendOption { 52 return func(rb *remoteBackend) { 53 rb.options.bufferSize = size 54 } 55 } 56 57 // WithBackendBusyBufferSize if len(writeC) >= size, backend is busy. 58 // Default is 3/4 buffer size. 59 func WithBackendBusyBufferSize(size int) BackendOption { 60 return func(rb *remoteBackend) { 61 rb.options.busySize = size 62 } 63 } 64 65 // WithBackendFilter set send fiter func. Input ready to send futures, output 66 // is really need to be send futures. 67 func WithBackendFilter(filter func(Message, string) bool) BackendOption { 68 return func(rb *remoteBackend) { 69 rb.options.filter = filter 70 } 71 } 72 73 // WithBackendBatchSendSize set the maximum number of messages to be sent together 74 // at each batch. Default is 8. 75 func WithBackendBatchSendSize(size int) BackendOption { 76 return func(rb *remoteBackend) { 77 rb.options.batchSendSize = size 78 } 79 } 80 81 // WithBackendConnectTimeout set the timeout for connect to remote. Default 10s. 82 func WithBackendConnectTimeout(timeout time.Duration) BackendOption { 83 return func(rb *remoteBackend) { 84 rb.options.connectTimeout = timeout 85 } 86 } 87 88 // WithBackendHasPayloadResponse has payload response means read a response that hold 89 // a slice of data in the read buffer to avoid data copy. 90 func WithBackendHasPayloadResponse() BackendOption { 91 return func(rb *remoteBackend) { 92 rb.options.hasPayloadResponse = true 93 } 94 } 95 96 // WithBackendStreamBufferSize set buffer size for stream receive message chan 97 func WithBackendStreamBufferSize(value int) BackendOption { 98 return func(rb *remoteBackend) { 99 rb.options.streamBufferSize = value 100 } 101 } 102 103 // WithBackendGoettyOptions set goetty connection options. e.g. set read/write buffer 104 // size, adjust net.Conn attribute etc. 105 func WithBackendGoettyOptions(options ...goetty.Option) BackendOption { 106 return func(rb *remoteBackend) { 107 rb.options.goettyOptions = options 108 } 109 } 110 111 type remoteBackend struct { 112 remote string 113 logger *zap.Logger 114 codec Codec 115 conn goetty.IOSession 116 writeC chan *Future 117 stopWriteC chan struct{} 118 resetConnC chan struct{} 119 stopper *stopper.Stopper 120 readStopper *stopper.Stopper 121 closeOnce sync.Once 122 123 options struct { 124 hasPayloadResponse bool 125 goettyOptions []goetty.Option 126 connectTimeout time.Duration 127 bufferSize int 128 busySize int 129 batchSendSize int 130 streamBufferSize int 131 filter func(msg Message, backendAddr string) bool 132 } 133 134 stateMu struct { 135 sync.RWMutex 136 state int32 137 readLoopActive bool 138 locked bool 139 } 140 141 mu struct { 142 sync.RWMutex 143 futures map[uint64]*Future 144 activeStreams map[uint64]*stream 145 } 146 147 atomic struct { 148 id uint64 149 lastActiveTime atomic.Value //time.Time 150 } 151 152 pool struct { 153 streams *sync.Pool 154 futures *sync.Pool 155 } 156 } 157 158 // NewRemoteBackend create a goetty connection based backend. This backend will start 2 159 // goroutiune, one for read and one for write. If there is a network error in the underlying 160 // goetty connection, it will automatically retry until the Future times out. 161 func NewRemoteBackend( 162 remote string, 163 codec Codec, 164 options ...BackendOption) (Backend, error) { 165 rb := &remoteBackend{ 166 stopper: stopper.NewStopper(fmt.Sprintf("backend-write-%s", remote)), 167 readStopper: stopper.NewStopper(fmt.Sprintf("backend-read-%s", remote)), 168 remote: remote, 169 codec: codec, 170 resetConnC: make(chan struct{}), 171 stopWriteC: make(chan struct{}), 172 } 173 174 for _, opt := range options { 175 opt(rb) 176 } 177 rb.adjust() 178 179 rb.pool.futures = &sync.Pool{ 180 New: func() interface{} { 181 return newFuture(rb.releaseFuture) 182 }, 183 } 184 rb.pool.streams = &sync.Pool{ 185 New: func() any { 186 return newStream(make(chan Message, rb.options.streamBufferSize), 187 rb.newFuture, 188 rb.doSend, 189 rb.removeActiveStream, 190 rb.active) 191 }, 192 } 193 rb.writeC = make(chan *Future, rb.options.bufferSize) 194 rb.mu.futures = make(map[uint64]*Future, rb.options.bufferSize) 195 rb.mu.activeStreams = make(map[uint64]*stream, rb.options.bufferSize) 196 if rb.options.hasPayloadResponse { 197 rb.options.goettyOptions = append(rb.options.goettyOptions, 198 goetty.WithSessionDisableAutoResetInBuffer()) 199 } 200 rb.conn = goetty.NewIOSession(rb.options.goettyOptions...) 201 202 if err := rb.resetConn(); err != nil { 203 rb.logger.Error("connect to remote failed", zap.Error(err)) 204 return nil, err 205 } 206 rb.activeReadLoop(false) 207 208 if err := rb.stopper.RunTask(rb.writeLoop); err != nil { 209 return nil, err 210 } 211 212 rb.active() 213 return rb, nil 214 } 215 216 func (rb *remoteBackend) adjust() { 217 if rb.options.bufferSize == 0 { 218 rb.options.bufferSize = 1024 219 } 220 if rb.options.busySize == 0 { 221 rb.options.busySize = rb.options.bufferSize * 3 / 4 222 if rb.options.busySize == 0 { 223 rb.options.busySize = 1 224 } 225 } 226 if rb.options.batchSendSize == 0 { 227 rb.options.batchSendSize = 8 228 } 229 if rb.options.connectTimeout == 0 { 230 rb.options.connectTimeout = time.Second * 10 231 } 232 if rb.options.streamBufferSize == 0 { 233 rb.options.streamBufferSize = 16 234 } 235 if rb.options.filter == nil { 236 rb.options.filter = func(Message, string) bool { 237 return true 238 } 239 } 240 241 rb.logger = logutil.Adjust(rb.logger).With(zap.String("remote", rb.remote), 242 zap.String("backend-id", uuid.NewString())) 243 rb.options.goettyOptions = append(rb.options.goettyOptions, 244 goetty.WithSessionCodec(rb.codec), 245 goetty.WithSessionLogger(rb.logger)) 246 } 247 248 func (rb *remoteBackend) Send(ctx context.Context, request Message) (*Future, error) { 249 return rb.send(ctx, request, false) 250 } 251 252 func (rb *remoteBackend) SendInternal(ctx context.Context, request Message) (*Future, error) { 253 return rb.send(ctx, request, true) 254 } 255 256 func (rb *remoteBackend) send(ctx context.Context, request Message, internal bool) (*Future, error) { 257 rb.active() 258 request.SetID(rb.nextID()) 259 260 f := rb.newFuture() 261 f.init(RPCMessage{Ctx: ctx, Message: request, internal: internal}) 262 rb.addFuture(f) 263 264 if err := rb.doSend(f); err != nil { 265 f.Close() 266 return nil, err 267 } 268 return f, nil 269 } 270 271 func (rb *remoteBackend) NewStream(unlockAfterClose bool) (Stream, error) { 272 rb.active() 273 rb.stateMu.RLock() 274 defer rb.stateMu.RUnlock() 275 276 if rb.stateMu.state == stateStopped { 277 return nil, moerr.NewBackendClosedNoCtx() 278 } 279 280 rb.mu.Lock() 281 defer rb.mu.Unlock() 282 283 st := rb.acquireStream() 284 st.init(rb.nextID(), unlockAfterClose) 285 rb.mu.activeStreams[st.ID()] = st 286 return st, nil 287 } 288 289 func (rb *remoteBackend) doSend(f *Future) error { 290 if err := rb.codec.Valid(f.send.Message); err != nil { 291 return err 292 } 293 294 for { 295 rb.stateMu.RLock() 296 if rb.stateMu.state == stateStopped { 297 rb.stateMu.RUnlock() 298 return moerr.NewBackendClosedNoCtx() 299 } 300 301 // The close method need acquire the write lock, so we cannot block at here. 302 // The write loop may reset the backend's network link and may not be able to 303 // process writeC for a long time, causing the writeC buffer to reach its limit. 304 select { 305 case rb.writeC <- f: 306 rb.stateMu.RUnlock() 307 return nil 308 case <-f.send.Ctx.Done(): 309 rb.stateMu.RUnlock() 310 return f.send.Ctx.Err() 311 default: 312 rb.stateMu.RUnlock() 313 } 314 } 315 } 316 317 func (rb *remoteBackend) Close() { 318 rb.stateMu.Lock() 319 if rb.stateMu.state == stateStopped { 320 rb.stateMu.Unlock() 321 return 322 } 323 rb.stateMu.state = stateStopped 324 rb.stopWriteLoop() 325 rb.stateMu.Unlock() 326 327 rb.stopper.Stop() 328 rb.doClose() 329 } 330 331 func (rb *remoteBackend) Busy() bool { 332 return len(rb.writeC) >= rb.options.busySize 333 } 334 335 func (rb *remoteBackend) LastActiveTime() time.Time { 336 return rb.atomic.lastActiveTime.Load().(time.Time) 337 } 338 339 func (rb *remoteBackend) Lock() { 340 rb.stateMu.Lock() 341 defer rb.stateMu.Unlock() 342 if rb.stateMu.locked { 343 panic("backend is already locked") 344 } 345 rb.stateMu.locked = true 346 } 347 348 func (rb *remoteBackend) Unlock() { 349 rb.stateMu.Lock() 350 defer rb.stateMu.Unlock() 351 if !rb.stateMu.locked { 352 panic("backend is not locked") 353 } 354 rb.stateMu.locked = false 355 } 356 357 func (rb *remoteBackend) Locked() bool { 358 rb.stateMu.RLock() 359 defer rb.stateMu.RUnlock() 360 return rb.stateMu.locked 361 } 362 363 func (rb *remoteBackend) active() { 364 now := time.Now() 365 rb.atomic.lastActiveTime.Store(now) 366 } 367 368 func (rb *remoteBackend) inactive() { 369 rb.atomic.lastActiveTime.Store(time.Time{}) 370 } 371 372 func (rb *remoteBackend) writeLoop(ctx context.Context) { 373 rb.logger.Info("write loop started") 374 defer func() { 375 rb.readStopper.Stop() 376 rb.closeConn(true) 377 rb.logger.Info("write loop stopped") 378 }() 379 380 defer func() { 381 rb.makeAllWritesDoneWithClosed(ctx) 382 close(rb.writeC) 383 }() 384 385 messages := make([]*Future, 0, rb.options.batchSendSize) 386 stopped := false 387 for { 388 messages, stopped = rb.fetch(ctx, messages, rb.options.batchSendSize) 389 if len(messages) > 0 { 390 written := 0 391 writeTimeout := time.Duration(0) 392 for _, f := range messages { 393 id := f.getSendMessageID() 394 if stopped { 395 f.messageSended(backendClosed) 396 continue 397 } 398 399 if v := rb.doWrite(ctx, id, f); v > 0 { 400 writeTimeout += v 401 written++ 402 } 403 } 404 405 if written > 0 { 406 if err := rb.conn.Flush(writeTimeout); err != nil { 407 for _, f := range messages { 408 if rb.options.filter(f.send.Message, rb.remote) { 409 id := f.getSendMessageID() 410 rb.logger.Error("write request failed", 411 zap.Uint64("request-id", id), 412 zap.Error(err)) 413 f.messageSended(err) 414 } 415 } 416 } 417 } 418 419 for _, m := range messages { 420 m.messageSended(nil) 421 } 422 } 423 if stopped { 424 return 425 } 426 } 427 } 428 429 func (rb *remoteBackend) doWrite(ctx context.Context, id uint64, f *Future) time.Duration { 430 if !rb.options.filter(f.send.Message, rb.remote) { 431 f.messageSended(messageSkipped) 432 return 0 433 } 434 // already timeout in future, and future will get a ctx timeout 435 if f.send.Timeout() { 436 f.messageSended(f.send.Ctx.Err()) 437 return 0 438 } 439 440 v, err := f.send.GetTimeoutFromContext() 441 if err != nil { 442 f.messageSended(err) 443 return 0 444 } 445 446 // For PayloadMessage, the internal Codec will write the Payload directly to the underlying socket 447 // instead of copying it to the buffer, so the write deadline of the underlying conn needs to be reset 448 // here, otherwise an old deadline will be out causing io/timeout. 449 conn := rb.conn.RawConn() 450 if _, ok := f.send.Message.(PayloadMessage); ok && conn != nil { 451 conn.SetWriteDeadline(time.Now().Add(v)) 452 } 453 if ce := rb.logger.Check(zap.DebugLevel, "write request"); ce != nil { 454 ce.Write(zap.Uint64("request-id", id), 455 zap.String("request", f.send.Message.DebugString())) 456 } 457 if err := rb.conn.Write(f.send, goetty.WriteOptions{}); err != nil { 458 rb.logger.Error("write request failed", 459 zap.Uint64("request-id", id), 460 zap.Error(err)) 461 f.messageSended(err) 462 return 0 463 } 464 return v 465 } 466 467 func (rb *remoteBackend) readLoop(ctx context.Context) { 468 rb.logger.Info("read loop started") 469 defer rb.logger.Error("read loop stopped") 470 471 wg := &sync.WaitGroup{} 472 var cb func() 473 if rb.options.hasPayloadResponse { 474 cb = wg.Done 475 } 476 477 for { 478 select { 479 case <-ctx.Done(): 480 rb.clean() 481 return 482 default: 483 msg, err := rb.conn.Read(goetty.ReadOptions{}) 484 if err != nil { 485 rb.logger.Error("read from backend failed", 486 zap.Error(err)) 487 rb.inactiveReadLoop() 488 rb.cancelActiveStreams() 489 rb.scheduleResetConn() 490 return 491 } 492 493 rb.active() 494 495 if rb.options.hasPayloadResponse { 496 wg.Add(1) 497 } 498 resp := msg.(RPCMessage).Message 499 rb.requestDone(ctx, resp.GetID(), msg.(RPCMessage), nil, cb) 500 if rb.options.hasPayloadResponse { 501 wg.Wait() 502 } 503 } 504 } 505 } 506 507 func (rb *remoteBackend) fetch(ctx context.Context, 508 messages []*Future, 509 maxFetchCount int) ([]*Future, bool) { 510 n := len(messages) 511 for i := 0; i < n; i++ { 512 messages[i] = nil 513 } 514 messages = messages[:0] 515 select { 516 case f := <-rb.writeC: 517 messages = append(messages, f) 518 n := maxFetchCount - 1 519 OUTER: 520 for i := 0; i < n; i++ { 521 select { 522 case f := <-rb.writeC: 523 messages = append(messages, f) 524 default: 525 break OUTER 526 } 527 } 528 case <-rb.resetConnC: 529 rb.handleResetConn() 530 case <-rb.stopWriteC: 531 return messages, true 532 } 533 return messages, false 534 } 535 536 func (rb *remoteBackend) makeAllWritesDoneWithClosed(ctx context.Context) { 537 for { 538 select { 539 case m := <-rb.writeC: 540 m.messageSended(backendClosed) 541 default: 542 return 543 } 544 } 545 } 546 547 func (rb *remoteBackend) handleResetConn() { 548 if err := rb.resetConn(); err != nil { 549 rb.logger.Error("fail to reset backend connection", 550 zap.Error(err)) 551 rb.inactive() 552 } 553 } 554 555 func (rb *remoteBackend) doClose() { 556 rb.closeOnce.Do(func() { 557 close(rb.resetConnC) 558 rb.closeConn(false) 559 }) 560 } 561 562 func (rb *remoteBackend) clean() { 563 rb.mu.Lock() 564 defer rb.mu.Unlock() 565 566 for id := range rb.mu.futures { 567 delete(rb.mu.futures, id) 568 } 569 } 570 571 func (rb *remoteBackend) acquireStream() *stream { 572 return rb.pool.streams.Get().(*stream) 573 } 574 575 func (rb *remoteBackend) cancelActiveStreams() { 576 rb.mu.Lock() 577 defer rb.mu.Unlock() 578 579 for _, st := range rb.mu.activeStreams { 580 st.done(RPCMessage{}) 581 } 582 } 583 584 func (rb *remoteBackend) removeActiveStream(s *stream) { 585 rb.mu.Lock() 586 defer rb.mu.Unlock() 587 588 delete(rb.mu.activeStreams, s.id) 589 delete(rb.mu.futures, s.id) 590 if s.unlockAfterClose { 591 rb.Unlock() 592 } 593 rb.pool.streams.Put(s) 594 } 595 596 func (rb *remoteBackend) stopWriteLoop() { 597 rb.closeConn(false) 598 close(rb.stopWriteC) 599 } 600 601 func (rb *remoteBackend) requestDone(ctx context.Context, id uint64, msg RPCMessage, err error, cb func()) { 602 response := msg.Message 603 if ce := rb.logger.Check(zap.DebugLevel, "read response"); ce != nil { 604 debugStr := "" 605 if response != nil { 606 debugStr = response.DebugString() 607 } 608 ce.Write(zap.Uint64("request-id", id), 609 zap.String("response", debugStr), 610 zap.Error(err)) 611 } 612 613 rb.mu.Lock() 614 if f, ok := rb.mu.futures[id]; ok { 615 delete(rb.mu.futures, id) 616 rb.mu.Unlock() 617 if err == nil { 618 f.done(response, cb) 619 } else { 620 errutil.ReportError(ctx, err) 621 f.error(id, err, cb) 622 } 623 } else if st, ok := rb.mu.activeStreams[id]; ok { 624 rb.mu.Unlock() 625 if response != nil { 626 st.done(msg) 627 } 628 } else { 629 // future has been removed, e.g. it has timed out. 630 rb.mu.Unlock() 631 if cb != nil { 632 cb() 633 } 634 } 635 } 636 637 func (rb *remoteBackend) addFuture(f *Future) { 638 rb.mu.Lock() 639 defer rb.mu.Unlock() 640 641 f.ref() 642 rb.mu.futures[f.getSendMessageID()] = f 643 } 644 645 func (rb *remoteBackend) releaseFuture(f *Future) { 646 rb.mu.Lock() 647 defer rb.mu.Unlock() 648 649 delete(rb.mu.futures, f.getSendMessageID()) 650 f.reset() 651 rb.pool.futures.Put(f) 652 } 653 654 func (rb *remoteBackend) resetConn() error { 655 rb.stateMu.Lock() 656 defer rb.stateMu.Unlock() 657 658 start := time.Now() 659 wait := time.Second 660 sleep := time.Millisecond * 200 661 for { 662 if !rb.runningLocked() { 663 return moerr.NewBackendClosedNoCtx() 664 } 665 666 rb.logger.Info("start connect to remote") 667 rb.closeConn(false) 668 err := rb.conn.Connect(rb.remote, rb.options.connectTimeout) 669 if err == nil { 670 rb.logger.Info("connect to remote succeed") 671 rb.activeReadLoop(true) 672 return nil 673 } 674 rb.logger.Error("init remote connection failed, retry later", 675 zap.Error(err)) 676 677 duration := time.Duration(0) 678 for { 679 time.Sleep(sleep) 680 duration += sleep 681 if time.Since(start) > rb.options.connectTimeout { 682 return moerr.NewBackendClosedNoCtx() 683 } 684 if duration >= wait { 685 break 686 } 687 } 688 wait += wait / 2 689 } 690 } 691 692 func (rb *remoteBackend) activeReadLoop(locked bool) { 693 if !locked { 694 rb.stateMu.Lock() 695 defer rb.stateMu.Unlock() 696 } 697 698 if rb.stateMu.readLoopActive { 699 return 700 } 701 702 if err := rb.readStopper.RunTask(rb.readLoop); err != nil { 703 rb.logger.Error("active read loop failed", 704 zap.Error(err)) 705 return 706 } 707 rb.stateMu.readLoopActive = true 708 } 709 710 func (rb *remoteBackend) inactiveReadLoop() { 711 rb.stateMu.Lock() 712 defer rb.stateMu.Unlock() 713 714 rb.stateMu.readLoopActive = false 715 } 716 717 func (rb *remoteBackend) runningLocked() bool { 718 return rb.stateMu.state == stateRunning 719 } 720 721 func (rb *remoteBackend) scheduleResetConn() { 722 rb.stateMu.RLock() 723 defer rb.stateMu.RUnlock() 724 725 if !rb.runningLocked() { 726 return 727 } 728 729 select { 730 case rb.resetConnC <- struct{}{}: 731 rb.logger.Debug("schedule reset remote connection") 732 case <-time.After(time.Second * 10): 733 rb.logger.Fatal("BUG: schedule reset remote connection timeout") 734 } 735 } 736 737 func (rb *remoteBackend) closeConn(close bool) { 738 fn := rb.conn.Disconnect 739 if close { 740 fn = rb.conn.Close 741 } 742 743 if err := fn(); err != nil { 744 rb.logger.Error("close remote conn failed", 745 zap.Error(err)) 746 } 747 } 748 749 func (rb *remoteBackend) newFuture() *Future { 750 return rb.pool.futures.Get().(*Future) 751 } 752 753 func (rb *remoteBackend) nextID() uint64 { 754 return atomic.AddUint64(&rb.atomic.id, 1) 755 } 756 757 type goettyBasedBackendFactory struct { 758 codec Codec 759 options []BackendOption 760 } 761 762 func NewGoettyBasedBackendFactory(codec Codec, options ...BackendOption) BackendFactory { 763 return &goettyBasedBackendFactory{ 764 codec: codec, 765 options: options, 766 } 767 } 768 769 func (bf *goettyBasedBackendFactory) Create(remote string) (Backend, error) { 770 return NewRemoteBackend(remote, bf.codec, bf.options...) 771 } 772 773 type stream struct { 774 c chan Message 775 sendFunc func(*Future) error 776 activeFunc func() 777 unregisterFunc func(*stream) 778 newFutureFunc func() *Future 779 unlockAfterClose bool 780 ctx context.Context 781 cancel context.CancelFunc 782 783 // reset fields 784 id uint64 785 sequence uint32 786 lastReceivedSequence uint32 787 mu struct { 788 sync.RWMutex 789 closed bool 790 } 791 } 792 793 func newStream( 794 c chan Message, 795 acquireFutureFunc func() *Future, 796 sendFunc func(*Future) error, 797 unregisterFunc func(*stream), 798 activeFunc func()) *stream { 799 ctx, cancel := context.WithCancel(context.Background()) 800 s := &stream{ 801 c: c, 802 ctx: ctx, 803 cancel: cancel, 804 sendFunc: sendFunc, 805 unregisterFunc: unregisterFunc, 806 activeFunc: activeFunc, 807 newFutureFunc: acquireFutureFunc, 808 } 809 s.setFinalizer() 810 return s 811 } 812 813 func (s *stream) init(id uint64, unlockAfterClose bool) { 814 s.id = id 815 s.sequence = 0 816 s.unlockAfterClose = unlockAfterClose 817 s.lastReceivedSequence = 0 818 s.mu.closed = false 819 for { 820 select { 821 case <-s.c: 822 default: 823 return 824 } 825 } 826 } 827 828 func (s *stream) setFinalizer() { 829 runtime.SetFinalizer(s, func(s *stream) { 830 s.destroy() 831 }) 832 } 833 834 func (s *stream) destroy() { 835 close(s.c) 836 s.cancel() 837 } 838 839 func (s *stream) Send(ctx context.Context, request Message) error { 840 if s.id != request.GetID() { 841 panic("request.id != stream.id") 842 } 843 if _, ok := ctx.Deadline(); !ok { 844 panic("deadline not set in context") 845 } 846 s.activeFunc() 847 848 f := s.newFutureFunc() 849 f.ref() 850 defer f.Close() 851 852 s.mu.RLock() 853 if s.mu.closed { 854 s.mu.RUnlock() 855 return moerr.NewStreamClosedNoCtx() 856 } 857 858 err := s.doSendLocked(ctx, f, request) 859 // unlock before future.close to avoid deadlock with future.Close 860 // 1. current goroutine: stream.Rlock 861 // 2. backend read goroutine: cancelActiveStream -> backend.Lock 862 // 3. backend read goroutine: cancelActiveStream -> stream.Lock : deadlock here 863 // 4. current goroutine: f.Close -> backend.Lock : deadlock here 864 s.mu.RUnlock() 865 866 if err != nil { 867 return err 868 } 869 // stream only wait send completed 870 return f.waitSendCompleted() 871 } 872 873 func (s *stream) doSendLocked( 874 ctx context.Context, 875 f *Future, 876 request Message) error { 877 s.sequence++ 878 f.init(RPCMessage{ 879 Ctx: ctx, 880 Message: request, 881 stream: true, 882 streamSequence: s.sequence, 883 }) 884 885 return s.sendFunc(f) 886 } 887 888 func (s *stream) Receive() (chan Message, error) { 889 s.mu.RLock() 890 defer s.mu.RUnlock() 891 if s.mu.closed { 892 return nil, moerr.NewStreamClosedNoCtx() 893 } 894 return s.c, nil 895 } 896 897 func (s *stream) Close() error { 898 s.mu.Lock() 899 defer s.mu.Unlock() 900 901 if s.mu.closed { 902 return nil 903 } 904 905 // the stream is reuseable, so use nil to notify stream is closed 906 s.c <- nil 907 s.mu.closed = true 908 s.unregisterFunc(s) 909 return nil 910 } 911 912 func (s *stream) ID() uint64 { 913 return s.id 914 } 915 916 func (s *stream) done(message RPCMessage) { 917 s.mu.Lock() 918 defer s.mu.Unlock() 919 920 if s.mu.closed { 921 return 922 } 923 924 response := message.Message 925 if response != nil && !message.stream { 926 panic("BUG") 927 } 928 if response != nil && 929 message.streamSequence != s.lastReceivedSequence+1 { 930 response = nil 931 } 932 933 s.lastReceivedSequence = message.streamSequence 934 s.c <- response 935 }