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 }