go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/mysql/task.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 "fmt" 31 "strings" 32 33 "go.temporal.io/api/serviceerror" 34 35 "go.temporal.io/server/common/persistence" 36 "go.temporal.io/server/common/persistence/sql/sqlplugin" 37 ) 38 39 const ( 40 taskQueueCreatePart = `INTO task_queues(range_hash, task_queue_id, range_id, data, data_encoding) ` + 41 `VALUES (:range_hash, :task_queue_id, :range_id, :data, :data_encoding)` 42 43 // (default range ID: initialRangeID == 1) 44 createTaskQueueQry = `INSERT ` + taskQueueCreatePart 45 46 updateTaskQueueQry = `UPDATE task_queues SET 47 range_id = :range_id, 48 data = :data, 49 data_encoding = :data_encoding 50 WHERE 51 range_hash = :range_hash AND 52 task_queue_id = :task_queue_id 53 ` 54 55 listTaskQueueRowSelect = `SELECT range_hash, task_queue_id, range_id, data, data_encoding from task_queues ` 56 57 listTaskQueueWithHashRangeQry = listTaskQueueRowSelect + 58 `WHERE range_hash >= ? AND range_hash <= ? AND task_queue_id > ? ORDER BY task_queue_id ASC LIMIT ?` 59 60 listTaskQueueQry = listTaskQueueRowSelect + 61 `WHERE range_hash = ? AND task_queue_id > ? ORDER BY task_queue_id ASC LIMIT ?` 62 63 getTaskQueueQry = listTaskQueueRowSelect + 64 `WHERE range_hash = ? AND task_queue_id = ?` 65 66 deleteTaskQueueQry = `DELETE FROM task_queues WHERE range_hash=? AND task_queue_id=? AND range_id=?` 67 68 lockTaskQueueQry = `SELECT range_id FROM task_queues ` + 69 `WHERE range_hash = ? AND task_queue_id = ? FOR UPDATE` 70 // *** Task_Queues Table Above *** 71 72 // *** Tasks Below *** 73 getTaskMinMaxQry = `SELECT task_id, data, data_encoding ` + 74 `FROM tasks ` + 75 `WHERE range_hash = ? AND task_queue_id = ? AND task_id >= ? AND task_id < ? ` + 76 ` ORDER BY task_id LIMIT ?` 77 78 getTaskMinQry = `SELECT task_id, data, data_encoding ` + 79 `FROM tasks ` + 80 `WHERE range_hash = ? AND task_queue_id = ? AND task_id >= ? ORDER BY task_id LIMIT ?` 81 82 createTaskQry = `INSERT INTO ` + 83 `tasks(range_hash, task_queue_id, task_id, data, data_encoding) ` + 84 `VALUES(:range_hash, :task_queue_id, :task_id, :data, :data_encoding)` 85 86 deleteTaskQry = `DELETE FROM tasks ` + 87 `WHERE range_hash = ? AND task_queue_id = ? AND task_id = ?` 88 89 rangeDeleteTaskQry = `DELETE FROM tasks ` + 90 `WHERE range_hash = ? AND task_queue_id = ? AND task_id < ? ` + 91 `ORDER BY task_queue_id,task_id LIMIT ?` 92 93 getTaskQueueUserDataQry = `SELECT data, data_encoding, version FROM task_queue_user_data ` + 94 `WHERE namespace_id = ? AND task_queue_name = ?` 95 96 updateTaskQueueUserDataQry = `UPDATE task_queue_user_data SET ` + 97 `data = ?, ` + 98 `data_encoding = ?, ` + 99 `version = ? ` + 100 `WHERE namespace_id = ? ` + 101 `AND task_queue_name = ? ` + 102 `AND version = ?` 103 104 insertTaskQueueUserDataQry = `INSERT INTO task_queue_user_data` + 105 `(namespace_id, task_queue_name, data, data_encoding, version) ` + 106 `VALUES (?, ?, ?, ?, 1)` 107 108 listTaskQueueUserDataQry = `SELECT task_queue_name, data, data_encoding, version FROM task_queue_user_data WHERE namespace_id = ? AND task_queue_name > ? LIMIT ?` 109 110 addBuildIdToTaskQueueMappingQry = `INSERT INTO build_id_to_task_queue (namespace_id, build_id, task_queue_name) VALUES ` 111 removeBuildIdToTaskQueueMappingQry = `DELETE FROM build_id_to_task_queue WHERE namespace_id = ? AND task_queue_name = ? AND build_id IN (` 112 listTaskQueuesByBuildIdQry = `SELECT task_queue_name FROM build_id_to_task_queue WHERE namespace_id = ? AND build_id = ?` 113 countTaskQueuesByBuildIdQry = `SELECT COUNT(*) FROM build_id_to_task_queue WHERE namespace_id = ? AND build_id = ?` 114 ) 115 116 // InsertIntoTasks inserts one or more rows into tasks table 117 func (mdb *db) InsertIntoTasks( 118 ctx context.Context, 119 rows []sqlplugin.TasksRow, 120 ) (sql.Result, error) { 121 return mdb.conn.NamedExecContext(ctx, 122 createTaskQry, 123 rows, 124 ) 125 } 126 127 // SelectFromTasks reads one or more rows from tasks table 128 func (mdb *db) SelectFromTasks( 129 ctx context.Context, 130 filter sqlplugin.TasksFilter, 131 ) ([]sqlplugin.TasksRow, error) { 132 var err error 133 var rows []sqlplugin.TasksRow 134 switch { 135 case filter.ExclusiveMaxTaskID != nil: 136 err = mdb.conn.SelectContext(ctx, 137 &rows, getTaskMinMaxQry, 138 filter.RangeHash, 139 filter.TaskQueueID, 140 *filter.InclusiveMinTaskID, 141 *filter.ExclusiveMaxTaskID, 142 *filter.PageSize, 143 ) 144 default: 145 err = mdb.conn.SelectContext(ctx, 146 &rows, getTaskMinQry, 147 filter.RangeHash, 148 filter.TaskQueueID, 149 *filter.InclusiveMinTaskID, 150 *filter.PageSize, 151 ) 152 } 153 if err != nil { 154 return nil, err 155 } 156 return rows, nil 157 } 158 159 // DeleteFromTasks deletes one or more rows from tasks table 160 func (mdb *db) DeleteFromTasks( 161 ctx context.Context, 162 filter sqlplugin.TasksFilter, 163 ) (sql.Result, error) { 164 if filter.ExclusiveMaxTaskID != nil { 165 if filter.Limit == nil || *filter.Limit == 0 { 166 return nil, fmt.Errorf("missing limit parameter") 167 } 168 return mdb.conn.ExecContext(ctx, 169 rangeDeleteTaskQry, 170 filter.RangeHash, 171 filter.TaskQueueID, 172 *filter.ExclusiveMaxTaskID, 173 *filter.Limit, 174 ) 175 } 176 return mdb.conn.ExecContext(ctx, 177 deleteTaskQry, 178 filter.RangeHash, 179 filter.TaskQueueID, 180 *filter.TaskID, 181 ) 182 } 183 184 // InsertIntoTaskQueues inserts one or more rows into task_queues table 185 func (mdb *db) InsertIntoTaskQueues( 186 ctx context.Context, 187 row *sqlplugin.TaskQueuesRow, 188 ) (sql.Result, error) { 189 return mdb.conn.NamedExecContext(ctx, 190 createTaskQueueQry, 191 row, 192 ) 193 } 194 195 // UpdateTaskQueues updates a row in task_queues table 196 func (mdb *db) UpdateTaskQueues( 197 ctx context.Context, 198 row *sqlplugin.TaskQueuesRow, 199 ) (sql.Result, error) { 200 return mdb.conn.NamedExecContext(ctx, 201 updateTaskQueueQry, 202 row, 203 ) 204 } 205 206 // SelectFromTaskQueues reads one or more rows from task_queues table 207 func (mdb *db) SelectFromTaskQueues( 208 ctx context.Context, 209 filter sqlplugin.TaskQueuesFilter, 210 ) ([]sqlplugin.TaskQueuesRow, error) { 211 switch { 212 case filter.TaskQueueID != nil: 213 if filter.RangeHashLessThanEqualTo != 0 || filter.RangeHashGreaterThanEqualTo != 0 { 214 return nil, serviceerror.NewInternal("range of hashes not supported for specific selection") 215 } 216 return mdb.selectFromTaskQueues(ctx, filter) 217 case filter.RangeHashLessThanEqualTo != 0 && filter.PageSize != nil: 218 if filter.RangeHashLessThanEqualTo < filter.RangeHashGreaterThanEqualTo { 219 return nil, serviceerror.NewInternal("range of hashes bound is invalid") 220 } 221 return mdb.rangeSelectFromTaskQueues(ctx, filter) 222 case filter.TaskQueueIDGreaterThan != nil && filter.PageSize != nil: 223 return mdb.rangeSelectFromTaskQueues(ctx, filter) 224 default: 225 return nil, serviceerror.NewInternal("invalid set of query filter params") 226 } 227 } 228 229 func (mdb *db) selectFromTaskQueues( 230 ctx context.Context, 231 filter sqlplugin.TaskQueuesFilter, 232 ) ([]sqlplugin.TaskQueuesRow, error) { 233 var err error 234 var row sqlplugin.TaskQueuesRow 235 err = mdb.conn.GetContext(ctx, 236 &row, 237 getTaskQueueQry, 238 filter.RangeHash, 239 filter.TaskQueueID, 240 ) 241 if err != nil { 242 return nil, err 243 } 244 return []sqlplugin.TaskQueuesRow{row}, nil 245 } 246 247 func (mdb *db) rangeSelectFromTaskQueues( 248 ctx context.Context, 249 filter sqlplugin.TaskQueuesFilter, 250 ) ([]sqlplugin.TaskQueuesRow, error) { 251 var err error 252 var rows []sqlplugin.TaskQueuesRow 253 254 if filter.RangeHashLessThanEqualTo != 0 { 255 err = mdb.conn.SelectContext(ctx, 256 &rows, 257 listTaskQueueWithHashRangeQry, 258 filter.RangeHashGreaterThanEqualTo, 259 filter.RangeHashLessThanEqualTo, 260 filter.TaskQueueIDGreaterThan, 261 *filter.PageSize, 262 ) 263 } else { 264 err = mdb.conn.SelectContext(ctx, 265 &rows, 266 listTaskQueueQry, 267 filter.RangeHash, 268 filter.TaskQueueIDGreaterThan, 269 *filter.PageSize, 270 ) 271 } 272 if err != nil { 273 return nil, err 274 } 275 return rows, nil 276 } 277 278 // DeleteFromTaskQueues deletes a row from task_queues table 279 func (mdb *db) DeleteFromTaskQueues( 280 ctx context.Context, 281 filter sqlplugin.TaskQueuesFilter, 282 ) (sql.Result, error) { 283 return mdb.conn.ExecContext(ctx, 284 deleteTaskQueueQry, 285 filter.RangeHash, 286 filter.TaskQueueID, 287 *filter.RangeID, 288 ) 289 } 290 291 // LockTaskQueues locks a row in task_queues table 292 func (mdb *db) LockTaskQueues( 293 ctx context.Context, 294 filter sqlplugin.TaskQueuesFilter, 295 ) (int64, error) { 296 var rangeID int64 297 err := mdb.conn.GetContext(ctx, 298 &rangeID, 299 lockTaskQueueQry, 300 filter.RangeHash, 301 filter.TaskQueueID, 302 ) 303 return rangeID, err 304 } 305 306 func (mdb *db) GetTaskQueueUserData(ctx context.Context, request *sqlplugin.GetTaskQueueUserDataRequest) (*sqlplugin.VersionedBlob, error) { 307 var row sqlplugin.VersionedBlob 308 err := mdb.conn.GetContext(ctx, &row, getTaskQueueUserDataQry, request.NamespaceID, request.TaskQueueName) 309 return &row, err 310 } 311 312 func (mdb *db) UpdateTaskQueueUserData(ctx context.Context, request *sqlplugin.UpdateTaskQueueDataRequest) error { 313 if request.Version == 0 { 314 _, err := mdb.conn.ExecContext( 315 ctx, 316 insertTaskQueueUserDataQry, 317 request.NamespaceID, 318 request.TaskQueueName, 319 request.Data, 320 request.DataEncoding) 321 return err 322 } 323 result, err := mdb.conn.ExecContext( 324 ctx, 325 updateTaskQueueUserDataQry, 326 request.Data, 327 request.DataEncoding, 328 request.Version+1, 329 request.NamespaceID, 330 request.TaskQueueName, 331 request.Version) 332 if err != nil { 333 return err 334 } 335 numRows, err := result.RowsAffected() 336 if err != nil { 337 return err 338 } 339 if numRows != 1 { 340 return &persistence.ConditionFailedError{Msg: "Expected exactly one row to be updated"} 341 } 342 return nil 343 } 344 345 func (mdb *db) AddToBuildIdToTaskQueueMapping(ctx context.Context, request sqlplugin.AddToBuildIdToTaskQueueMapping) error { 346 query := addBuildIdToTaskQueueMappingQry 347 var params []any 348 for idx, buildId := range request.BuildIds { 349 if idx == len(request.BuildIds)-1 { 350 query += "(?, ?, ?)" 351 } else { 352 query += "(?, ?, ?), " 353 } 354 params = append(params, request.NamespaceID, buildId, request.TaskQueueName) 355 } 356 357 _, err := mdb.conn.ExecContext(ctx, query, params...) 358 return err 359 } 360 361 func (mdb *db) RemoveFromBuildIdToTaskQueueMapping(ctx context.Context, request sqlplugin.RemoveFromBuildIdToTaskQueueMapping) error { 362 query := removeBuildIdToTaskQueueMappingQry + strings.Repeat("?, ", len(request.BuildIds)-1) + "?)" 363 // Golang doesn't support appending a string slice to an any slice which is essentially what we're doing here. 364 params := make([]any, len(request.BuildIds)+2) 365 params[0] = request.NamespaceID 366 params[1] = request.TaskQueueName 367 for i, buildId := range request.BuildIds { 368 params[i+2] = buildId 369 } 370 371 _, err := mdb.conn.ExecContext(ctx, query, params...) 372 return err 373 } 374 375 func (mdb *db) ListTaskQueueUserDataEntries(ctx context.Context, request *sqlplugin.ListTaskQueueUserDataEntriesRequest) ([]sqlplugin.TaskQueueUserDataEntry, error) { 376 var rows []sqlplugin.TaskQueueUserDataEntry 377 err := mdb.conn.SelectContext(ctx, &rows, listTaskQueueUserDataQry, request.NamespaceID, request.LastTaskQueueName, request.Limit) 378 return rows, err 379 } 380 381 func (mdb *db) GetTaskQueuesByBuildId(ctx context.Context, request *sqlplugin.GetTaskQueuesByBuildIdRequest) ([]string, error) { 382 var rows []struct { 383 TaskQueueName string 384 } 385 386 err := mdb.conn.SelectContext(ctx, &rows, listTaskQueuesByBuildIdQry, request.NamespaceID, request.BuildID) 387 taskQueues := make([]string, len(rows)) 388 for i, row := range rows { 389 taskQueues[i] = row.TaskQueueName 390 } 391 return taskQueues, err 392 } 393 394 func (mdb *db) CountTaskQueuesByBuildId(ctx context.Context, request *sqlplugin.CountTaskQueuesByBuildIdRequest) (int, error) { 395 var count int 396 err := mdb.conn.GetContext(ctx, &count, countTaskQueuesByBuildIdQry, request.NamespaceID, request.BuildID) 397 return count, err 398 }