github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/processor/manager.go (about) 1 // Copyright 2021 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 processor 15 16 import ( 17 "context" 18 "fmt" 19 "io" 20 "time" 21 22 "github.com/pingcap/errors" 23 "github.com/pingcap/failpoint" 24 "github.com/pingcap/log" 25 "github.com/pingcap/ticdc/cdc/model" 26 tablepipeline "github.com/pingcap/ticdc/cdc/processor/pipeline" 27 cdcContext "github.com/pingcap/ticdc/pkg/context" 28 cerrors "github.com/pingcap/ticdc/pkg/errors" 29 "github.com/pingcap/ticdc/pkg/orchestrator" 30 "go.uber.org/zap" 31 ) 32 33 type commandTp int 34 35 const ( 36 commandTpUnknow commandTp = iota //nolint:varcheck,deadcode 37 commandTpClose 38 commandTpWriteDebugInfo 39 ) 40 41 type command struct { 42 tp commandTp 43 payload interface{} 44 done chan struct{} 45 } 46 47 // Manager is a manager of processor, which maintains the state and behavior of processors 48 type Manager struct { 49 processors map[model.ChangeFeedID]*processor 50 51 commandQueue chan *command 52 53 newProcessor func(cdcContext.Context) *processor 54 } 55 56 // NewManager creates a new processor manager 57 func NewManager() *Manager { 58 return &Manager{ 59 processors: make(map[model.ChangeFeedID]*processor), 60 commandQueue: make(chan *command, 4), 61 newProcessor: newProcessor, 62 } 63 } 64 65 // NewManager4Test creates a new processor manager for test 66 func NewManager4Test( 67 createTablePipeline func(ctx cdcContext.Context, tableID model.TableID, replicaInfo *model.TableReplicaInfo) (tablepipeline.TablePipeline, error), 68 ) *Manager { 69 m := NewManager() 70 m.newProcessor = func(ctx cdcContext.Context) *processor { 71 return newProcessor4Test(ctx, createTablePipeline) 72 } 73 return m 74 } 75 76 // Tick implements the `orchestrator.State` interface 77 // the `state` parameter is sent by the etcd worker, the `state` must be a snapshot of KVs in etcd 78 // the Tick function of Manager create or remove processor instances according to the specified `state`, or pass the `state` to processor instances 79 func (m *Manager) Tick(stdCtx context.Context, state orchestrator.ReactorState) (nextState orchestrator.ReactorState, err error) { 80 ctx := stdCtx.(cdcContext.Context) 81 globalState := state.(*model.GlobalReactorState) 82 if err := m.handleCommand(); err != nil { 83 return state, err 84 } 85 captureID := ctx.GlobalVars().CaptureInfo.ID 86 var inactiveChangefeedCount int 87 for changefeedID, changefeedState := range globalState.Changefeeds { 88 if !changefeedState.Active(captureID) { 89 inactiveChangefeedCount++ 90 m.closeProcessor(changefeedID) 91 continue 92 } 93 ctx := cdcContext.WithChangefeedVars(ctx, &cdcContext.ChangefeedVars{ 94 ID: changefeedID, 95 Info: changefeedState.Info, 96 }) 97 processor, exist := m.processors[changefeedID] 98 if !exist { 99 if changefeedState.Status.AdminJobType.IsStopState() || changefeedState.TaskStatuses[captureID].AdminJobType.IsStopState() { 100 continue 101 } 102 // the processor should start after at least one table has been added to this capture 103 taskStatus := changefeedState.TaskStatuses[captureID] 104 if taskStatus == nil || (len(taskStatus.Tables) == 0 && len(taskStatus.Operation) == 0) { 105 continue 106 } 107 failpoint.Inject("processorManagerHandleNewChangefeedDelay", nil) 108 processor = m.newProcessor(ctx) 109 m.processors[changefeedID] = processor 110 } 111 if _, err := processor.Tick(ctx, changefeedState); err != nil { 112 m.closeProcessor(changefeedID) 113 if cerrors.ErrReactorFinished.Equal(errors.Cause(err)) { 114 continue 115 } 116 return state, errors.Trace(err) 117 } 118 } 119 // check if the processors in memory is leaked 120 if len(globalState.Changefeeds)-inactiveChangefeedCount != len(m.processors) { 121 for changefeedID := range m.processors { 122 if _, exist := globalState.Changefeeds[changefeedID]; !exist { 123 m.closeProcessor(changefeedID) 124 } 125 } 126 } 127 return state, nil 128 } 129 130 func (m *Manager) closeProcessor(changefeedID model.ChangeFeedID) { 131 if processor, exist := m.processors[changefeedID]; exist { 132 err := processor.Close() 133 if err != nil { 134 log.Warn("failed to close processor", zap.Error(err)) 135 } 136 delete(m.processors, changefeedID) 137 } 138 } 139 140 // AsyncClose sends a close signal to Manager and closing all processors 141 func (m *Manager) AsyncClose() { 142 m.sendCommand(commandTpClose, nil) 143 } 144 145 // WriteDebugInfo write the debug info to Writer 146 func (m *Manager) WriteDebugInfo(w io.Writer) { 147 timeout := time.Second * 3 148 done := m.sendCommand(commandTpWriteDebugInfo, w) 149 // wait the debug info printed 150 select { 151 case <-done: 152 case <-time.After(timeout): 153 fmt.Fprintf(w, "failed to print debug info for processor\n") 154 } 155 } 156 157 func (m *Manager) sendCommand(tp commandTp, payload interface{}) chan struct{} { 158 timeout := time.Second * 3 159 cmd := &command{tp: tp, payload: payload, done: make(chan struct{})} 160 select { 161 case m.commandQueue <- cmd: 162 case <-time.After(timeout): 163 close(cmd.done) 164 log.Warn("the command queue is full, ignore this command", zap.Any("command", cmd)) 165 } 166 return cmd.done 167 } 168 169 func (m *Manager) handleCommand() error { 170 var cmd *command 171 select { 172 case cmd = <-m.commandQueue: 173 default: 174 return nil 175 } 176 defer close(cmd.done) 177 switch cmd.tp { 178 case commandTpClose: 179 for changefeedID := range m.processors { 180 m.closeProcessor(changefeedID) 181 } 182 return cerrors.ErrReactorFinished 183 case commandTpWriteDebugInfo: 184 w := cmd.payload.(io.Writer) 185 m.writeDebugInfo(w) 186 default: 187 log.Warn("Unknown command in processor manager", zap.Any("command", cmd)) 188 } 189 return nil 190 } 191 192 func (m *Manager) writeDebugInfo(w io.Writer) { 193 for changefeedID, processor := range m.processors { 194 fmt.Fprintf(w, "changefeedID: %s\n", changefeedID) 195 processor.WriteDebugInfo(w) 196 fmt.Fprintf(w, "\n") 197 } 198 }