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 }