go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/postgresql/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 postgresql
    26  
    27  import (
    28  	"context"
    29  	"database/sql"
    30  	"strconv"
    31  	"strings"
    32  
    33  	p "go.temporal.io/server/common/persistence"
    34  
    35  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    36  )
    37  
    38  const constMetadataPartition = 0
    39  const constMembershipPartition = 0
    40  const (
    41  	// ****** CLUSTER_METADATA_INFO TABLE ******
    42  	insertClusterMetadataQry = `INSERT INTO cluster_metadata_info (metadata_partition, cluster_name, data, data_encoding, version) VALUES($1, $2, $3, $4, $5)`
    43  
    44  	updateClusterMetadataQry = `UPDATE cluster_metadata_info SET data = $1, data_encoding = $2, version = $3 WHERE metadata_partition = $4 AND cluster_name = $5`
    45  
    46  	getClusterMetadataBase      = `SELECT data, data_encoding, version FROM cluster_metadata_info `
    47  	getClusterMetadataQry       = getClusterMetadataBase + `WHERE metadata_partition = $1 AND cluster_name = $2`
    48  	listClusterMetadataQry      = getClusterMetadataBase + `WHERE metadata_partition = $1 ORDER BY cluster_name LIMIT $2`
    49  	listClusterMetadataRangeQry = getClusterMetadataBase + `WHERE metadata_partition = $1 AND cluster_name > $2 ORDER BY cluster_name LIMIT $3`
    50  
    51  	writeLockGetClusterMetadataQry = getClusterMetadataQry + ` FOR UPDATE`
    52  
    53  	deleteClusterMetadataQry = `DELETE FROM cluster_metadata_info WHERE metadata_partition = $1 AND cluster_name = $2`
    54  
    55  	// ****** CLUSTER_MEMBERSHIP TABLE ******
    56  	templateUpsertActiveClusterMembership = `INSERT INTO
    57  cluster_membership (membership_partition, host_id, rpc_address, rpc_port, role, session_start, last_heartbeat, record_expiry)
    58  VALUES($1, $2, $3, $4, $5, $6, $7, $8)
    59  ON CONFLICT(membership_partition, host_id)
    60  DO UPDATE SET 
    61  membership_partition = $1, host_id = $2, rpc_address = $3, rpc_port = $4, role = $5, session_start = $6, last_heartbeat = $7, record_expiry = $8`
    62  
    63  	templatePruneStaleClusterMembership = `DELETE FROM
    64  cluster_membership 
    65  WHERE host_id = ANY(ARRAY(
    66  SELECT host_id FROM cluster_membership WHERE membership_partition = $1 AND record_expiry < $2))`
    67  
    68  	templateGetClusterMembership = `SELECT host_id, rpc_address, rpc_port, role, session_start, last_heartbeat, record_expiry FROM
    69  cluster_membership WHERE membership_partition = $`
    70  
    71  	// ClusterMembership WHERE Suffixes
    72  	templateWithRoleSuffix           = ` AND role = $`
    73  	templateWithHeartbeatSinceSuffix = ` AND last_heartbeat > $`
    74  	templateWithRecordExpirySuffix   = ` AND record_expiry > $`
    75  	templateWithRPCAddressSuffix     = ` AND rpc_address = $`
    76  	templateWithHostIDSuffix         = ` AND host_id = $`
    77  	templateWithHostIDGreaterSuffix  = ` AND host_id > $`
    78  	templateWithSessionStartSuffix   = ` AND session_start >= $`
    79  
    80  	// Generic SELECT Suffixes
    81  	templateWithLimitSuffix               = ` LIMIT $`
    82  	templateWithOrderBySessionStartSuffix = ` ORDER BY membership_partition ASC, host_id ASC`
    83  )
    84  
    85  func (pdb *db) SaveClusterMetadata(
    86  	ctx context.Context,
    87  	row *sqlplugin.ClusterMetadataRow,
    88  ) (sql.Result, error) {
    89  	if row.Version == 0 {
    90  		return pdb.conn.ExecContext(ctx,
    91  			insertClusterMetadataQry,
    92  			constMetadataPartition,
    93  			row.ClusterName,
    94  			row.Data,
    95  			row.DataEncoding,
    96  			1,
    97  		)
    98  	}
    99  	return pdb.conn.ExecContext(ctx,
   100  		updateClusterMetadataQry,
   101  		row.Data,
   102  		row.DataEncoding,
   103  		row.Version+1,
   104  		constMetadataPartition,
   105  		row.ClusterName,
   106  	)
   107  }
   108  
   109  func (pdb *db) ListClusterMetadata(
   110  	ctx context.Context,
   111  	filter *sqlplugin.ClusterMetadataFilter,
   112  ) ([]sqlplugin.ClusterMetadataRow, error) {
   113  	var err error
   114  	var rows []sqlplugin.ClusterMetadataRow
   115  	switch {
   116  	case len(filter.ClusterName) != 0:
   117  		err = pdb.conn.SelectContext(ctx,
   118  			&rows,
   119  			listClusterMetadataRangeQry,
   120  			constMetadataPartition,
   121  			filter.ClusterName,
   122  			filter.PageSize,
   123  		)
   124  	default:
   125  		err = pdb.conn.SelectContext(ctx,
   126  			&rows,
   127  			listClusterMetadataQry,
   128  			constMetadataPartition,
   129  			filter.PageSize,
   130  		)
   131  	}
   132  	return rows, err
   133  }
   134  
   135  func (pdb *db) GetClusterMetadata(
   136  	ctx context.Context,
   137  	filter *sqlplugin.ClusterMetadataFilter,
   138  ) (*sqlplugin.ClusterMetadataRow, error) {
   139  	var row sqlplugin.ClusterMetadataRow
   140  	err := pdb.conn.GetContext(ctx,
   141  		&row,
   142  		getClusterMetadataQry,
   143  		constMetadataPartition,
   144  		filter.ClusterName,
   145  	)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	return &row, err
   150  }
   151  
   152  func (pdb *db) WriteLockGetClusterMetadata(
   153  	ctx context.Context,
   154  	filter *sqlplugin.ClusterMetadataFilter,
   155  ) (*sqlplugin.ClusterMetadataRow, error) {
   156  	var row sqlplugin.ClusterMetadataRow
   157  	err := pdb.conn.GetContext(ctx,
   158  		&row,
   159  		writeLockGetClusterMetadataQry,
   160  		constMetadataPartition,
   161  		filter.ClusterName,
   162  	)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return &row, nil
   167  }
   168  
   169  func (pdb *db) DeleteClusterMetadata(
   170  	ctx context.Context,
   171  	filter *sqlplugin.ClusterMetadataFilter,
   172  ) (sql.Result, error) {
   173  
   174  	return pdb.conn.ExecContext(ctx,
   175  		deleteClusterMetadataQry,
   176  		constMetadataPartition,
   177  		filter.ClusterName,
   178  	)
   179  }
   180  
   181  func (pdb *db) UpsertClusterMembership(
   182  	ctx context.Context,
   183  	row *sqlplugin.ClusterMembershipRow,
   184  ) (sql.Result, error) {
   185  	return pdb.conn.ExecContext(ctx,
   186  		templateUpsertActiveClusterMembership,
   187  		constMembershipPartition,
   188  		row.HostID,
   189  		row.RPCAddress,
   190  		row.RPCPort,
   191  		row.Role,
   192  		row.SessionStart,
   193  		row.LastHeartbeat,
   194  		row.RecordExpiry)
   195  }
   196  
   197  func (pdb *db) GetClusterMembers(
   198  	ctx context.Context,
   199  	filter *sqlplugin.ClusterMembershipFilter,
   200  ) ([]sqlplugin.ClusterMembershipRow, error) {
   201  	var queryString strings.Builder
   202  	var operands []interface{}
   203  	queryString.WriteString(templateGetClusterMembership)
   204  	operands = append(operands, constMembershipPartition)
   205  	queryString.WriteString(strconv.Itoa(len(operands)))
   206  
   207  	if filter.HostIDEquals != nil {
   208  		queryString.WriteString(templateWithHostIDSuffix)
   209  		operands = append(operands, filter.HostIDEquals)
   210  		queryString.WriteString(strconv.Itoa(len(operands)))
   211  	}
   212  
   213  	if filter.RPCAddressEquals != "" {
   214  		queryString.WriteString(templateWithRPCAddressSuffix)
   215  		operands = append(operands, filter.RPCAddressEquals)
   216  		queryString.WriteString(strconv.Itoa(len(operands)))
   217  	}
   218  
   219  	if filter.RoleEquals != p.All {
   220  		queryString.WriteString(templateWithRoleSuffix)
   221  		operands = append(operands, filter.RoleEquals)
   222  		queryString.WriteString(strconv.Itoa(len(operands)))
   223  	}
   224  
   225  	if !filter.LastHeartbeatAfter.IsZero() {
   226  		queryString.WriteString(templateWithHeartbeatSinceSuffix)
   227  		operands = append(operands, filter.LastHeartbeatAfter)
   228  		queryString.WriteString(strconv.Itoa(len(operands)))
   229  	}
   230  
   231  	if !filter.RecordExpiryAfter.IsZero() {
   232  		queryString.WriteString(templateWithRecordExpirySuffix)
   233  		operands = append(operands, filter.RecordExpiryAfter)
   234  		queryString.WriteString(strconv.Itoa(len(operands)))
   235  	}
   236  
   237  	if !filter.SessionStartedAfter.IsZero() {
   238  		queryString.WriteString(templateWithSessionStartSuffix)
   239  		operands = append(operands, filter.SessionStartedAfter)
   240  		queryString.WriteString(strconv.Itoa(len(operands)))
   241  	}
   242  
   243  	if filter.HostIDGreaterThan != nil {
   244  		queryString.WriteString(templateWithHostIDGreaterSuffix)
   245  		operands = append(operands, filter.HostIDGreaterThan)
   246  		queryString.WriteString(strconv.Itoa(len(operands)))
   247  	}
   248  
   249  	queryString.WriteString(templateWithOrderBySessionStartSuffix)
   250  
   251  	if filter.MaxRecordCount > 0 {
   252  		queryString.WriteString(templateWithLimitSuffix)
   253  		operands = append(operands, filter.MaxRecordCount)
   254  		queryString.WriteString(strconv.Itoa(len(operands)))
   255  	}
   256  
   257  	compiledQryString := queryString.String()
   258  
   259  	var rows []sqlplugin.ClusterMembershipRow
   260  	err := pdb.conn.SelectContext(ctx, &rows,
   261  		compiledQryString,
   262  		operands...)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	for i := range rows {
   267  		rows[i].SessionStart = pdb.converter.FromPostgreSQLDateTime(rows[i].SessionStart)
   268  		rows[i].LastHeartbeat = pdb.converter.FromPostgreSQLDateTime(rows[i].LastHeartbeat)
   269  		rows[i].RecordExpiry = pdb.converter.FromPostgreSQLDateTime(rows[i].RecordExpiry)
   270  	}
   271  	return rows, nil
   272  }
   273  
   274  func (pdb *db) PruneClusterMembership(
   275  	ctx context.Context,
   276  	filter *sqlplugin.PruneClusterMembershipFilter,
   277  ) (sql.Result, error) {
   278  	return pdb.conn.ExecContext(ctx,
   279  		templatePruneStaleClusterMembership,
   280  		constMembershipPartition,
   281  		filter.PruneRecordsBefore,
   282  	)
   283  }