github.com/polarismesh/polaris@v1.17.8/store/mysql/config_file_release.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  	"encoding/json"
    23  	"errors"
    24  	"time"
    25  
    26  	"github.com/polarismesh/polaris/common/model"
    27  	"github.com/polarismesh/polaris/common/utils"
    28  	"github.com/polarismesh/polaris/store"
    29  )
    30  
    31  var _ store.ConfigFileReleaseStore = (*configFileReleaseStore)(nil)
    32  
    33  var (
    34  	ErrTxIsNil = errors.New("tx is nil")
    35  )
    36  
    37  type configFileReleaseStore struct {
    38  	master *BaseDB
    39  	slave  *BaseDB
    40  }
    41  
    42  // CreateConfigFileRelease 新建配置文件发布
    43  func (cfr *configFileReleaseStore) CreateConfigFileReleaseTx(tx store.Tx, data *model.ConfigFileRelease) error {
    44  	if tx == nil {
    45  		return ErrTxIsNil
    46  	}
    47  	dbTx := tx.GetDelegateTx().(*BaseTx)
    48  	args := []interface{}{data.Namespace, data.Group, data.Name}
    49  	_, err := dbTx.Exec("SELECT id FROM config_file WHERE namespace = ? AND `group` = ? AND name = ? FOR UPDATE", args...)
    50  	if err != nil {
    51  		return store.Error(err)
    52  	}
    53  
    54  	clean := "DELETE FROM config_file_release WHERE namespace = ? AND `group` = ? AND file_name = ? AND name = ? AND flag = 1"
    55  	if _, err := dbTx.Exec(clean, data.Namespace, data.Group, data.FileName, data.Name); err != nil {
    56  		return store.Error(err)
    57  	}
    58  
    59  	maxVersion, err := cfr.inactiveConfigFileRelease(dbTx, data)
    60  	if err != nil {
    61  		return store.Error(err)
    62  	}
    63  
    64  	s := "INSERT INTO config_file_release(name, namespace, `group`, file_name, content , comment, md5, " +
    65  		" version, create_time, create_by , modify_time, modify_by, active, tags, description) " +
    66  		" VALUES (?, ?, ?, ?, ? , ?, ?, ?, sysdate(), ? , sysdate(), ?, 1, ?, ?)"
    67  
    68  	args = []interface{}{
    69  		data.Name, data.Namespace, data.Group,
    70  		data.FileName, data.Content, data.Comment, data.Md5, maxVersion + 1,
    71  		data.CreateBy, data.ModifyBy, utils.MustJson(data.Metadata), data.ReleaseDescription,
    72  	}
    73  	if _, err = dbTx.Exec(s, args...); err != nil {
    74  		return store.Error(err)
    75  	}
    76  	return nil
    77  }
    78  
    79  // GetConfigFileRelease 获取配置文件发布,只返回 flag=0 的记录
    80  func (cfr *configFileReleaseStore) GetConfigFileRelease(req *model.ConfigFileReleaseKey) (*model.ConfigFileRelease, error) {
    81  	tx, err := cfr.master.Begin()
    82  	if err != nil {
    83  		return nil, store.Error(err)
    84  	}
    85  	defer func() {
    86  		_ = tx.Rollback()
    87  	}()
    88  	return cfr.GetConfigFileReleaseTx(NewSqlDBTx(tx), req)
    89  }
    90  
    91  // GetConfigFileReleaseTx 在已开启的事务中获取配置文件发布内容,只获取 flag=0 的记录
    92  func (cfr *configFileReleaseStore) GetConfigFileReleaseTx(tx store.Tx,
    93  	req *model.ConfigFileReleaseKey) (*model.ConfigFileRelease, error) {
    94  	if tx == nil {
    95  		return nil, ErrTxIsNil
    96  	}
    97  
    98  	dbTx := tx.GetDelegateTx().(*BaseTx)
    99  	querySql := cfr.baseQuerySql() + "WHERE namespace = ? AND `group` = ? AND " +
   100  		" file_name = ? AND name = ? AND flag = 0 "
   101  	var (
   102  		rows *sql.Rows
   103  		err  error
   104  	)
   105  
   106  	rows, err = dbTx.Query(querySql, req.Namespace, req.Group, req.FileName, req.Name)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	fileRelease, err := cfr.transferRows(rows)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if len(fileRelease) > 0 {
   115  		return fileRelease[0], nil
   116  	}
   117  	return nil, nil
   118  }
   119  
   120  // DeleteConfigFileRelease
   121  func (cfr *configFileReleaseStore) DeleteConfigFileReleaseTx(tx store.Tx, data *model.ConfigFileReleaseKey) error {
   122  	if tx == nil {
   123  		return ErrTxIsNil
   124  	}
   125  
   126  	dbTx := tx.GetDelegateTx().(*BaseTx)
   127  	s := "update config_file_release set flag = 1, modify_time = sysdate() " +
   128  		" where namespace = ? and `group` = ? and file_name = ? and name = ?"
   129  	_, err := dbTx.Exec(s, data.Namespace, data.Group, data.FileName, data.Name)
   130  	if err != nil {
   131  		return store.Error(err)
   132  	}
   133  	return nil
   134  }
   135  
   136  // CleanConfigFileReleasesTx
   137  func (cfr *configFileReleaseStore) CleanConfigFileReleasesTx(tx store.Tx,
   138  	namespace, group, fileName string) error {
   139  	if tx == nil {
   140  		return ErrTxIsNil
   141  	}
   142  
   143  	dbTx := tx.GetDelegateTx().(*BaseTx)
   144  	s := "UPDATE config_file_release SET flag = 1, modify_time = sysdate() WHERE namespace = ? " +
   145  		" AND `group` = ? AND file_name = ?"
   146  	_, err := dbTx.Exec(s, namespace, group, fileName)
   147  	if err != nil {
   148  		return store.Error(err)
   149  	}
   150  	return nil
   151  }
   152  
   153  // CleanDeletedConfigFileRelease 清理配置发布历史
   154  func (cfr *configFileReleaseStore) CleanDeletedConfigFileRelease(endTime time.Time, limit uint64) error {
   155  	delSql := "DELETE FROM config_file_release WHERE modify_time < ? AND flag = 1 LIMIT ?"
   156  	_, err := cfr.master.Exec(delSql, endTime, limit)
   157  	return err
   158  }
   159  
   160  // GetConfigFileActiveRelease .
   161  func (cfr *configFileReleaseStore) GetConfigFileActiveRelease(file *model.ConfigFileKey) (*model.ConfigFileRelease, error) {
   162  	tx, err := cfr.master.Begin()
   163  	if err != nil {
   164  		return nil, store.Error(err)
   165  	}
   166  	defer func() {
   167  		_ = tx.Rollback()
   168  	}()
   169  	return cfr.GetConfigFileActiveReleaseTx(NewSqlDBTx(tx), file)
   170  }
   171  
   172  // GetConfigFileActiveReleaseTx .
   173  func (cfr *configFileReleaseStore) GetConfigFileActiveReleaseTx(tx store.Tx,
   174  	file *model.ConfigFileKey) (*model.ConfigFileRelease, error) {
   175  	if tx == nil {
   176  		return nil, ErrTxIsNil
   177  	}
   178  
   179  	dbTx := tx.GetDelegateTx().(*BaseTx)
   180  	querySql := cfr.baseQuerySql() + "WHERE namespace = ? AND `group` = ? AND " +
   181  		" file_name = ? AND active = 1 AND flag = 0 "
   182  	var (
   183  		rows *sql.Rows
   184  		err  error
   185  	)
   186  
   187  	rows, err = dbTx.Query(querySql, file.Namespace, file.Group, file.Name)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	fileRelease, err := cfr.transferRows(rows)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	if len(fileRelease) > 1 {
   196  		return nil, errors.New("multi active file release found")
   197  	}
   198  	if len(fileRelease) > 0 {
   199  		return fileRelease[0], nil
   200  	}
   201  	return nil, nil
   202  }
   203  
   204  // ActiveConfigFileReleaseTx
   205  func (cfr *configFileReleaseStore) ActiveConfigFileReleaseTx(tx store.Tx, release *model.ConfigFileRelease) error {
   206  	if tx == nil {
   207  		return ErrTxIsNil
   208  	}
   209  
   210  	dbTx := tx.GetDelegateTx().(*BaseTx)
   211  	maxVersion, err := cfr.inactiveConfigFileRelease(dbTx, release)
   212  	if err != nil {
   213  		return err
   214  	}
   215  	args := []interface{}{maxVersion + 1, release.Namespace, release.Group,
   216  		release.FileName, release.Name}
   217  	//	update 指定的 release 记录,设置其 active、version 以及 mtime
   218  	updateSql := "UPDATE config_file_release SET active = 1, version = ?, modify_time = sysdate() " +
   219  		" WHERE namespace = ? AND `group` = ? AND file_name = ? AND name = ?"
   220  	if _, err := dbTx.Exec(updateSql, args...); err != nil {
   221  		return store.Error(err)
   222  	}
   223  	return nil
   224  }
   225  
   226  func (cfr *configFileReleaseStore) inactiveConfigFileRelease(tx *BaseTx,
   227  	release *model.ConfigFileRelease) (uint64, error) {
   228  	if tx == nil {
   229  		return 0, ErrTxIsNil
   230  	}
   231  
   232  	args := []interface{}{release.Namespace, release.Group, release.FileName}
   233  	//	先取消所有 active == true 的记录
   234  	if _, err := tx.Exec("UPDATE config_file_release SET active = 0, modify_time = sysdate() "+
   235  		" WHERE namespace = ? AND `group` = ? AND file_name = ? AND active = 1", args...); err != nil {
   236  		return 0, err
   237  	}
   238  
   239  	//	生成最新的 version 版本信息
   240  	row := tx.QueryRow("SELECT IFNULL(MAX(`version`), 0) FROM config_file_release WHERE namespace = ? AND "+
   241  		" `group` = ? AND file_name = ?", args...)
   242  	var maxVersion uint64
   243  	if err := row.Scan(&maxVersion); err != nil {
   244  		return 0, err
   245  	}
   246  	return maxVersion, nil
   247  }
   248  
   249  // GetMoreReleaseFile 获取最近更新的配置文件发布, 此方法用于 cache 增量更新,需要注意 modifyTime 应为数据库时间戳
   250  func (cfr *configFileReleaseStore) GetMoreReleaseFile(firstUpdate bool,
   251  	modifyTime time.Time) ([]*model.ConfigFileRelease, error) {
   252  
   253  	if firstUpdate {
   254  		modifyTime = time.Time{}
   255  	}
   256  
   257  	s := cfr.baseQuerySql() + " WHERE modify_time > FROM_UNIXTIME(?)"
   258  	rows, err := cfr.slave.Query(s, timeToTimestamp(modifyTime))
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	releases, err := cfr.transferRows(rows)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	return releases, nil
   267  }
   268  
   269  // CountConfigReleases 获取一个配置文件组下的文件数量
   270  func (cfr *configFileReleaseStore) CountConfigReleases(namespace, group string, onlyActive bool) (uint64, error) {
   271  	metricsSql := "SELECT count(file_name) FROM config_file_release WHERE flag = 0 " +
   272  		" AND namespace = ? AND `group` = ?"
   273  	if onlyActive {
   274  		metricsSql = "SELECT count(file_name) FROM config_file_release WHERE flag = 0 " +
   275  			" AND namespace = ? AND `group` = ? AND active = 1"
   276  	}
   277  	row := cfr.master.QueryRow(metricsSql, namespace, group)
   278  	var total uint64
   279  	if err := row.Scan(&total); err != nil {
   280  		return 0, store.Error(err)
   281  	}
   282  	return total, nil
   283  }
   284  
   285  func (cfr *configFileReleaseStore) baseQuerySql() string {
   286  	return "SELECT id, name, namespace, `group`, file_name, content, IFNULL(comment, ''), " +
   287  		" md5, version, UNIX_TIMESTAMP(create_time), IFNULL(create_by, ''), UNIX_TIMESTAMP(modify_time), " +
   288  		" IFNULL(modify_by, ''), flag, IFNULL(tags, ''), active, IFNULL(description, '') FROM config_file_release "
   289  }
   290  
   291  func (cfr *configFileReleaseStore) transferRows(rows *sql.Rows) ([]*model.ConfigFileRelease, error) {
   292  	if rows == nil {
   293  		return nil, nil
   294  	}
   295  	defer func() {
   296  		_ = rows.Close()
   297  	}()
   298  
   299  	var fileReleases []*model.ConfigFileRelease
   300  
   301  	for rows.Next() {
   302  		fileRelease := model.NewConfigFileRelease()
   303  		var (
   304  			ctime, mtime, active int64
   305  			tags                 string
   306  		)
   307  		err := rows.Scan(&fileRelease.Id, &fileRelease.Name, &fileRelease.Namespace, &fileRelease.Group,
   308  			&fileRelease.FileName, &fileRelease.Content,
   309  			&fileRelease.Comment, &fileRelease.Md5, &fileRelease.Version, &ctime, &fileRelease.CreateBy,
   310  			&mtime, &fileRelease.ModifyBy, &fileRelease.Flag, &tags, &active, &fileRelease.ReleaseDescription)
   311  		if err != nil {
   312  			return nil, err
   313  		}
   314  		fileRelease.CreateTime = time.Unix(ctime, 0)
   315  		fileRelease.ModifyTime = time.Unix(mtime, 0)
   316  		fileRelease.Active = active == 1
   317  		fileRelease.Valid = fileRelease.Flag == 0
   318  		fileRelease.Metadata = map[string]string{}
   319  		_ = json.Unmarshal([]byte(tags), &fileRelease.Metadata)
   320  		fileReleases = append(fileReleases, fileRelease)
   321  	}
   322  
   323  	if err := rows.Err(); err != nil {
   324  		return nil, err
   325  	}
   326  
   327  	return fileReleases, nil
   328  }