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 }