github.com/polarismesh/polaris@v1.17.8/store/mysql/client.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package sqldb
    19  
    20  import (
    21  	"database/sql"
    22  	"errors"
    23  	"fmt"
    24  	"strings"
    25  	"time"
    26  
    27  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    28  
    29  	"github.com/polarismesh/polaris/common/model"
    30  	"github.com/polarismesh/polaris/store"
    31  )
    32  
    33  type clientStore struct {
    34  	master *BaseDB
    35  	slave  *BaseDB // 缓存相关的读取,请求到slave
    36  }
    37  
    38  // CreateClient insert the client info
    39  func (cs *clientStore) CreateClient(client *model.Client) error {
    40  	clientID := client.Proto().GetId().GetValue()
    41  	if len(clientID) == 0 {
    42  		log.Errorf("[Store][database] add business missing id")
    43  		return fmt.Errorf("add Business missing some params, id %s, name %s", clientID,
    44  			client.Proto().GetHost().GetValue())
    45  	}
    46  	err := RetryTransaction("createClient", func() error {
    47  		return cs.createClient(client)
    48  	})
    49  	return store.Error(err)
    50  }
    51  
    52  // UpdateClient update the client info
    53  func (cs *clientStore) UpdateClient(client *model.Client) error {
    54  	err := RetryTransaction("updateClient", func() error {
    55  		return cs.updateClient(client)
    56  	})
    57  	if err == nil {
    58  		return nil
    59  	}
    60  
    61  	serr := store.Error(err)
    62  	if store.Code(serr) == store.DuplicateEntryErr {
    63  		serr = store.NewStatusError(store.DataConflictErr, err.Error())
    64  	}
    65  	return serr
    66  }
    67  
    68  // deleteClient delete the client info
    69  func deleteClient(tx *BaseTx, clientID string) error {
    70  	if clientID == "" {
    71  		return errors.New("delete client missing client id")
    72  	}
    73  
    74  	str := "update client set flag = 1, mtime = sysdate() where `id` = ?"
    75  	_, err := tx.Exec(str, clientID)
    76  	return store.Error(err)
    77  }
    78  
    79  // BatchAddClients 增加多个实例
    80  func (cs *clientStore) BatchAddClients(clients []*model.Client) error {
    81  	err := RetryTransaction("batchAddClients", func() error {
    82  		return cs.batchAddClients(clients)
    83  	})
    84  	if err == nil {
    85  		return nil
    86  	}
    87  	return store.Error(err)
    88  }
    89  
    90  // BatchDeleteClients 批量删除实例,flag=1
    91  func (cs *clientStore) BatchDeleteClients(ids []string) error {
    92  	err := RetryTransaction("batchDeleteClients", func() error {
    93  		return cs.batchDeleteClients(ids)
    94  	})
    95  	if err == nil {
    96  		return nil
    97  	}
    98  	return store.Error(err)
    99  }
   100  
   101  // GetMoreClients 根据mtime获取增量clients,返回所有store的变更信息
   102  func (cs *clientStore) GetMoreClients(mtime time.Time, firstUpdate bool) (map[string]*model.Client, error) {
   103  	str := `select client.id, client.host, client.type, IFNULL(client.version,""), IFNULL(client.region, ""),
   104  		 IFNULL(client.zone, ""), IFNULL(client.campus, ""), client.flag,  IFNULL(client_stat.target, ""), 
   105  		 IFNULL(client_stat.port, 0), IFNULL(client_stat.protocol, ""), IFNULL(client_stat.path, ""), 
   106  		 UNIX_TIMESTAMP(client.ctime), UNIX_TIMESTAMP(client.mtime)
   107  		 from client left join client_stat on client.id = client_stat.client_id `
   108  	str += " where client.mtime >= FROM_UNIXTIME(?)"
   109  	if firstUpdate {
   110  		str += " and flag != 1"
   111  	}
   112  	rows, err := cs.slave.Query(str, timeToTimestamp(mtime))
   113  	if err != nil {
   114  		log.Errorf("[Store][database] get more client query err: %s", err.Error())
   115  		return nil, err
   116  	}
   117  
   118  	out := make(map[string]*model.Client)
   119  	err = callFetchClientRows(rows, func(entry *model.ClientStore) (b bool, e error) {
   120  		outClient, ok := out[entry.ID]
   121  		if !ok {
   122  			out[entry.ID] = model.Store2Client(entry)
   123  		} else {
   124  			statInfo := model.Store2ClientStat(&entry.Stat)
   125  			outClient.Proto().Stat = append(outClient.Proto().Stat, statInfo)
   126  		}
   127  		return true, nil
   128  	})
   129  	if err != nil {
   130  		log.Errorf("[Store][database] call fetch client rows err: %s", err.Error())
   131  		return nil, err
   132  	}
   133  
   134  	return out, nil
   135  }
   136  
   137  func (cs *clientStore) batchAddClients(clients []*model.Client) error {
   138  	tx, err := cs.master.Begin()
   139  	if err != nil {
   140  		log.Errorf("[Store][database] batch add clients tx begin err: %s", err.Error())
   141  		return err
   142  	}
   143  	defer func() { _ = tx.Rollback() }()
   144  
   145  	ids := make([]string, 0, len(clients))
   146  	var client2StatInfos = make(map[string][]*apiservice.StatInfo)
   147  	builder := strings.Builder{}
   148  	for idx, entry := range clients {
   149  		if idx > 0 {
   150  			builder.WriteString(",")
   151  		}
   152  		builder.WriteString("?")
   153  		ids = append(ids, entry.Proto().GetId().GetValue())
   154  		var statInfos []*apiservice.StatInfo
   155  		if len(entry.Proto().GetStat()) > 0 {
   156  			statInfos = append(statInfos, entry.Proto().GetStat()...)
   157  			client2StatInfos[entry.Proto().GetId().GetValue()] = statInfos
   158  		}
   159  	}
   160  	if err = batchCleanClientStats(tx, ids); nil != err {
   161  		log.Errorf("[Store][database] batch clean client stat err: %s", err.Error())
   162  		return err
   163  	}
   164  	if err = batchAddClientMain(tx, clients); nil != err {
   165  		log.Errorf("[Store][database] batch add clients err: %s", err.Error())
   166  		return err
   167  	}
   168  	if err = batchAddClientStat(tx, client2StatInfos); nil != err {
   169  		log.Errorf("[Store][database] batch add clientStats err: %s", err.Error())
   170  		return err
   171  	}
   172  	if err := tx.Commit(); err != nil {
   173  		log.Errorf("[Store][database] batch add clients commit tx err: %s", err.Error())
   174  		return err
   175  	}
   176  	return nil
   177  }
   178  
   179  func (cs *clientStore) batchDeleteClients(ids []string) error {
   180  	tx, err := cs.master.Begin()
   181  	if err != nil {
   182  		log.Errorf("[Store][database] batch delete clients tx begin err: %s", err.Error())
   183  		return err
   184  	}
   185  	defer func() { _ = tx.Rollback() }()
   186  
   187  	if err = batchCleanClientStats(tx, ids); nil != err {
   188  		log.Errorf("[Store][database] batch clean client stat err: %s", err.Error())
   189  		return err
   190  	}
   191  	if err = batchDeleteClientsMain(tx, ids); nil != err {
   192  		log.Errorf("[Store][database] batch delete clients err: %s", err.Error())
   193  		return err
   194  	}
   195  	if err := tx.Commit(); err != nil {
   196  		log.Errorf("[Store][database] batch delete clients commit tx err: %s", err.Error())
   197  		return err
   198  	}
   199  	return nil
   200  }
   201  
   202  func batchDeleteClientsMain(tx *BaseTx, ids []string) error {
   203  	args := make([]interface{}, 0, len(ids))
   204  	for i := range ids {
   205  		args = append(args, ids[i])
   206  	}
   207  
   208  	return BatchOperation("batch-delete-clients", args, func(objects []interface{}) error {
   209  		if len(objects) == 0 {
   210  			return nil
   211  		}
   212  		str := `update client set flag = 1, mtime = sysdate() where id in ( ` + PlaceholdersN(len(objects)) + `)`
   213  		_, err := tx.Exec(str, objects...)
   214  		return store.Error(err)
   215  	})
   216  }
   217  
   218  func batchCleanClientStats(tx *BaseTx, ids []string) error {
   219  	args := make([]interface{}, 0, len(ids))
   220  	for i := range ids {
   221  		args = append(args, ids[i])
   222  	}
   223  
   224  	return BatchOperation("batch-delete-client-stats", args, func(objects []interface{}) error {
   225  		if len(objects) == 0 {
   226  			return nil
   227  		}
   228  		str := `delete from client_stat where client_id in (` + PlaceholdersN(len(objects)) + `)`
   229  		_, err := tx.Exec(str, objects...)
   230  		return store.Error(err)
   231  	})
   232  }
   233  
   234  func (cs *clientStore) GetClientStat(clientID string) ([]*model.ClientStatStore, error) {
   235  	str := "select `target`, `port`, `protocol`, `path` from client_stat where client.id = ?"
   236  	rows, err := cs.master.Query(str, clientID)
   237  	if err != nil {
   238  		log.Errorf("[Store][database] query client stat err: %s", err.Error())
   239  		return nil, err
   240  	}
   241  	defer rows.Close()
   242  
   243  	var clientStatStores []*model.ClientStatStore
   244  	for rows.Next() {
   245  		clientStatStore := &model.ClientStatStore{}
   246  		if err := rows.Scan(&clientStatStore.Target,
   247  			&clientStatStore.Port, &clientStatStore.Protocol, &clientStatStore.Path); err != nil {
   248  			log.Errorf("[Store][database] get client meta rows scan err: %s", err.Error())
   249  			return nil, err
   250  		}
   251  		clientStatStores = append(clientStatStores, clientStatStore)
   252  	}
   253  	if err := rows.Err(); err != nil {
   254  		log.Errorf("[Store][database] get client meta rows next err: %s", err.Error())
   255  		return nil, err
   256  	}
   257  
   258  	return clientStatStores, nil
   259  }
   260  
   261  // callFetchClientRows 带回调的fetch client
   262  func callFetchClientRows(rows *sql.Rows, callback func(entry *model.ClientStore) (bool, error)) error {
   263  	if rows == nil {
   264  		return nil
   265  	}
   266  	defer rows.Close()
   267  	var item model.ClientStore
   268  	progress := 0
   269  	for rows.Next() {
   270  		progress++
   271  		if progress%100000 == 0 {
   272  			log.Infof("[Store][database] client fetch rows progress: %d", progress)
   273  		}
   274  		err := rows.Scan(&item.ID, &item.Host, &item.Type, &item.Version, &item.Region, &item.Zone,
   275  			&item.Campus, &item.Flag, &item.Stat.Target, &item.Stat.Port, &item.Stat.Protocol,
   276  			&item.Stat.Path, &item.CreateTime, &item.ModifyTime)
   277  		if err != nil {
   278  			log.Errorf("[Store][database] fetch client rows err: %s", err.Error())
   279  			return err
   280  		}
   281  		ok, err := callback(&item)
   282  		if err != nil {
   283  			return err
   284  		}
   285  		if !ok {
   286  			return nil
   287  		}
   288  	}
   289  	if err := rows.Err(); err != nil {
   290  		log.Errorf("[Store][database] client rows catch err: %s", err.Error())
   291  		return err
   292  	}
   293  
   294  	return nil
   295  }
   296  
   297  func (cs *clientStore) createClient(client *model.Client) error {
   298  	tx, err := cs.master.Begin()
   299  	if err != nil {
   300  		log.Errorf("[Store][database] create client tx begin err: %s", err.Error())
   301  		return err
   302  	}
   303  	defer func() { _ = tx.Rollback() }()
   304  	// clean the old items before add
   305  	if err := deleteClient(tx, client.Proto().GetId().GetValue()); err != nil {
   306  		return err
   307  	}
   308  	if err := addClientMain(tx, client); err != nil {
   309  		log.Errorf("[Store][database] add client main err: %s", err.Error())
   310  		return err
   311  	}
   312  	if err := addClientStat(tx, client); err != nil {
   313  		log.Errorf("[Store][database] add client stat err: %s", err.Error())
   314  		return err
   315  	}
   316  	if err := tx.Commit(); err != nil {
   317  		log.Errorf("[Store][database] create client commit tx err: %s", err.Error())
   318  		return err
   319  	}
   320  	return nil
   321  }
   322  
   323  func (cs *clientStore) updateClient(client *model.Client) error {
   324  	tx, err := cs.master.Begin()
   325  	if err != nil {
   326  		log.Errorf("[Store][database] update client tx begin err: %s", err.Error())
   327  		return err
   328  	}
   329  	defer func() { _ = tx.Rollback() }()
   330  
   331  	if err := updateClientMain(tx, client); err != nil {
   332  		log.Errorf("[Store][database] update client main err: %s", err.Error())
   333  		return err
   334  	}
   335  
   336  	if err := updateClientStat(tx, client); err != nil {
   337  		log.Errorf("[Store][database] update client stat err: %s", err.Error())
   338  		return err
   339  	}
   340  	if err := tx.Commit(); err != nil {
   341  		log.Errorf("[Store][database] update client commit tx err: %s", err.Error())
   342  		return err
   343  	}
   344  	return nil
   345  }
   346  
   347  func addClientMain(tx *BaseTx, client *model.Client) error {
   348  	str := `insert into client(id, host, type, version, region, zone, campus, flag, ctime, mtime)
   349  			 values(?, ?, ?, ?, ?, ?, ?, 0, sysdate(), sysdate())`
   350  	_, err := tx.Exec(str,
   351  		client.Proto().GetId().GetValue(),
   352  		client.Proto().GetHost().GetValue(),
   353  		client.Proto().GetType().String(),
   354  		client.Proto().GetVersion().GetValue(),
   355  		client.Proto().GetLocation().GetRegion().GetValue(),
   356  		client.Proto().GetLocation().GetZone().GetValue(),
   357  		client.Proto().GetLocation().GetCampus().GetValue(),
   358  	)
   359  	return err
   360  }
   361  
   362  func batchAddClientMain(tx *BaseTx, clients []*model.Client) error {
   363  	str := `replace into client(id, host, type, version, region, zone, campus, flag, ctime, mtime)
   364  		 values`
   365  	first := true
   366  	args := make([]interface{}, 0)
   367  	for _, client := range clients {
   368  		if !first {
   369  			str += ","
   370  		}
   371  		str += "(?, ?, ?, ?, ?, ?, ?, 0, sysdate(), sysdate())"
   372  		first = false
   373  
   374  		args = append(args, client.Proto().GetId().GetValue(),
   375  			client.Proto().GetHost().GetValue(),
   376  			client.Proto().GetType().String())
   377  		args = append(args, client.Proto().GetVersion().GetValue(),
   378  			client.Proto().GetLocation().GetRegion().GetValue(),
   379  			client.Proto().GetLocation().GetZone().GetValue(),
   380  			client.Proto().GetLocation().GetCampus().GetValue())
   381  	}
   382  	_, err := tx.Exec(str, args...)
   383  	return err
   384  }
   385  
   386  func batchAddClientStat(tx *BaseTx, client2Stats map[string][]*apiservice.StatInfo) error {
   387  	if len(client2Stats) == 0 {
   388  		return nil
   389  	}
   390  	str := `insert into client_stat(client_id, target, port, protocol, path)
   391  			 values`
   392  	first := true
   393  	args := make([]interface{}, 0)
   394  	for clientId, stats := range client2Stats {
   395  		for _, entry := range stats {
   396  			if !first {
   397  				str += ","
   398  			}
   399  			str += "(?, ?, ?, ?, ?)"
   400  			first = false
   401  			args = append(args,
   402  				clientId,
   403  				entry.GetTarget().GetValue(),
   404  				entry.GetPort().GetValue(),
   405  				entry.GetProtocol().GetValue(),
   406  				entry.GetPath().GetValue())
   407  		}
   408  	}
   409  	_, err := tx.Exec(str, args...)
   410  	return err
   411  }
   412  
   413  func addClientStat(tx *BaseTx, client *model.Client) error {
   414  	stats := client.Proto().GetStat()
   415  	if len(stats) == 0 {
   416  		return nil
   417  	}
   418  	str := `insert into client_stat(client_id, target, port, protocol, path)
   419  			 values`
   420  	first := true
   421  	args := make([]interface{}, 0)
   422  	for _, entry := range stats {
   423  		if !first {
   424  			str += ","
   425  		}
   426  		str += "(?, ?, ?, ?, ?)"
   427  		first = false
   428  		args = append(args,
   429  			client.Proto().GetId().GetValue(),
   430  			entry.GetTarget().GetValue(),
   431  			entry.GetPort().GetValue(),
   432  			entry.GetProtocol().GetValue(),
   433  			entry.GetPath().GetValue())
   434  	}
   435  	_, err := tx.Exec(str, args...)
   436  	return err
   437  }
   438  
   439  func updateClientMain(tx *BaseTx, client *model.Client) error {
   440  	str := `update client set host = ?,
   441  	 type = ?, version = ?, region = ?, zone = ?, campus = ?, mtime = sysdate() where id = ?`
   442  
   443  	_, err := tx.Exec(str,
   444  		client.Proto().GetHost().GetValue(),
   445  		client.Proto().GetType().String(),
   446  		client.Proto().GetVersion().GetValue(),
   447  		client.Proto().GetLocation().GetRegion().GetValue(),
   448  		client.Proto().GetLocation().GetZone().GetValue(),
   449  		client.Proto().GetLocation().GetCampus().GetValue(),
   450  		client.Proto().GetId().GetValue(),
   451  	)
   452  
   453  	return err
   454  }
   455  
   456  // updateClientStat 更新client的stat表
   457  func updateClientStat(tx *BaseTx, client *model.Client) error {
   458  	deleteStr := "delete from client_stat where cliend_id = ?"
   459  	if _, err := tx.Exec(deleteStr, client.Proto().GetId().GetValue()); err != nil {
   460  		return err
   461  	}
   462  	return addClientStat(tx, client)
   463  }