github.com/polarismesh/polaris@v1.17.8/store/mysql/transaction.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  	"crypto/rand"
    22  	"math/big"
    23  
    24  	"github.com/pkg/errors"
    25  
    26  	"github.com/polarismesh/polaris/common/model"
    27  )
    28  
    29  // transaction 事务; 不支持多协程并发操作,当前先支持单个协程串行操作
    30  type transaction struct {
    31  	tx     *BaseTx
    32  	failed bool // 判断事务执行是否失败
    33  	commit bool // 判断事务已经提交,如果已经提交,则Commit会立即返回
    34  }
    35  
    36  // Commit 提交事务,释放tx
    37  func (t *transaction) Commit() error {
    38  	if t.commit {
    39  		return nil
    40  	}
    41  
    42  	t.commit = true
    43  	if t.failed {
    44  		return t.tx.Rollback()
    45  	}
    46  
    47  	return t.tx.Commit()
    48  }
    49  
    50  // LockBootstrap 启动锁,限制Server启动的并发数
    51  func (t *transaction) LockBootstrap(key string, server string) error {
    52  	countStr := "select count(*) from start_lock where lock_key = ?"
    53  	var count int
    54  	if err := t.tx.QueryRow(countStr, key).Scan(&count); err != nil {
    55  		log.Errorf("[Store][database] lock bootstrap scan count err: %s", err.Error())
    56  		t.failed = true
    57  		return err
    58  	}
    59  
    60  	bid, err := rand.Int(rand.Reader, big.NewInt(1024))
    61  	if err != nil {
    62  		log.Errorf("[Store][database] rand int err: %s", err.Error())
    63  		return err
    64  	}
    65  
    66  	log.Infof("[Store][database] get rand int: %d", bid.Int64())
    67  	id := int(bid.Int64())%count + 1
    68  	// innodb_lock_wait_timeout这个global变量表示锁超时的时间,cdb为7200秒
    69  	log.Infof("[Store][database] update start lock_id: %d, lock_key: %s, lock server: %s", id, key, server)
    70  	lockStr := "update start_lock set server = ? where lock_id = ? and lock_key = ?"
    71  	if _, err := t.tx.Exec(lockStr, server, id, key); err != nil {
    72  		log.Errorf("[Store][database] update start lock err: %s", err.Error())
    73  		t.failed = true
    74  		return err
    75  	}
    76  
    77  	return nil
    78  }
    79  
    80  // LockNamespace 排它锁,锁住指定命名空间
    81  func (t *transaction) LockNamespace(name string) (*model.Namespace, error) {
    82  	str := genNamespaceSelectSQL() + " where name = ? and flag != 1 for update"
    83  	return t.getValidNamespace(str, name)
    84  }
    85  
    86  // RLockNamespace 共享锁,锁住命名空间
    87  func (t *transaction) RLockNamespace(name string) (*model.Namespace, error) {
    88  	str := genNamespaceSelectSQL() + " where name = ? and flag != 1 lock in share mode"
    89  	return t.getValidNamespace(str, name)
    90  }
    91  
    92  // DeleteNamespace 删除命名空间,并且提交事务
    93  func (t *transaction) DeleteNamespace(name string) error {
    94  	if err := t.finish(); err != nil {
    95  		return err
    96  	}
    97  
    98  	str := "update namespace set flag = 1, mtime = sysdate() where name = ?"
    99  	if _, err := t.tx.Exec(str, name); err != nil {
   100  		t.failed = true
   101  	}
   102  
   103  	return t.Commit()
   104  }
   105  
   106  // LockService 排它锁,锁住指定服务
   107  func (t *transaction) LockService(name string, namespace string) (*model.Service, error) {
   108  	str := genServiceSelectSQL() +
   109  		" from service where name = ? and namespace = ? and flag !=1 for update"
   110  	return t.getValidService(str, name, namespace)
   111  }
   112  
   113  // RLockService 共享锁,锁住指定服务
   114  func (t *transaction) RLockService(name string, namespace string) (*model.Service, error) {
   115  	str := genServiceSelectSQL() +
   116  		" from service where name = ? and namespace = ? and flag !=1 lock in share mode"
   117  	return t.getValidService(str, name, namespace)
   118  }
   119  
   120  // BatchRLockServices 批量锁住服务
   121  func (t *transaction) BatchRLockServices(ids map[string]bool) (map[string]bool, error) {
   122  	str := "select id, flag from service where id in ( "
   123  	first := true
   124  	args := make([]interface{}, 0, len(ids))
   125  	for id := range ids {
   126  		if first {
   127  			str += "?"
   128  			first = false
   129  		} else {
   130  			str += ", ?"
   131  		}
   132  		args = append(args, id)
   133  	}
   134  	str += ") and flag != 1 lock in share mode"
   135  	log.Infof("[Store][database] RLock services: %+v", args)
   136  	rows, err := t.tx.Query(str, args...)
   137  	if err != nil {
   138  		log.Errorf("[Store][database] batch RLock services err: %s", err.Error())
   139  		return nil, err
   140  	}
   141  	defer rows.Close()
   142  
   143  	out := make(map[string]bool)
   144  	var flag int
   145  	var id string
   146  	for rows.Next() {
   147  		if err := rows.Scan(&id, &flag); err != nil {
   148  			log.Errorf("[Store][database] RLock services scan err: %s", err.Error())
   149  			return nil, err
   150  		}
   151  
   152  		if flag == 0 {
   153  			out[id] = true
   154  		} else {
   155  			out[id] = false
   156  		}
   157  	}
   158  	if err := rows.Err(); err != nil {
   159  		log.Errorf("[Store][database] RLock service rows next err: %s", err.Error())
   160  		return nil, err
   161  	}
   162  
   163  	return out, nil
   164  }
   165  
   166  // DeleteService 删除服务,并且提交事务
   167  func (t *transaction) DeleteService(name string, namespace string) error {
   168  	if err := t.finish(); err != nil {
   169  		return err
   170  	}
   171  
   172  	str := "update service set flag = 1, mtime = sysdate() where name = ? and namespace = ?"
   173  	if _, err := t.tx.Exec(str, name, namespace); err != nil {
   174  		log.Errorf("[Store][database] delete service err: %s", err.Error())
   175  		t.failed = true
   176  		return err
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  // DeleteAliasWithSourceID 根据源服务的ID,删除其所有的别名
   183  func (t *transaction) DeleteAliasWithSourceID(sourceServiceID string) error {
   184  	if err := t.finish(); err != nil {
   185  		return err
   186  	}
   187  
   188  	str := `update service set flag = 1, mtime = sysdate() where reference = ?`
   189  	if _, err := t.tx.Exec(str, sourceServiceID); err != nil {
   190  		log.Errorf("[Store][database] delete service alias err: %s", err.Error())
   191  		t.failed = false
   192  		return err
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  // finish 判断事务是否已经提交
   199  func (t *transaction) finish() error {
   200  	if t.failed || t.commit {
   201  		return errors.New("transaction has failed")
   202  	}
   203  
   204  	return nil
   205  }
   206  
   207  // getValidNamespace 获取有效的命名空间数据
   208  func (t *transaction) getValidNamespace(sql string, name string) (*model.Namespace, error) {
   209  	if err := t.finish(); err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	rows, err := t.tx.Query(sql, name)
   214  	if err != nil {
   215  		t.failed = true
   216  		return nil, err
   217  	}
   218  
   219  	out, err := namespaceFetchRows(rows)
   220  	if err != nil {
   221  		t.failed = true
   222  		return nil, err
   223  	}
   224  
   225  	if len(out) == 0 {
   226  		return nil, nil
   227  	}
   228  	return out[0], nil
   229  }
   230  
   231  // getValidService 获取有效的服务数据
   232  // 注意:该函数不会返回service_metadata
   233  func (t *transaction) getValidService(sql string, name string, namespace string) (*model.Service, error) {
   234  	if err := t.finish(); err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	rows, err := t.tx.Query(sql, name, namespace)
   239  	if err != nil {
   240  		t.failed = true
   241  		return nil, err
   242  	}
   243  
   244  	out, err := fetchServiceRows(rows)
   245  	if err != nil {
   246  		t.failed = true
   247  		return nil, err
   248  	}
   249  
   250  	if len(out) == 0 {
   251  		return nil, nil
   252  	}
   253  
   254  	return out[0], nil
   255  }