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(&params)
    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(&params)
    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  }