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 }