go.temporal.io/server@v1.23.0/common/persistence/sql/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 sql
    26  
    27  import (
    28  	"context"
    29  	"database/sql"
    30  	"fmt"
    31  	"net"
    32  	"time"
    33  
    34  	"go.temporal.io/api/serviceerror"
    35  
    36  	"go.temporal.io/server/common/log"
    37  	p "go.temporal.io/server/common/persistence"
    38  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    39  )
    40  
    41  type sqlClusterMetadataManager struct {
    42  	SqlStore
    43  }
    44  
    45  var _ p.ClusterMetadataStore = (*sqlClusterMetadataManager)(nil)
    46  
    47  func (s *sqlClusterMetadataManager) ListClusterMetadata(
    48  	ctx context.Context,
    49  	request *p.InternalListClusterMetadataRequest,
    50  ) (*p.InternalListClusterMetadataResponse, error) {
    51  	var clusterName string
    52  	if request.NextPageToken != nil {
    53  		err := gobDeserialize(request.NextPageToken, &clusterName)
    54  		if err != nil {
    55  			return nil, serviceerror.NewInternal(fmt.Sprintf("error deserializing page token: %v", err))
    56  		}
    57  	}
    58  
    59  	rows, err := s.Db.ListClusterMetadata(ctx, &sqlplugin.ClusterMetadataFilter{ClusterName: clusterName, PageSize: &request.PageSize})
    60  	if err != nil {
    61  		if err == sql.ErrNoRows {
    62  			return &p.InternalListClusterMetadataResponse{}, nil
    63  		}
    64  		return nil, serviceerror.NewUnavailable(fmt.Sprintf("ListClusterMetadata operation failed. Failed to get cluster metadata rows. Error: %v", err))
    65  	}
    66  
    67  	var clusterMetadata []*p.InternalGetClusterMetadataResponse
    68  	for _, row := range rows {
    69  		resp := &p.InternalGetClusterMetadataResponse{
    70  			ClusterMetadata: p.NewDataBlob(row.Data, row.DataEncoding),
    71  			Version:         row.Version,
    72  		}
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  		clusterMetadata = append(clusterMetadata, resp)
    77  	}
    78  
    79  	resp := &p.InternalListClusterMetadataResponse{ClusterMetadata: clusterMetadata}
    80  	if len(rows) >= request.PageSize {
    81  		nextPageToken, err := gobSerialize(rows[len(rows)-1].ClusterName)
    82  		if err != nil {
    83  			return nil, serviceerror.NewInternal(fmt.Sprintf("error serializing page token: %v", err))
    84  		}
    85  		resp.NextPageToken = nextPageToken
    86  	}
    87  	return resp, nil
    88  }
    89  
    90  func (s *sqlClusterMetadataManager) GetClusterMetadata(
    91  	ctx context.Context,
    92  	request *p.InternalGetClusterMetadataRequest,
    93  ) (*p.InternalGetClusterMetadataResponse, error) {
    94  	row, err := s.Db.GetClusterMetadata(ctx, &sqlplugin.ClusterMetadataFilter{ClusterName: request.ClusterName})
    95  
    96  	if err != nil {
    97  		return nil, convertCommonErrors("GetClusterMetadata", err)
    98  	}
    99  
   100  	return &p.InternalGetClusterMetadataResponse{
   101  		ClusterMetadata: p.NewDataBlob(row.Data, row.DataEncoding),
   102  		Version:         row.Version,
   103  	}, nil
   104  }
   105  
   106  func (s *sqlClusterMetadataManager) SaveClusterMetadata(
   107  	ctx context.Context,
   108  	request *p.InternalSaveClusterMetadataRequest,
   109  ) (bool, error) {
   110  	err := s.txExecute(ctx, "SaveClusterMetadata", func(tx sqlplugin.Tx) error {
   111  		oldClusterMetadata, err := tx.WriteLockGetClusterMetadata(
   112  			ctx,
   113  			&sqlplugin.ClusterMetadataFilter{ClusterName: request.ClusterName})
   114  		var lastVersion int64
   115  		if err != nil {
   116  			if err != sql.ErrNoRows {
   117  				return serviceerror.NewUnavailable(fmt.Sprintf("SaveClusterMetadata operation failed. Error %v", err))
   118  			}
   119  		} else {
   120  			lastVersion = oldClusterMetadata.Version
   121  		}
   122  		if request.Version != lastVersion {
   123  			return serviceerror.NewUnavailable(fmt.Sprintf("SaveClusterMetadata encountered version mismatch, expected %v but got %v.",
   124  				request.Version, oldClusterMetadata.Version))
   125  		}
   126  		_, err = tx.SaveClusterMetadata(ctx, &sqlplugin.ClusterMetadataRow{
   127  			ClusterName:  request.ClusterName,
   128  			Data:         request.ClusterMetadata.Data,
   129  			DataEncoding: request.ClusterMetadata.EncodingType.String(),
   130  			Version:      request.Version,
   131  		})
   132  		if err != nil {
   133  			return convertCommonErrors("SaveClusterMetadata", err)
   134  		}
   135  		return nil
   136  	})
   137  
   138  	if err != nil {
   139  		return false, serviceerror.NewUnavailable(err.Error())
   140  	}
   141  	return true, nil
   142  }
   143  
   144  func (s *sqlClusterMetadataManager) DeleteClusterMetadata(
   145  	ctx context.Context,
   146  	request *p.InternalDeleteClusterMetadataRequest,
   147  ) error {
   148  	_, err := s.Db.DeleteClusterMetadata(ctx, &sqlplugin.ClusterMetadataFilter{ClusterName: request.ClusterName})
   149  
   150  	if err != nil {
   151  		return convertCommonErrors("DeleteClusterMetadata", err)
   152  	}
   153  	return nil
   154  }
   155  
   156  func (s *sqlClusterMetadataManager) GetClusterMembers(
   157  	ctx context.Context,
   158  	request *p.GetClusterMembersRequest,
   159  ) (*p.GetClusterMembersResponse, error) {
   160  	var lastSeenHostId []byte
   161  	if len(request.NextPageToken) == 16 {
   162  		lastSeenHostId = request.NextPageToken
   163  	} else if len(request.NextPageToken) > 0 {
   164  		return nil, serviceerror.NewInternal("page token is corrupted.")
   165  	}
   166  
   167  	now := time.Now().UTC()
   168  	filter := &sqlplugin.ClusterMembershipFilter{
   169  		HostIDEquals:        request.HostIDEquals,
   170  		RoleEquals:          request.RoleEquals,
   171  		RecordExpiryAfter:   now,
   172  		SessionStartedAfter: request.SessionStartedAfter,
   173  		MaxRecordCount:      request.PageSize,
   174  	}
   175  
   176  	if lastSeenHostId != nil && filter.HostIDEquals == nil {
   177  		filter.HostIDGreaterThan = lastSeenHostId
   178  	}
   179  
   180  	if request.LastHeartbeatWithin > 0 {
   181  		filter.LastHeartbeatAfter = now.Add(-request.LastHeartbeatWithin)
   182  	}
   183  
   184  	if request.RPCAddressEquals != nil {
   185  		filter.RPCAddressEquals = request.RPCAddressEquals.String()
   186  	}
   187  
   188  	rows, err := s.Db.GetClusterMembers(ctx, filter)
   189  
   190  	if err != nil {
   191  		return nil, convertCommonErrors("GetClusterMembers", err)
   192  	}
   193  
   194  	convertedRows := make([]*p.ClusterMember, 0, len(rows))
   195  	for _, row := range rows {
   196  		convertedRows = append(convertedRows, &p.ClusterMember{
   197  			HostID:        row.HostID,
   198  			Role:          row.Role,
   199  			RPCAddress:    net.ParseIP(row.RPCAddress),
   200  			RPCPort:       row.RPCPort,
   201  			SessionStart:  row.SessionStart,
   202  			LastHeartbeat: row.LastHeartbeat,
   203  			RecordExpiry:  row.RecordExpiry,
   204  		})
   205  	}
   206  
   207  	var nextPageToken []byte
   208  	if request.PageSize > 0 && len(rows) == request.PageSize {
   209  		lastRow := rows[len(rows)-1]
   210  		nextPageToken = lastRow.HostID
   211  	}
   212  
   213  	return &p.GetClusterMembersResponse{ActiveMembers: convertedRows, NextPageToken: nextPageToken}, nil
   214  }
   215  
   216  func (s *sqlClusterMetadataManager) UpsertClusterMembership(
   217  	ctx context.Context,
   218  	request *p.UpsertClusterMembershipRequest,
   219  ) error {
   220  	now := time.Now().UTC()
   221  	recordExpiry := now.Add(request.RecordExpiry)
   222  	_, err := s.Db.UpsertClusterMembership(ctx, &sqlplugin.ClusterMembershipRow{
   223  		Role:          request.Role,
   224  		HostID:        request.HostID,
   225  		RPCAddress:    request.RPCAddress.String(),
   226  		RPCPort:       request.RPCPort,
   227  		SessionStart:  request.SessionStart,
   228  		LastHeartbeat: now,
   229  		RecordExpiry:  recordExpiry})
   230  
   231  	if err != nil {
   232  		return convertCommonErrors("UpsertClusterMembership", err)
   233  	}
   234  
   235  	return nil
   236  }
   237  
   238  func (s *sqlClusterMetadataManager) PruneClusterMembership(
   239  	ctx context.Context,
   240  	request *p.PruneClusterMembershipRequest,
   241  ) error {
   242  	_, err := s.Db.PruneClusterMembership(
   243  		ctx,
   244  		&sqlplugin.PruneClusterMembershipFilter{
   245  			PruneRecordsBefore: time.Now().UTC(),
   246  		},
   247  	)
   248  
   249  	if err != nil {
   250  		return convertCommonErrors("PruneClusterMembership", err)
   251  	}
   252  
   253  	return nil
   254  }
   255  
   256  func newClusterMetadataPersistence(
   257  	db sqlplugin.DB,
   258  	logger log.Logger,
   259  ) (p.ClusterMetadataStore, error) {
   260  	return &sqlClusterMetadataManager{
   261  		SqlStore: NewSqlStore(db, logger),
   262  	}, nil
   263  }