github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/framework/internal/master/worker_entry.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package master
    15  
    16  import (
    17  	"fmt"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/pingcap/log"
    22  	frameModel "github.com/pingcap/tiflow/engine/framework/model"
    23  	"github.com/pingcap/tiflow/engine/model"
    24  	"go.uber.org/atomic"
    25  	"go.uber.org/zap"
    26  )
    27  
    28  // workerEntryState is the state of a worker
    29  // internal to WorkerManager. It is NOT part of
    30  // the public API of Dataflow Engine.
    31  type workerEntryState int32
    32  
    33  const (
    34  	workerEntryWait = workerEntryState(iota + 1)
    35  	workerEntryCreated
    36  	workerEntryNormal
    37  	workerEntryOffline
    38  	workerEntryTombstone
    39  )
    40  
    41  // The following is the state-transition diagram.
    42  // Refer to ../doc/worker_entry_fsm.puml for a UML version.
    43  //
    44  // workerEntryCreated            workerEntryWait
    45  //      │  │                            │  │
    46  //      │  │                            │  │
    47  //      │ heartbeat              heartbeat │
    48  //      │  │                            │  │
    49  //      │  │                            │  │
    50  //      │  └────► workerEntryNormal ◄───┘  │
    51  //      │         │                        │
    52  //      │         │                        │
    53  //    timeout   timeout                  timeout
    54  //      │         │                        │
    55  //      ▼         ▼                        ▼
    56  // workerEntryOffline ─────────► workerEntryTombstone
    57  //                    callback
    58  
    59  // workerEntry records the state of a worker managed by
    60  // WorkerManager.
    61  type workerEntry struct {
    62  	id         frameModel.WorkerID
    63  	executorID model.ExecutorID
    64  
    65  	mu       sync.Mutex
    66  	expireAt time.Time
    67  	state    workerEntryState
    68  
    69  	receivedFinish atomic.Bool
    70  
    71  	statusMu sync.RWMutex
    72  	status   *frameModel.WorkerStatus
    73  }
    74  
    75  func newWorkerEntry(
    76  	id frameModel.WorkerID,
    77  	executorID model.ExecutorID,
    78  	expireAt time.Time,
    79  	state workerEntryState,
    80  	initWorkerStatus *frameModel.WorkerStatus,
    81  ) *workerEntry {
    82  	return &workerEntry{
    83  		id:         id,
    84  		executorID: executorID,
    85  		expireAt:   expireAt,
    86  		state:      state,
    87  		status:     initWorkerStatus,
    88  	}
    89  }
    90  
    91  func newWaitingWorkerEntry(
    92  	id frameModel.WorkerID,
    93  	lastStatus *frameModel.WorkerStatus,
    94  ) *workerEntry {
    95  	return newWorkerEntry(id, "", time.Time{}, workerEntryWait, lastStatus)
    96  }
    97  
    98  // String implements fmt.Stringer, note the implementation is not thread safe
    99  func (e *workerEntry) String() string {
   100  	return fmt.Sprintf("{worker-id:%s, executor-id:%s, state:%d}",
   101  		e.id, e.executorID, e.state)
   102  }
   103  
   104  func (e *workerEntry) State() workerEntryState {
   105  	e.mu.Lock()
   106  	defer e.mu.Unlock()
   107  
   108  	return e.state
   109  }
   110  
   111  func (e *workerEntry) MarkAsTombstone() {
   112  	e.mu.Lock()
   113  	defer e.mu.Unlock()
   114  
   115  	if e.state == workerEntryWait || e.state == workerEntryOffline || e.IsFinished() {
   116  		// Only workerEntryWait and workerEntryOffline are allowed
   117  		// to transition to workerEntryTombstone.
   118  		e.state = workerEntryTombstone
   119  		return
   120  	}
   121  
   122  	log.Panic("Unreachable", zap.Stringer("entry", e))
   123  }
   124  
   125  func (e *workerEntry) IsTombstone() bool {
   126  	e.mu.Lock()
   127  	defer e.mu.Unlock()
   128  
   129  	return e.state == workerEntryTombstone
   130  }
   131  
   132  func (e *workerEntry) MarkAsOnline(executor model.ExecutorID, expireAt time.Time) {
   133  	e.mu.Lock()
   134  	defer e.mu.Unlock()
   135  
   136  	if e.state == workerEntryCreated || e.state == workerEntryWait {
   137  		e.state = workerEntryNormal
   138  		e.expireAt = expireAt
   139  		e.executorID = executor
   140  		return
   141  	}
   142  
   143  	log.Panic("Unreachable", zap.Stringer("entry", e))
   144  }
   145  
   146  func (e *workerEntry) MarkAsOffline() {
   147  	e.mu.Lock()
   148  	defer e.mu.Unlock()
   149  
   150  	if e.state == workerEntryCreated || e.state == workerEntryNormal {
   151  		e.state = workerEntryOffline
   152  		return
   153  	}
   154  
   155  	log.Panic("Unreachable", zap.Stringer("entry", e))
   156  }
   157  
   158  func (e *workerEntry) Status() *frameModel.WorkerStatus {
   159  	e.statusMu.RLock()
   160  	defer e.statusMu.RUnlock()
   161  
   162  	return e.status
   163  }
   164  
   165  func (e *workerEntry) UpdateStatus(status *frameModel.WorkerStatus) {
   166  	e.statusMu.Lock()
   167  	defer e.statusMu.Unlock()
   168  
   169  	e.status = status
   170  }
   171  
   172  func (e *workerEntry) SetExpireTime(expireAt time.Time) {
   173  	e.mu.Lock()
   174  	defer e.mu.Unlock()
   175  
   176  	e.expireAt = expireAt
   177  }
   178  
   179  func (e *workerEntry) ExpireTime() time.Time {
   180  	e.mu.Lock()
   181  	defer e.mu.Unlock()
   182  
   183  	return e.expireAt
   184  }
   185  
   186  func (e *workerEntry) SetFinished() {
   187  	e.receivedFinish.Store(true)
   188  }
   189  
   190  func (e *workerEntry) IsFinished() bool {
   191  	return e.receivedFinish.Load()
   192  }