github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/executor/server/metastore.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 server 15 16 import ( 17 "context" 18 "encoding/json" 19 20 "github.com/pingcap/log" 21 pb "github.com/pingcap/tiflow/engine/enginepb" 22 "github.com/pingcap/tiflow/engine/pkg/client" 23 "github.com/pingcap/tiflow/engine/pkg/meta" 24 metaModel "github.com/pingcap/tiflow/engine/pkg/meta/model" 25 "github.com/pingcap/tiflow/pkg/errors" 26 "go.uber.org/atomic" 27 "go.uber.org/zap" 28 ) 29 30 // MetastoreManager maintains all metastore clients we need. 31 // Except for FrameworkStore and BusinessClientConn, 32 // a MetastoreManager is not thread-safe. 33 // 34 // TODO refactor some code repetition together with servermaster.MetaStoreManager, 35 // and add integration tests between MetastoreManager in this file and servermaster.MetaStoreManager. 36 type MetastoreManager interface { 37 // Init fetches metastore configurations from Servermaster and 38 // creates the necessary client. 39 // Init is made part of the interface because the interface is intended 40 // to reflect the dependency between the objects during server initialization. 41 // NOTE: Init must be called before other methods can be. 42 Init(ctx context.Context, discoveryClient client.DiscoveryClient) error 43 IsInitialized() bool 44 Close() 45 46 FrameworkClientConn() metaModel.ClientConn 47 BusinessClientConn() metaModel.ClientConn 48 } 49 50 // NewMetastoreManager returns a new MetastoreManager. 51 // Note that Init() should be called first before using it. 52 func NewMetastoreManager() MetastoreManager { 53 return &metastoreManagerImpl{ 54 creator: metastoreCreatorImpl{}, 55 } 56 } 57 58 // metastoreManagerImpl implements MetastoreManager. 59 // We make the implementation private because it 60 // is the only one implementation used in production code. 61 type metastoreManagerImpl struct { 62 initialized atomic.Bool 63 64 frameworkClientConn metaModel.ClientConn 65 businessClientConn metaModel.ClientConn 66 67 creator MetastoreCreator 68 } 69 70 // MetastoreCreator abstracts creation behavior of the various 71 // metastore clients. 72 type MetastoreCreator interface { 73 CreateClientConnForBusiness( 74 ctx context.Context, params metaModel.StoreConfig, 75 ) (metaModel.ClientConn, error) 76 77 CreateClientConnForFramework( 78 ctx context.Context, params metaModel.StoreConfig, 79 ) (metaModel.ClientConn, error) 80 } 81 82 type metastoreCreatorImpl struct{} 83 84 func (c metastoreCreatorImpl) CreateClientConnForBusiness( 85 _ context.Context, params metaModel.StoreConfig, 86 ) (metaModel.ClientConn, error) { 87 cc, err := meta.NewClientConn(¶ms) 88 if err != nil { 89 return nil, errors.Trace(err) 90 } 91 return cc, nil 92 } 93 94 func (c metastoreCreatorImpl) CreateClientConnForFramework( 95 _ context.Context, params metaModel.StoreConfig, 96 ) (metaModel.ClientConn, error) { 97 cc, err := meta.NewClientConn(¶ms) 98 if err != nil { 99 return nil, errors.Trace(err) 100 } 101 return cc, err 102 } 103 104 func (m *metastoreManagerImpl) Init(ctx context.Context, discoveryClient client.DiscoveryClient) (retErr error) { 105 if m.initialized.Load() { 106 log.Panic("MetastoreManager: double Init") 107 } 108 109 defer func() { 110 // Close all store clients in case the final return value 111 // is not nil. 112 if retErr != nil { 113 m.Close() 114 } 115 }() 116 117 if err := m.initFrameworkStore(ctx, discoveryClient); err != nil { 118 return err 119 } 120 121 if err := m.initBusinessStore(ctx, discoveryClient); err != nil { 122 return err 123 } 124 125 m.initialized.Store(true) 126 return nil 127 } 128 129 func (m *metastoreManagerImpl) IsInitialized() bool { 130 return m.initialized.Load() 131 } 132 133 func (m *metastoreManagerImpl) initFrameworkStore(ctx context.Context, discoveryClient client.DiscoveryClient) error { 134 // Query framework metastore endpoints. 135 resp, err := discoveryClient.QueryMetaStore( 136 ctx, 137 &pb.QueryMetaStoreRequest{Tp: pb.StoreType_SystemMetaStore}) 138 if err != nil { 139 return errors.Trace(err) 140 } 141 142 conf := parseStoreConfig(resp.Config) 143 cc, err := m.creator.CreateClientConnForFramework(ctx, conf) 144 if err != nil { 145 return err 146 } 147 148 log.Info("Obtained framework metastore endpoint", zap.Any("endpoints", conf.Endpoints), 149 zap.Any("schema", conf.Schema), zap.Any("username", conf.User)) 150 m.frameworkClientConn = cc 151 return nil 152 } 153 154 func (m *metastoreManagerImpl) initBusinessStore(ctx context.Context, discoveryClient client.DiscoveryClient) error { 155 // fetch business metastore connection endpoint 156 resp, err := discoveryClient.QueryMetaStore( 157 ctx, 158 &pb.QueryMetaStoreRequest{Tp: pb.StoreType_AppMetaStore}) 159 if err != nil { 160 return err 161 } 162 163 conf := parseStoreConfig(resp.Config) 164 cc, err := m.creator.CreateClientConnForBusiness(ctx, conf) 165 if err != nil { 166 return err 167 } 168 169 log.Info("Obtained framework metastore endpoint", zap.Any("endpoints", conf.Endpoints), 170 zap.Any("schema", conf.Schema), zap.Any("username", conf.User)) 171 m.businessClientConn = cc 172 return nil 173 } 174 175 func (m *metastoreManagerImpl) FrameworkClientConn() metaModel.ClientConn { 176 if !m.initialized.Load() { 177 log.Panic("FrameworkStore is called before Init is successful") 178 } 179 return m.frameworkClientConn 180 } 181 182 func (m *metastoreManagerImpl) BusinessClientConn() metaModel.ClientConn { 183 if !m.initialized.Load() { 184 log.Panic("BusinessClientConn is called before Init is successful") 185 } 186 return m.businessClientConn 187 } 188 189 func (m *metastoreManagerImpl) Close() { 190 if m.frameworkClientConn != nil { 191 _ = m.frameworkClientConn.Close() 192 m.frameworkClientConn = nil 193 } 194 195 if m.businessClientConn != nil { 196 _ = m.businessClientConn.Close() 197 m.businessClientConn = nil 198 } 199 200 log.Info("MetastoreManager: Closed all metastores") 201 } 202 203 func parseStoreConfig(rawBytes []byte) metaModel.StoreConfig { 204 var conf metaModel.StoreConfig 205 206 // Try unmarshal as json first. 207 err := json.Unmarshal(rawBytes, &conf) 208 if err == nil { 209 return conf 210 } 211 212 log.Info("Could not unmarshal metastore config, fallback to treating it as an endpoint list") 213 214 conf.SetEndpoints(string(rawBytes)) 215 return conf 216 }