go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/mysql/cluster_metadata.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package mysql
    26  
    27  import (
    28  	"context"
    29  	"database/sql"
    30  	"strings"
    31  
    32  	p "go.temporal.io/server/common/persistence"
    33  
    34  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    35  )
    36  
    37  const constMetadataPartition = 0
    38  const constMembershipPartition = 0
    39  const (
    40  	// ****** CLUSTER_METADATA_INFO TABLE ******
    41  	insertClusterMetadataQry = `INSERT INTO cluster_metadata_info (metadata_partition, cluster_name, data, data_encoding, version) VALUES(?, ?, ?, ?, ?)`
    42  
    43  	updateClusterMetadataQry = `UPDATE cluster_metadata_info SET data = ?, data_encoding = ?, version = ? WHERE metadata_partition = ? AND cluster_name = ?`
    44  
    45  	getClusterMetadataBase         = `SELECT data, data_encoding, version FROM cluster_metadata_info `
    46  	listClusterMetadataQry         = getClusterMetadataBase + `WHERE metadata_partition = ? ORDER BY cluster_name LIMIT ?`
    47  	listClusterMetadataRangeQry    = getClusterMetadataBase + `WHERE metadata_partition = ? AND cluster_name > ? ORDER BY cluster_name LIMIT ?`
    48  	getClusterMetadataQry          = getClusterMetadataBase + `WHERE metadata_partition = ? AND cluster_name = ?`
    49  	writeLockGetClusterMetadataQry = getClusterMetadataQry + ` FOR UPDATE`
    50  
    51  	deleteClusterMetadataQry = `DELETE FROM cluster_metadata_info WHERE metadata_partition = ? AND cluster_name = ?`
    52  
    53  	// ****** CLUSTER_MEMBERSHIP TABLE ******
    54  	templateUpsertActiveClusterMembership = `INSERT INTO
    55  cluster_membership (membership_partition, host_id, rpc_address, rpc_port, role, session_start, last_heartbeat, record_expiry)
    56  VALUES(?, ?, ?, ?, ?, ?, ?, ?) 
    57  ON DUPLICATE KEY UPDATE 
    58  session_start=VALUES(session_start), last_heartbeat=VALUES(last_heartbeat), record_expiry=VALUES(record_expiry)`
    59  
    60  	templatePruneStaleClusterMembership = `DELETE FROM
    61  cluster_membership 
    62  WHERE membership_partition = ? AND record_expiry < ?`
    63  
    64  	templateGetClusterMembership = `SELECT host_id, rpc_address, rpc_port, role, session_start, last_heartbeat, record_expiry FROM
    65  cluster_membership WHERE membership_partition = ?`
    66  
    67  	// ClusterMembership WHERE Suffixes
    68  	templateWithRoleSuffix           = ` AND role = ?`
    69  	templateWithHeartbeatSinceSuffix = ` AND last_heartbeat > ?`
    70  	templateWithRecordExpirySuffix   = ` AND record_expiry > ?`
    71  	templateWithRPCAddressSuffix     = ` AND rpc_address = ?`
    72  	templateWithHostIDSuffix         = ` AND host_id = ?`
    73  	templateWithHostIDGreaterSuffix  = ` AND host_id > ?`
    74  	templateWithSessionStartSuffix   = ` AND session_start >= ?`
    75  
    76  	// Generic SELECT Suffixes
    77  	templateWithLimitSuffix               = ` LIMIT ?`
    78  	templateWithOrderBySessionStartSuffix = ` ORDER BY membership_partition ASC, host_id ASC`
    79  )
    80  
    81  func (mdb *db) SaveClusterMetadata(
    82  	ctx context.Context,
    83  	row *sqlplugin.ClusterMetadataRow,
    84  ) (sql.Result, error) {
    85  	if row.Version == 0 {
    86  		return mdb.conn.ExecContext(ctx,
    87  			insertClusterMetadataQry,
    88  			constMetadataPartition,
    89  			row.ClusterName,
    90  			row.Data,
    91  			row.DataEncoding,
    92  			1,
    93  		)
    94  	}
    95  	return mdb.conn.ExecContext(ctx,
    96  		updateClusterMetadataQry,
    97  		row.Data,
    98  		row.DataEncoding,
    99  		row.Version+1,
   100  		constMetadataPartition,
   101  		row.ClusterName,
   102  	)
   103  }
   104  
   105  func (mdb *db) ListClusterMetadata(
   106  	ctx context.Context,
   107  	filter *sqlplugin.ClusterMetadataFilter,
   108  ) ([]sqlplugin.ClusterMetadataRow, error) {
   109  	var err error
   110  	var rows []sqlplugin.ClusterMetadataRow
   111  	switch {
   112  	case len(filter.ClusterName) != 0:
   113  		err = mdb.conn.SelectContext(ctx,
   114  			&rows,
   115  			listClusterMetadataRangeQry,
   116  			constMetadataPartition,
   117  			filter.ClusterName,
   118  			filter.PageSize,
   119  		)
   120  	default:
   121  		err = mdb.conn.SelectContext(ctx,
   122  			&rows,
   123  			listClusterMetadataQry,
   124  			constMetadataPartition,
   125  			filter.PageSize,
   126  		)
   127  	}
   128  	return rows, err
   129  }
   130  
   131  func (mdb *db) GetClusterMetadata(
   132  	ctx context.Context,
   133  	filter *sqlplugin.ClusterMetadataFilter,
   134  ) (*sqlplugin.ClusterMetadataRow, error) {
   135  	var row sqlplugin.ClusterMetadataRow
   136  	err := mdb.conn.GetContext(ctx,
   137  		&row,
   138  		getClusterMetadataQry,
   139  		constMetadataPartition,
   140  		filter.ClusterName,
   141  	)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	return &row, err
   146  }
   147  
   148  func (mdb *db) DeleteClusterMetadata(
   149  	ctx context.Context,
   150  	filter *sqlplugin.ClusterMetadataFilter,
   151  ) (sql.Result, error) {
   152  
   153  	return mdb.conn.ExecContext(ctx,
   154  		deleteClusterMetadataQry,
   155  		constMetadataPartition,
   156  		filter.ClusterName,
   157  	)
   158  }
   159  
   160  func (mdb *db) WriteLockGetClusterMetadata(
   161  	ctx context.Context,
   162  	filter *sqlplugin.ClusterMetadataFilter,
   163  ) (*sqlplugin.ClusterMetadataRow, error) {
   164  	var row sqlplugin.ClusterMetadataRow
   165  	err := mdb.conn.GetContext(ctx,
   166  		&row,
   167  		writeLockGetClusterMetadataQry,
   168  		constMetadataPartition,
   169  		filter.ClusterName,
   170  	)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	return &row, err
   175  }
   176  
   177  func (mdb *db) UpsertClusterMembership(
   178  	ctx context.Context,
   179  	row *sqlplugin.ClusterMembershipRow,
   180  ) (sql.Result, error) {
   181  	return mdb.conn.ExecContext(ctx,
   182  		templateUpsertActiveClusterMembership,
   183  		constMembershipPartition,
   184  		row.HostID,
   185  		row.RPCAddress,
   186  		row.RPCPort,
   187  		row.Role,
   188  		mdb.converter.ToMySQLDateTime(row.SessionStart),
   189  		mdb.converter.ToMySQLDateTime(row.LastHeartbeat),
   190  		mdb.converter.ToMySQLDateTime(row.RecordExpiry))
   191  }
   192  
   193  func (mdb *db) GetClusterMembers(
   194  	ctx context.Context,
   195  	filter *sqlplugin.ClusterMembershipFilter,
   196  ) ([]sqlplugin.ClusterMembershipRow, error) {
   197  	var queryString strings.Builder
   198  	var operands []interface{}
   199  	queryString.WriteString(templateGetClusterMembership)
   200  	operands = append(operands, constMembershipPartition)
   201  
   202  	if filter.HostIDEquals != nil {
   203  		queryString.WriteString(templateWithHostIDSuffix)
   204  		operands = append(operands, filter.HostIDEquals)
   205  	}
   206  
   207  	if filter.RPCAddressEquals != "" {
   208  		queryString.WriteString(templateWithRPCAddressSuffix)
   209  		operands = append(operands, filter.RPCAddressEquals)
   210  	}
   211  
   212  	if filter.RoleEquals != p.All {
   213  		queryString.WriteString(templateWithRoleSuffix)
   214  		operands = append(operands, filter.RoleEquals)
   215  	}
   216  
   217  	if !filter.LastHeartbeatAfter.IsZero() {
   218  		queryString.WriteString(templateWithHeartbeatSinceSuffix)
   219  		operands = append(operands, filter.LastHeartbeatAfter)
   220  	}
   221  
   222  	if !filter.RecordExpiryAfter.IsZero() {
   223  		queryString.WriteString(templateWithRecordExpirySuffix)
   224  		operands = append(operands, filter.RecordExpiryAfter)
   225  	}
   226  
   227  	if !filter.SessionStartedAfter.IsZero() {
   228  		queryString.WriteString(templateWithSessionStartSuffix)
   229  		operands = append(operands, filter.SessionStartedAfter)
   230  	}
   231  
   232  	if filter.HostIDGreaterThan != nil {
   233  		queryString.WriteString(templateWithHostIDGreaterSuffix)
   234  		operands = append(operands, filter.HostIDGreaterThan)
   235  	}
   236  
   237  	queryString.WriteString(templateWithOrderBySessionStartSuffix)
   238  
   239  	if filter.MaxRecordCount > 0 {
   240  		queryString.WriteString(templateWithLimitSuffix)
   241  		operands = append(operands, filter.MaxRecordCount)
   242  	}
   243  
   244  	compiledQryString := queryString.String()
   245  
   246  	var rows []sqlplugin.ClusterMembershipRow
   247  	if err := mdb.conn.SelectContext(ctx,
   248  		&rows,
   249  		compiledQryString,
   250  		operands...,
   251  	); err != nil {
   252  		return nil, err
   253  	}
   254  	for i := range rows {
   255  		rows[i].SessionStart = mdb.converter.FromMySQLDateTime(rows[i].SessionStart)
   256  		rows[i].LastHeartbeat = mdb.converter.FromMySQLDateTime(rows[i].LastHeartbeat)
   257  		rows[i].RecordExpiry = mdb.converter.FromMySQLDateTime(rows[i].RecordExpiry)
   258  	}
   259  	return rows, nil
   260  }
   261  
   262  func (mdb *db) PruneClusterMembership(
   263  	ctx context.Context,
   264  	filter *sqlplugin.PruneClusterMembershipFilter,
   265  ) (sql.Result, error) {
   266  	return mdb.conn.ExecContext(ctx,
   267  		templatePruneStaleClusterMembership,
   268  		constMembershipPartition,
   269  		mdb.converter.ToMySQLDateTime(filter.PruneRecordsBefore),
   270  	)
   271  }