stream_sender.go ×19

Frontier kind: Code frontier

unlabeled · c_6dc490746a64

3 tests · 3682 LOC · 161 files · introduces 0 tests · 117 LOC · 4 files

Introduces — evidence that enters the hierarchy at this concept

Code
24 ranges117 lines · 4 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
548 ranges3682 lines · 161 files · Browse complete extent
All tests (intent)
3 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

4 files ranked by introduced lines: 117 introduced LOC across 24 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

go.temporal.io/server/service/history/replication/stream_sender.go 98 introduced LOC · 19 ranges

Open complete file

503 Loop:
504 for iter.HasNext() {
505 > if s.shutdownChan.IsShutdown() { stream_sender.go
506 return nil
507 }
508
509 > item, err := iter.Next() stream_sender.go
510 > if err != nil {
511 return fmt.Errorf("streamSender unable to get next replication task: %w", err)
512 }
513
514 > skipCount++ stream_sender.go
515 > // To avoid a situation: we are skipping a lot of tasks and never send any task, receiver side will not have updated high watermark,
516 > // so it will not ACK back to sender, sender will not update the ACK level.
517 > // i.e. in tiered stack, if no low priority task in queue, we should still send watermark info to receiver to let it update ACK level.
518 > if skipCount > TaskMaxSkipCount {
519 if err := s.sendToStream(&historyservice.StreamWorkflowReplicationMessagesResponse{
520 Attributes: &historyservice.StreamWorkflowReplicationMessagesResponse_Messages{
530 skipCount = 0
531 }
532 > if priority != enumsspb.TASK_PRIORITY_UNSPECIFIED && // case: skip priority check. When priority is unspecified, send all tasks stream_sender.go
533 > priority != s.getTaskPriority(item) { // case: skip task with different priority than this loop
534 continue Loop
535 }
536 > if !s.shouldProcessTask(item) { stream_sender.go
537 continue Loop
538 }
539 > metrics.ReplicationTaskLoadLatency.With(s.metrics).Record( stream_sender.go
540 > time.Since(item.GetVisibilityTime()),
541 > metrics.FromClusterIDTag(s.serverShardKey.ClusterID),
542 > metrics.ToClusterIDTag(s.clientShardKey.ClusterID),
543 > metrics.OperationTag(TaskOperationTagFromTask(item.GetType())),
544 > metrics.ReplicationTaskPriorityTag(priority),
545 > )
546 >
547 > var attempt int64
548 > operation := func() error {
549 > attempt++
550 > startTime := time.Now().UTC()
551 > defer func() {
552 > metrics.ReplicationTaskGenerationLatency.With(s.metrics).Record(
553 > time.Since(startTime),
554 > metrics.FromClusterIDTag(s.serverShardKey.ClusterID),
555 > metrics.ToClusterIDTag(s.clientShardKey.ClusterID),
556 > metrics.OperationTag(TaskOperationTagFromTask(item.GetType())),
557 > metrics.ReplicationTaskPriorityTag(priority),
558 > )
559 > }()
560 > task, err := s.taskConverter.Convert(item, s.clientShardKey.ClusterID, priority)
561 > if err != nil {
562 return s.recordRetry(item, attempt, fmt.Errorf("convert: %w", err))
563 }
564 > if task == nil { stream_sender.go
565 return nil
566 }
567 > task.Priority = priority stream_sender.go
568 > if s.isTieredStackEnabled {
569 if err := s.flowController.Wait(s.server.Context(), priority); err != nil {
570 if errors.Is(err, context.Canceled) {
574 }
575 }
576 > if s.config.ReplicationEnableRateLimit() && task.Priority == enumsspb.TASK_PRIORITY_LOW { stream_sender.go
577 nsName, err := s.shardContext.GetNamespaceRegistry().GetNamespaceName(
578 namespace.ID(item.GetNamespaceID()),
595 metrics.ReplicationRateLimitLatency.With(s.metrics).Record(time.Since(rlStartTime), metrics.OperationTag(TaskOperationTag(task)))
596 }
597 > if s.config.EmitReplicationLifecycleEvents() { stream_sender.go
598 s.emitReplicationSent(task, item)
599 }
600 > if err := s.sendToStream(&historyservice.StreamWorkflowReplicationMessagesResponse{ stream_sender.go
601 > Attributes: &historyservice.StreamWorkflowReplicationMessagesResponse_Messages{
602 > Messages: &replicationspb.WorkflowReplicationMessages{
603 > ReplicationTasks: []*replicationspb.ReplicationTask{task},
604 > ExclusiveHighWatermark: task.SourceTaskId + 1,
605 > ExclusiveHighWatermarkTime: task.VisibilityTime,
606 > Priority: priority,
607 > },
608 > },
609 > }); err != nil {
610 return s.recordRetry(item, attempt, fmt.Errorf("send: %w", err))
611 }
612 > skipCount = 0 stream_sender.go
613 > metrics.ReplicationTasksSend.With(s.metrics).Record(
614 > int64(1),
615 > metrics.FromClusterIDTag(s.serverShardKey.ClusterID),
616 > metrics.ToClusterIDTag(s.clientShardKey.ClusterID),
617 > metrics.OperationTag(TaskOperationTag(task)),
618 > )
619 > return nil
620 }
621
622 > retryPolicy := backoff.NewExponentialRetryPolicy(s.config.ReplicationStreamSenderErrorRetryWait()). stream_sender.go
623 > WithBackoffCoefficient(s.config.ReplicationStreamSenderErrorRetryBackoffCoefficient()).
624 > WithMaximumInterval(s.config.ReplicationStreamSenderErrorRetryMaxInterval()).
625 > WithMaximumAttempts(s.config.ReplicationStreamSenderErrorRetryMaxAttempts()).
626 > WithExpirationInterval(s.config.ReplicationStreamSenderErrorRetryExpiration())
627 >
628 > err = backoff.ThrottleRetry(operation, retryPolicy, isRetryableError)
629 > metrics.ReplicationTaskSendAttempt.With(s.metrics).Record(
630 > attempt,
631 > metrics.FromClusterIDTag(s.serverShardKey.ClusterID),
632 > metrics.ToClusterIDTag(s.clientShardKey.ClusterID),
633 > metrics.OperationTag(TaskOperationTagFromTask(item.GetType())),
634 > metrics.ReplicationTaskPriorityTag(priority),
635 > )
636 > metrics.ReplicationTaskSendLatency.With(s.metrics).Record(
637 > time.Since(item.GetVisibilityTime()),
638 > metrics.FromClusterIDTag(s.serverShardKey.ClusterID),
639 > metrics.ToClusterIDTag(s.clientShardKey.ClusterID),
640 > metrics.OperationTag(TaskOperationTagFromTask(item.GetType())),
641 > metrics.ReplicationTaskPriorityTag(priority),
642 > )
643 > if err != nil {
644 metrics.ReplicationTaskSendError.With(s.metrics).Record(
645 int64(1),
674 }
675
676 > func (s *StreamSenderImpl) shouldProcessTask(item tasks.Task) bool { stream_sender.go
677 > clientShardID := common.WorkflowIDToHistoryShard(item.GetNamespaceID(), item.GetWorkflowID(), s.clientClusterShardCount)
678 > if clientShardID != s.clientShardKey.ShardID {
679 return false
680 }
681
682 > targetClusters := s.getTaskTargetCluster(item) stream_sender.go
683 > if len(targetClusters) != 0 && !slices.Contains(targetClusters, s.clientClusterName) {
684 return false
685 }
686
687 > var shouldProcessTask bool stream_sender.go
688 > namespaceEntry, err := s.shardContext.GetNamespaceRegistry().GetNamespaceByID(
689 > namespace.ID(item.GetNamespaceID()),
690 > )
691 > if err != nil {
692 // if there is error, then blindly send the task, better safe than sorry
693 shouldProcessTask = true
694 }
695
696 > if namespaceEntry != nil { stream_sender.go
697 > FilterLoop:
698 > for _, targetCluster := range namespaceEntry.ClusterNames(item.GetWorkflowID()) {
699 > if s.clientClusterName == targetCluster {
700 > shouldProcessTask = true
701 > break FilterLoop
702 }
703 }
704 }
705
706 > return shouldProcessTask stream_sender.go
707 }
708
719 }
720
721 > func (s *StreamSenderImpl) getTaskTargetCluster(task tasks.Task) []string { stream_sender.go
722 > switch t := task.(type) {
723 case *tasks.SyncWorkflowStateTask:
724 return t.TargetClusters
go.temporal.io/server/service/history/replication/raw_task_converter_mock.go 14 introduced LOC · 3 ranges

Open complete file

39
40 // EXPECT returns an object that allows the caller to indicate expected use.
41 > func (m *MockSourceTaskConverter) EXPECT() *MockSourceTaskConverterMockRecorder { raw_task_converter_mock.go
42 > return m.recorder
43 > }
44
45 // Convert mocks base method.
46 > func (m *MockSourceTaskConverter) Convert(task tasks.Task, targetClusterID int32, priority enums.TaskPriority) (*repication.ReplicationTask, error) { raw_task_converter_mock.go
47 > m.ctrl.T.Helper()
48 > ret := m.ctrl.Call(m, "Convert", task, targetClusterID, priority)
49 > ret0, _ := ret[0].(*repication.ReplicationTask)
50 > ret1, _ := ret[1].(error)
51 > return ret0, ret1
52 > }
53
54 // Convert indicates an expected call of Convert.
55 > func (mr *MockSourceTaskConverterMockRecorder) Convert(task, targetClusterID, priority any) *gomock.Call { raw_task_converter_mock.go
56 > mr.mock.ctrl.T.Helper()
57 > return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Convert", reflect.TypeOf((*MockSourceTaskConverter)(nil).Convert), task, targetClusterID, priority)
58 > }
go.temporal.io/server/common/metrics/tags.go 3 introduced LOC · 1 range

Open complete file

487
488 // ReplicationTaskPriorityTag returns a replication task priority tag.
489 > func ReplicationTaskPriorityTag(value enumsspb.TaskPriority) Tag { tags.go
490 > return Tag{Key: replicationTaskPriority, Value: value.String()}
491 > }
492
493 // DestinationTag is a tag for metrics emitted by outbound task executors for the task's destination.
go.temporal.io/server/service/history/replication/metrics.go 2 introduced LOC · 1 range

Open complete file

41 func TaskOperationTagFromTask(
42 taskType enumsspb.TaskType,
43 > ) string { metrics.go
44 > switch taskType {
45 case enumsspb.TASK_TYPE_REPLICATION_SYNC_HSM:
46 return metrics.SyncHSMTaskScope