github.com/polarismesh/polaris@v1.17.8/store/mysql/config_file.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 "strings" 23 "time" 24 25 "go.uber.org/zap" 26 27 "github.com/polarismesh/polaris/common/model" 28 "github.com/polarismesh/polaris/common/utils" 29 "github.com/polarismesh/polaris/store" 30 ) 31 32 var ( 33 configFileStoreFieldMapping = map[string]map[string]string{ 34 "config_file": { 35 "group": "`group`", 36 "file_name": "name", 37 "namespace": "namespace", 38 }, 39 "config_file_release": { 40 "group": "`group`", 41 "file_name": "file_name", 42 "release_name": "name", 43 }, 44 "config_file_group": {}, 45 } 46 ) 47 48 var _ store.ConfigFileStore = (*configFileStore)(nil) 49 50 type configFileStore struct { 51 master *BaseDB 52 slave *BaseDB 53 } 54 55 // LockConfigFile 加锁配置文件 56 func (cf *configFileStore) LockConfigFile(tx store.Tx, file *model.ConfigFileKey) (*model.ConfigFile, error) { 57 if tx == nil { 58 return nil, ErrTxIsNil 59 } 60 61 dbTx := tx.GetDelegateTx().(*BaseTx) 62 args := []interface{}{file.Namespace, file.Group, file.Name} 63 lockSql := cf.baseSelectConfigFileSql() + 64 " WHERE namespace = ? AND `group` = ? AND name = ? AND flag = 0 FOR UPDATE" 65 66 rows, err := dbTx.Query(lockSql, args...) 67 if err != nil { 68 return nil, store.Error(err) 69 } 70 files, err := cf.transferRows(rows) 71 if err != nil { 72 return nil, err 73 } 74 if len(files) > 0 { 75 return files[0], nil 76 } 77 return nil, nil 78 } 79 80 // CreateConfigFile 创建配置文件 81 func (cf *configFileStore) CreateConfigFileTx(tx store.Tx, file *model.ConfigFile) error { 82 if tx == nil { 83 return ErrTxIsNil 84 } 85 86 dbTx := tx.GetDelegateTx().(*BaseTx) 87 deleteSql := "DELETE FROM config_file WHERE namespace = ? AND `group` = ? AND name = ? AND flag = 1" 88 if _, err := dbTx.Exec(deleteSql, file.Namespace, file.Group, file.Name); err != nil { 89 return store.Error(err) 90 } 91 92 createSql := "INSERT INTO config_file( " + 93 " name, namespace, `group`, content, comment, format, create_time, " + 94 "create_by, modify_time, modify_by) " + 95 " VALUES " + 96 "(?, ?, ?, ?, ?, ?, sysdate(), ?, sysdate(), ?)" 97 if _, err := dbTx.Exec(createSql, file.Name, file.Namespace, file.Group, 98 file.Content, file.Comment, file.Format, file.CreateBy, file.ModifyBy); err != nil { 99 return store.Error(err) 100 } 101 102 if err := cf.batchCleanTags(dbTx, file); err != nil { 103 return store.Error(err) 104 } 105 if err := cf.batchAddTags(dbTx, file); err != nil { 106 return store.Error(err) 107 } 108 return nil 109 } 110 111 func (cf *configFileStore) batchAddTags(tx *BaseTx, file *model.ConfigFile) error { 112 if len(file.Metadata) == 0 { 113 return nil 114 } 115 116 // 添加配置标签 117 insertSql := "INSERT INTO config_file_tag(" + 118 " `key`, `value`, namespace, `group`, file_name, create_time, create_by, modify_time, modify_by) " + 119 " VALUES " 120 valuesSql := []string{} 121 args := []interface{}{} 122 for k, v := range file.Metadata { 123 valuesSql = append(valuesSql, " (?, ?, ?, ?, ?, sysdate(), ?, sysdate(), ?) ") 124 args = append(args, k, v, file.Namespace, file.Group, file.Name, file.CreateBy, file.ModifyBy) 125 } 126 insertSql = insertSql + strings.Join(valuesSql, ",") 127 _, err := tx.Exec(insertSql, args...) 128 return store.Error(err) 129 } 130 131 func (cf *configFileStore) batchCleanTags(tx *BaseTx, file *model.ConfigFile) error { 132 // 添加配置标签 133 cleanSql := "DELETE FROM config_file_tag WHERE namespace = ? AND `group` = ? AND file_name = ? " 134 args := []interface{}{file.Namespace, file.Group, file.Name} 135 _, err := tx.Exec(cleanSql, args...) 136 return store.Error(err) 137 } 138 139 func (cf *configFileStore) loadFileTags(tx *BaseTx, file *model.ConfigFile) error { 140 querySql := "SELECT `key`, `value` FROM config_file_tag WHERE namespace = ? AND " + 141 " `group` = ? AND file_name = ? " 142 143 rows, err := tx.Query(querySql, file.Namespace, file.Group, file.Name) 144 if err != nil { 145 return err 146 } 147 if rows == nil { 148 return nil 149 } 150 defer rows.Close() 151 152 file.Metadata = make(map[string]string) 153 for rows.Next() { 154 var key, value string 155 if err := rows.Scan(&key, &value); err != nil { 156 return err 157 } 158 file.Metadata[key] = value 159 } 160 return nil 161 } 162 163 // CountConfigFiles 获取一个配置文件组下的文件数量 164 func (cfr *configFileStore) CountConfigFiles(namespace, group string) (uint64, error) { 165 metricsSql := "SELECT count(*) FROM config_file WHERE flag = 0 AND namespace = ? AND `group` = ?" 166 row := cfr.slave.QueryRow(metricsSql, namespace, group) 167 var total uint64 168 if err := row.Scan(&total); err != nil { 169 return 0, store.Error(err) 170 } 171 return total, nil 172 } 173 174 // GetConfigFile 获取配置文件 175 func (cf *configFileStore) GetConfigFile(namespace, group, name string) (*model.ConfigFile, error) { 176 tx, err := cf.master.Begin() 177 if err != nil { 178 return nil, store.Error(err) 179 } 180 defer func() { 181 _ = tx.Rollback() 182 }() 183 184 return cf.GetConfigFileTx(NewSqlDBTx(tx), namespace, group, name) 185 } 186 187 // GetConfigFile 获取配置文件 188 func (cf *configFileStore) GetConfigFileTx(tx store.Tx, 189 namespace, group, name string) (*model.ConfigFile, error) { 190 if tx == nil { 191 return nil, ErrTxIsNil 192 } 193 194 dbTx := tx.GetDelegateTx().(*BaseTx) 195 querySql := cf.baseSelectConfigFileSql() + "WHERE namespace = ? AND `group` = ? AND name = ? AND flag = 0" 196 rows, err := dbTx.Query(querySql, namespace, group, name) 197 if err != nil { 198 return nil, store.Error(err) 199 } 200 files, err := cf.transferRows(rows) 201 if err != nil { 202 return nil, store.Error(err) 203 } 204 if len(files) == 0 { 205 return nil, nil 206 } 207 if err := cf.loadFileTags(dbTx, files[0]); err != nil { 208 return nil, store.Error(err) 209 } 210 return files[0], nil 211 } 212 213 // UpdateConfigFile 更新配置文件 214 func (cf *configFileStore) UpdateConfigFileTx(tx store.Tx, file *model.ConfigFile) error { 215 if tx == nil { 216 return ErrTxIsNil 217 } 218 219 updateSql := "UPDATE config_file SET content = ?, comment = ?, format = ?, modify_time = sysdate(), " + 220 " modify_by = ? WHERE namespace = ? AND `group` = ? AND name = ?" 221 dbTx := tx.GetDelegateTx().(*BaseTx) 222 _, err := dbTx.Exec(updateSql, file.Content, file.Comment, file.Format, 223 file.ModifyBy, file.Namespace, file.Group, file.Name) 224 if err != nil { 225 return store.Error(err) 226 } 227 228 if err := cf.batchCleanTags(dbTx, file); err != nil { 229 return store.Error(err) 230 } 231 if err := cf.batchAddTags(dbTx, file); err != nil { 232 return store.Error(err) 233 } 234 return nil 235 } 236 237 // DeleteConfigFileTx 删除配置文件 238 func (cf *configFileStore) DeleteConfigFileTx(tx store.Tx, namespace, group, name string) error { 239 if tx == nil { 240 return ErrTxIsNil 241 } 242 243 deleteSql := "UPDATE config_file SET flag = 1 WHERE namespace = ? AND `group` = ? AND name = ?" 244 dbTx := tx.GetDelegateTx().(*BaseTx) 245 if _, err := dbTx.Exec(deleteSql, namespace, group, name); err != nil { 246 return store.Error(err) 247 } 248 return nil 249 } 250 251 // QueryConfigFiles 翻页查询配置文件,group、name可为模糊匹配 252 func (cf *configFileStore) QueryConfigFiles(filter map[string]string, offset, limit uint32) (uint32, []*model.ConfigFile, error) { 253 254 countSql := "SELECT COUNT(*) FROM config_file WHERE flag = 0 " 255 querySql := cf.baseSelectConfigFileSql() + " WHERE flag = 0 " 256 257 args := make([]interface{}, 0, len(filter)) 258 searchQuery := make([]string, 0, len(filter)) 259 260 for k, v := range filter { 261 if v, ok := configFileStoreFieldMapping["config_file"][k]; ok { 262 k = v 263 } 264 if utils.IsWildName(v) { 265 searchQuery = append(searchQuery, k+" LIKE ? ") 266 } else { 267 searchQuery = append(searchQuery, k+" = ? ") 268 } 269 args = append(args, utils.ParseWildNameForSql(v)) 270 } 271 272 if len(searchQuery) > 0 { 273 countSql = countSql + " AND " 274 querySql = querySql + " AND " 275 } 276 countSql = countSql + (strings.Join(searchQuery, " AND ")) 277 278 var count uint32 279 err := cf.master.QueryRow(countSql, args...).Scan(&count) 280 if err != nil { 281 log.Error("[Config][Storage] query config files", zap.String("count-sql", countSql), zap.Error(err)) 282 return 0, nil, store.Error(err) 283 } 284 285 querySql = querySql + (strings.Join(searchQuery, " AND ")) + " ORDER BY id DESC LIMIT ?, ? " 286 287 args = append(args, offset, limit) 288 rows, err := cf.master.Query(querySql, args...) 289 if err != nil { 290 log.Error("[Config][Storage] query config files", zap.String("query-sql", countSql), zap.Error(err)) 291 return 0, nil, store.Error(err) 292 } 293 294 files, err := cf.transferRows(rows) 295 if err != nil { 296 return 0, nil, store.Error(err) 297 } 298 299 err = cf.slave.processWithTransaction("batch-load-file-tags", func(tx *BaseTx) error { 300 for i := range files { 301 item := files[i] 302 if err := cf.loadFileTags(tx, item); err != nil { 303 return err 304 } 305 } 306 return nil 307 }) 308 if err != nil { 309 return 0, nil, store.Error(err) 310 } 311 312 return count, files, nil 313 } 314 315 // CountConfigFileEachGroup 316 func (cf *configFileStore) CountConfigFileEachGroup() (map[string]map[string]int64, error) { 317 metricsSql := "SELECT namespace, `group`, count(name) FROM config_file WHERE flag = 0 GROUP by namespace, `group`" 318 rows, err := cf.slave.Query(metricsSql) 319 if err != nil { 320 return nil, store.Error(err) 321 } 322 323 defer func() { 324 _ = rows.Close() 325 }() 326 327 ret := map[string]map[string]int64{} 328 for rows.Next() { 329 var ( 330 namespce string 331 group string 332 cnt int64 333 ) 334 335 if err := rows.Scan(&namespce, &group, &cnt); err != nil { 336 return nil, err 337 } 338 if _, ok := ret[namespce]; !ok { 339 ret[namespce] = map[string]int64{} 340 } 341 ret[namespce][group] = cnt 342 } 343 344 return ret, nil 345 } 346 347 func (cf *configFileStore) baseSelectConfigFileSql() string { 348 return "SELECT id, name, namespace, `group`, content, IFNULL(comment, ''), format, " + 349 " UNIX_TIMESTAMP(create_time), IFNULL(create_by, ''), UNIX_TIMESTAMP(modify_time), " + 350 " IFNULL(modify_by, '') FROM config_file " 351 } 352 353 func (cf *configFileStore) hardDeleteConfigFile(namespace, group, name string) error { 354 deleteSql := "DELETE FROM config_file WHERE namespace = ? AND `group` = ? AND name = ? AND flag = 1" 355 _, err := cf.master.Exec(deleteSql, namespace, group, name) 356 if err != nil { 357 return store.Error(err) 358 } 359 360 return nil 361 } 362 363 func (cf *configFileStore) transferRows(rows *sql.Rows) ([]*model.ConfigFile, error) { 364 if rows == nil { 365 return nil, nil 366 } 367 defer rows.Close() 368 369 var ( 370 files = make([]*model.ConfigFile, 0, 32) 371 ) 372 373 for rows.Next() { 374 file := &model.ConfigFile{ 375 Metadata: map[string]string{}, 376 } 377 var ctime, mtime int64 378 if err := rows.Scan(&file.Id, &file.Name, &file.Namespace, &file.Group, &file.Content, &file.Comment, 379 &file.Format, &ctime, &file.CreateBy, &mtime, &file.ModifyBy); err != nil { 380 return nil, err 381 } 382 file.CreateTime = time.Unix(ctime, 0) 383 file.ModifyTime = time.Unix(mtime, 0) 384 files = append(files, file) 385 } 386 387 if err := rows.Err(); err != nil { 388 return nil, err 389 } 390 return files, nil 391 }