github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/context/context.go (about) 1 // Copyright 2020 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 context 15 16 import ( 17 "context" 18 "log" 19 "time" 20 21 "github.com/pingcap/ticdc/pkg/version" 22 23 "github.com/pingcap/ticdc/cdc/kv" 24 "github.com/pingcap/ticdc/cdc/model" 25 "github.com/pingcap/ticdc/pkg/config" 26 tidbkv "github.com/pingcap/tidb/kv" 27 "github.com/pingcap/tidb/store/tikv/oracle" 28 pd "github.com/tikv/pd/client" 29 "go.uber.org/zap" 30 ) 31 32 // GlobalVars contains some vars which can be used anywhere in a pipeline 33 // the lifecycle of vars in the GlobalVars shoule be aligned with the ticdc server process. 34 // All field in Vars should be READ-ONLY and THREAD-SAFE 35 type GlobalVars struct { 36 PDClient pd.Client 37 KVStorage tidbkv.Storage 38 CaptureInfo *model.CaptureInfo 39 EtcdClient *kv.CDCEtcdClient 40 GrpcPool kv.GrpcPool 41 } 42 43 // ChangefeedVars contains some vars which can be used anywhere in a pipeline 44 // the lifecycle of vars in the ChangefeedVars shoule be aligned with the changefeed. 45 // All field in Vars should be READ-ONLY and THREAD-SAFE 46 type ChangefeedVars struct { 47 ID model.ChangeFeedID 48 Info *model.ChangeFeedInfo 49 } 50 51 // Context contains Vars(), Done(), Throw(error) and StdContext() context.Context 52 // Context is used to instead of standard context 53 type Context interface { 54 context.Context 55 56 // GlobalVars return the `GlobalVars` store by the root context created by `NewContext` 57 // Note that the `GlobalVars` should be READ-ONLY and THREAD-SAFE 58 // The root node and all its children node share one pointer of `GlobalVars` 59 // So any modification of `GlobalVars` will cause all other family nodes to change. 60 GlobalVars() *GlobalVars 61 62 // ChangefeedVars return the `ChangefeedVars` store by the context created by `WithChangefeedVars` 63 // Note that the `ChangefeedVars` should be READ-ONLY and THREAD-SAFE 64 // The root node and all its children node share one pointer of `ChangefeedVars` 65 // So any modification of `ChangefeedVars` will cause all other family nodes to change. 66 // ChangefeedVars could be return nil when the `ChangefeedVars` is not set by `WithChangefeedVars` 67 ChangefeedVars() *ChangefeedVars 68 69 // Throw an error to parents nodes 70 // we can using `WatchThrow` to listen the errors thrown by children nodes 71 Throw(error) 72 } 73 74 type rootContext struct { 75 Context 76 globalVars *GlobalVars 77 } 78 79 // NewContext returns a new pipeline context 80 func NewContext(stdCtx context.Context, globalVars *GlobalVars) Context { 81 ctx := &rootContext{ 82 globalVars: globalVars, 83 } 84 return WithStd(ctx, stdCtx) 85 } 86 87 func (ctx *rootContext) GlobalVars() *GlobalVars { 88 return ctx.globalVars 89 } 90 91 func (ctx *rootContext) ChangefeedVars() *ChangefeedVars { 92 return nil 93 } 94 95 func (ctx *rootContext) Throw(err error) { 96 if err == nil { 97 return 98 } 99 // make sure all error has been catched 100 log.Panic("an error has escaped, please report a bug", zap.Error(err)) 101 } 102 103 // WithChangefeedVars return a Context with the `ChangefeedVars` 104 func WithChangefeedVars(ctx Context, changefeedVars *ChangefeedVars) Context { 105 return &changefeedVarsContext{ 106 Context: ctx, 107 changefeedVars: changefeedVars, 108 } 109 } 110 111 type changefeedVarsContext struct { 112 Context 113 changefeedVars *ChangefeedVars 114 } 115 116 func (ctx *changefeedVarsContext) ChangefeedVars() *ChangefeedVars { 117 return ctx.changefeedVars 118 } 119 120 type stdContext struct { 121 stdCtx context.Context 122 Context 123 } 124 125 func (ctx *stdContext) Deadline() (deadline time.Time, ok bool) { 126 return ctx.stdCtx.Deadline() 127 } 128 129 func (ctx *stdContext) Err() error { 130 return ctx.stdCtx.Err() 131 } 132 133 func (ctx *stdContext) Value(key interface{}) interface{} { 134 return ctx.stdCtx.Value(key) 135 } 136 137 func (ctx *stdContext) Done() <-chan struct{} { 138 return ctx.stdCtx.Done() 139 } 140 141 // WithStd returns a Context with the standard Context 142 func WithStd(ctx Context, stdCtx context.Context) Context { //revive:disable:context-as-argument 143 return &stdContext{ 144 stdCtx: stdCtx, 145 Context: ctx, 146 } 147 } 148 149 // WithCancel returns a Context with the cancel function 150 func WithCancel(ctx Context) (Context, context.CancelFunc) { 151 stdCtx, cancel := context.WithCancel(ctx) 152 return WithStd(ctx, stdCtx), cancel 153 } 154 155 type throwContext struct { 156 Context 157 f func(error) error 158 } 159 160 // WithErrorHandler creates a new context that can watch the Throw function 161 // if the function `f` specified in WithErrorHandler returns an error, 162 // the error will be thrown to the parent context. 163 func WithErrorHandler(ctx Context, f func(error) error) Context { 164 return &throwContext{ 165 Context: ctx, 166 f: f, 167 } 168 } 169 170 func (ctx *throwContext) Throw(err error) { 171 if err == nil { 172 return 173 } 174 if err := ctx.f(err); err != nil { 175 ctx.Context.Throw(err) 176 } 177 } 178 179 // NewBackendContext4Test returns a new pipeline context for test 180 func NewBackendContext4Test(withChangefeedVars bool) Context { 181 ctx := NewContext(context.Background(), &GlobalVars{ 182 CaptureInfo: &model.CaptureInfo{ 183 ID: "capture-id-test", 184 AdvertiseAddr: "127.0.0.1:0000", 185 Version: version.ReleaseVersion, 186 }, 187 }) 188 if withChangefeedVars { 189 ctx = WithChangefeedVars(ctx, &ChangefeedVars{ 190 ID: "changefeed-id-test", 191 Info: &model.ChangeFeedInfo{ 192 StartTs: oracle.GoTimeToTS(time.Now()), 193 Config: config.GetDefaultReplicaConfig(), 194 }, 195 }) 196 } 197 return ctx 198 } 199 200 // ZapFieldCapture returns a zap field containing capture address 201 func ZapFieldCapture(ctx Context) zap.Field { 202 return zap.String("capture", ctx.GlobalVars().CaptureInfo.AdvertiseAddr) 203 } 204 205 // ZapFieldChangefeed returns a zap field containing changefeed id 206 func ZapFieldChangefeed(ctx Context) zap.Field { 207 return zap.String("changefeed", ctx.ChangefeedVars().ID) 208 }