go.temporal.io/server@v1.23.0/common/persistence/sql/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 sql 26 27 import ( 28 "bytes" 29 "context" 30 "database/sql" 31 "encoding/json" 32 "fmt" 33 "math" 34 35 "github.com/dgryski/go-farm" 36 commonpb "go.temporal.io/api/common/v1" 37 enumspb "go.temporal.io/api/enums/v1" 38 "go.temporal.io/api/serviceerror" 39 40 "go.temporal.io/server/common/log" 41 "go.temporal.io/server/common/persistence" 42 "go.temporal.io/server/common/persistence/sql/sqlplugin" 43 "go.temporal.io/server/common/primitives" 44 ) 45 46 type ( 47 taskQueuePageToken struct { 48 MinRangeHash uint32 49 MinTaskQueueId []byte 50 } 51 52 sqlTaskManager struct { 53 SqlStore 54 taskScanPartitions uint32 55 } 56 ) 57 58 var ( 59 // minUUID = primitives.MustParseUUID("00000000-0000-0000-0000-000000000000") 60 minTaskQueueId = make([]byte, 0) 61 ) 62 63 // newTaskPersistence creates a new instance of TaskManager 64 func newTaskPersistence( 65 db sqlplugin.DB, 66 taskScanPartitions int, 67 logger log.Logger, 68 ) (persistence.TaskStore, error) { 69 return &sqlTaskManager{ 70 SqlStore: NewSqlStore(db, logger), 71 taskScanPartitions: uint32(taskScanPartitions), 72 }, nil 73 } 74 75 func (m *sqlTaskManager) CreateTaskQueue( 76 ctx context.Context, 77 request *persistence.InternalCreateTaskQueueRequest, 78 ) error { 79 nidBytes, err := primitives.ParseUUID(request.NamespaceID) 80 if err != nil { 81 return serviceerror.NewInternal(err.Error()) 82 } 83 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue, request.TaskType) 84 85 row := sqlplugin.TaskQueuesRow{ 86 RangeHash: tqHash, 87 TaskQueueID: tqId, 88 RangeID: request.RangeID, 89 Data: request.TaskQueueInfo.Data, 90 DataEncoding: request.TaskQueueInfo.EncodingType.String(), 91 } 92 if _, err := m.Db.InsertIntoTaskQueues(ctx, &row); err != nil { 93 if m.Db.IsDupEntryError(err) { 94 return &persistence.ConditionFailedError{Msg: err.Error()} 95 } 96 return serviceerror.NewUnavailable(fmt.Sprintf("CreateTaskQueue operation failed. Failed to make task queue %v of type %v. Error: %v", request.TaskQueue, request.TaskType, err)) 97 } 98 99 return nil 100 } 101 102 func (m *sqlTaskManager) GetTaskQueue( 103 ctx context.Context, 104 request *persistence.InternalGetTaskQueueRequest, 105 ) (*persistence.InternalGetTaskQueueResponse, error) { 106 nidBytes, err := primitives.ParseUUID(request.NamespaceID) 107 if err != nil { 108 return nil, serviceerror.NewInternal(err.Error()) 109 } 110 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue, request.TaskType) 111 rows, err := m.Db.SelectFromTaskQueues(ctx, sqlplugin.TaskQueuesFilter{ 112 RangeHash: tqHash, 113 TaskQueueID: tqId, 114 }) 115 116 switch err { 117 case nil: 118 if len(rows) != 1 { 119 return nil, serviceerror.NewUnavailable( 120 fmt.Sprintf("GetTaskQueue operation failed. Expect exactly one result row, but got %d for task queue %v of type %v", 121 len(rows), request.TaskQueue, request.TaskType)) 122 } 123 row := rows[0] 124 return &persistence.InternalGetTaskQueueResponse{ 125 RangeID: row.RangeID, 126 TaskQueueInfo: persistence.NewDataBlob(row.Data, row.DataEncoding), 127 }, nil 128 case sql.ErrNoRows: 129 return nil, serviceerror.NewNotFound( 130 fmt.Sprintf("GetTaskQueue operation failed. TaskQueue: %v, TaskQueueType: %v, Error: %v", 131 request.TaskQueue, request.TaskType, err)) 132 default: 133 return nil, serviceerror.NewUnavailable( 134 fmt.Sprintf("GetTaskQueue operation failed. Failed to check if task queue %v of type %v existed. Error: %v", 135 request.TaskQueue, request.TaskType, err)) 136 } 137 } 138 139 func (m *sqlTaskManager) UpdateTaskQueue( 140 ctx context.Context, 141 request *persistence.InternalUpdateTaskQueueRequest, 142 ) (*persistence.UpdateTaskQueueResponse, error) { 143 nidBytes, err := primitives.ParseUUID(request.NamespaceID) 144 if err != nil { 145 return nil, serviceerror.NewInternal(err.Error()) 146 } 147 148 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue, request.TaskType) 149 var resp *persistence.UpdateTaskQueueResponse 150 err = m.txExecute(ctx, "UpdateTaskQueue", func(tx sqlplugin.Tx) error { 151 if err := lockTaskQueue(ctx, 152 tx, 153 tqHash, 154 tqId, 155 request.PrevRangeID, 156 ); err != nil { 157 return err 158 } 159 result, err := tx.UpdateTaskQueues(ctx, &sqlplugin.TaskQueuesRow{ 160 RangeHash: tqHash, 161 TaskQueueID: tqId, 162 RangeID: request.RangeID, 163 Data: request.TaskQueueInfo.Data, 164 DataEncoding: request.TaskQueueInfo.EncodingType.String(), 165 }) 166 if err != nil { 167 return err 168 } 169 rowsAffected, err := result.RowsAffected() 170 if err != nil { 171 return err 172 } 173 if rowsAffected != 1 { 174 return fmt.Errorf("%v rows were affected instead of 1", rowsAffected) 175 } 176 resp = &persistence.UpdateTaskQueueResponse{} 177 return nil 178 }) 179 return resp, err 180 } 181 182 func (m *sqlTaskManager) ListTaskQueue( 183 ctx context.Context, 184 request *persistence.ListTaskQueueRequest, 185 ) (*persistence.InternalListTaskQueueResponse, error) { 186 pageToken := taskQueuePageToken{MinTaskQueueId: minTaskQueueId} 187 if request.PageToken != nil { 188 if err := gobDeserialize(request.PageToken, &pageToken); err != nil { 189 return nil, serviceerror.NewInternal(fmt.Sprintf("error deserializing page token: %v", err)) 190 } 191 } 192 var err error 193 var rows []sqlplugin.TaskQueuesRow 194 var shardGreaterThan uint32 195 var shardLessThan uint32 196 197 i := uint32(0) 198 if pageToken.MinRangeHash > 0 { 199 // Resume partition position from page token, if exists, before entering loop 200 i = getPartitionForRangeHash(pageToken.MinRangeHash, m.taskScanPartitions) 201 } 202 203 lastPageFull := !bytes.Equal(pageToken.MinTaskQueueId, minTaskQueueId) 204 for ; i < m.taskScanPartitions; i++ { 205 // Get start/end boundaries for partition 206 shardGreaterThan, shardLessThan = getBoundariesForPartition(i, m.taskScanPartitions) 207 208 // If page token hash is greater than the boundaries for this partition, use the pageToken hash for resume point 209 if pageToken.MinRangeHash > shardGreaterThan { 210 shardGreaterThan = pageToken.MinRangeHash 211 } 212 213 filter := sqlplugin.TaskQueuesFilter{ 214 RangeHashGreaterThanEqualTo: shardGreaterThan, 215 RangeHashLessThanEqualTo: shardLessThan, 216 TaskQueueIDGreaterThan: minTaskQueueId, 217 PageSize: &request.PageSize, 218 } 219 220 if lastPageFull { 221 // Use page token TaskQueueID filter for this query and set this to false 222 // in order for the next partition so we don't miss any results. 223 filter.TaskQueueIDGreaterThan = pageToken.MinTaskQueueId 224 lastPageFull = false 225 } 226 227 rows, err = m.Db.SelectFromTaskQueues(ctx, filter) 228 if err != nil { 229 return nil, serviceerror.NewUnavailable(err.Error()) 230 } 231 232 if len(rows) > 0 { 233 break 234 } 235 } 236 237 maxRangeHash := uint32(0) 238 resp := &persistence.InternalListTaskQueueResponse{ 239 Items: make([]*persistence.InternalListTaskQueueItem, len(rows)), 240 } 241 242 for i, row := range rows { 243 resp.Items[i] = &persistence.InternalListTaskQueueItem{ 244 RangeID: row.RangeID, 245 TaskQueue: persistence.NewDataBlob(row.Data, row.DataEncoding), 246 } 247 248 // Only want to look at up to PageSize number of records to prevent losing data. 249 if row.RangeHash > maxRangeHash { 250 maxRangeHash = row.RangeHash 251 } 252 253 // Enforces PageSize 254 if i >= request.PageSize-1 { 255 break 256 } 257 } 258 259 var nextPageToken []byte 260 switch { 261 case len(rows) >= request.PageSize: 262 // Store the details of the lastRow seen up to PageSize. 263 // Note we don't increment the rangeHash as we do in the case below. 264 // This is so we can exhaust this hash before moving forward. 265 lastRow := &rows[request.PageSize-1] 266 nextPageToken, err = gobSerialize(&taskQueuePageToken{ 267 MinRangeHash: shardGreaterThan, 268 MinTaskQueueId: lastRow.TaskQueueID, 269 }) 270 case shardLessThan < math.MaxUint32: 271 // Create page token with +1 from the last rangeHash we have seen to prevent duplicating the last row. 272 // Since we have not exceeded PageSize, we are confident we won't lose data here and we have exhausted this hash. 273 nextPageToken, err = gobSerialize(&taskQueuePageToken{MinRangeHash: shardLessThan + 1, MinTaskQueueId: minTaskQueueId}) 274 } 275 276 if err != nil { 277 return nil, serviceerror.NewUnavailable(fmt.Sprintf("error serializing nextPageToken:%v", err)) 278 } 279 280 resp.NextPageToken = nextPageToken 281 return resp, nil 282 } 283 284 func getPartitionForRangeHash(rangeHash uint32, totalPartitions uint32) uint32 { 285 if totalPartitions == 0 { 286 return 0 287 } 288 return rangeHash / getPartitionBoundaryStart(1, totalPartitions) 289 } 290 291 func getPartitionBoundaryStart(partition uint32, totalPartitions uint32) uint32 { 292 if totalPartitions == 0 { 293 return 0 294 } 295 296 if partition >= totalPartitions { 297 return math.MaxUint32 298 } 299 300 return uint32((float32(partition) / float32(totalPartitions)) * math.MaxUint32) 301 } 302 303 func getBoundariesForPartition(partition uint32, totalPartitions uint32) (uint32, uint32) { 304 endBoundary := getPartitionBoundaryStart(partition+1, totalPartitions) 305 306 if endBoundary != math.MaxUint32 { 307 endBoundary-- 308 } 309 310 return getPartitionBoundaryStart(partition, totalPartitions), endBoundary 311 } 312 313 func (m *sqlTaskManager) DeleteTaskQueue( 314 ctx context.Context, 315 request *persistence.DeleteTaskQueueRequest, 316 ) error { 317 nidBytes, err := primitives.ParseUUID(request.TaskQueue.NamespaceID) 318 if err != nil { 319 return serviceerror.NewUnavailable(err.Error()) 320 } 321 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue.TaskQueueName, request.TaskQueue.TaskQueueType) 322 result, err := m.Db.DeleteFromTaskQueues(ctx, sqlplugin.TaskQueuesFilter{ 323 RangeHash: tqHash, 324 TaskQueueID: tqId, 325 RangeID: &request.RangeID, 326 }) 327 if err != nil { 328 return serviceerror.NewUnavailable(err.Error()) 329 } 330 nRows, err := result.RowsAffected() 331 if err != nil { 332 return serviceerror.NewUnavailable(fmt.Sprintf("rowsAffected returned error:%v", err)) 333 } 334 if nRows != 1 { 335 return &persistence.ConditionFailedError{ 336 Msg: fmt.Sprintf("delete failed: %v rows affected instead of 1", nRows), 337 } 338 } 339 return nil 340 } 341 func (m *sqlTaskManager) CreateTasks( 342 ctx context.Context, 343 request *persistence.InternalCreateTasksRequest, 344 ) (*persistence.CreateTasksResponse, error) { 345 nidBytes, err := primitives.ParseUUID(request.NamespaceID) 346 if err != nil { 347 return nil, serviceerror.NewUnavailable(err.Error()) 348 } 349 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue, request.TaskType) 350 351 tasksRows := make([]sqlplugin.TasksRow, len(request.Tasks)) 352 for i, v := range request.Tasks { 353 tasksRows[i] = sqlplugin.TasksRow{ 354 RangeHash: tqHash, 355 TaskQueueID: tqId, 356 TaskID: v.TaskId, 357 Data: v.Task.Data, 358 DataEncoding: v.Task.EncodingType.String(), 359 } 360 } 361 var resp *persistence.CreateTasksResponse 362 err = m.txExecute(ctx, "CreateTasks", func(tx sqlplugin.Tx) error { 363 if _, err1 := tx.InsertIntoTasks(ctx, tasksRows); err1 != nil { 364 return err1 365 } 366 // Lock task queue before committing. 367 if err := lockTaskQueue(ctx, 368 tx, 369 tqHash, 370 tqId, 371 request.RangeID, 372 ); err != nil { 373 return err 374 } 375 resp = &persistence.CreateTasksResponse{} 376 return nil 377 }) 378 return resp, err 379 } 380 381 func (m *sqlTaskManager) GetTasks( 382 ctx context.Context, 383 request *persistence.GetTasksRequest, 384 ) (*persistence.InternalGetTasksResponse, error) { 385 nidBytes, err := primitives.ParseUUID(request.NamespaceID) 386 if err != nil { 387 return nil, serviceerror.NewUnavailable(err.Error()) 388 } 389 390 inclusiveMinTaskID := request.InclusiveMinTaskID 391 exclusiveMaxTaskID := request.ExclusiveMaxTaskID 392 if len(request.NextPageToken) != 0 { 393 token, err := deserializeMatchingTaskPageToken(request.NextPageToken) 394 if err != nil { 395 return nil, err 396 } 397 inclusiveMinTaskID = token.TaskID 398 } 399 400 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue, request.TaskType) 401 rows, err := m.Db.SelectFromTasks(ctx, sqlplugin.TasksFilter{ 402 RangeHash: tqHash, 403 TaskQueueID: tqId, 404 InclusiveMinTaskID: &inclusiveMinTaskID, 405 ExclusiveMaxTaskID: &exclusiveMaxTaskID, 406 PageSize: &request.PageSize, 407 }) 408 if err != nil { 409 return nil, serviceerror.NewUnavailable(fmt.Sprintf("GetTasks operation failed. Failed to get rows. Error: %v", err)) 410 } 411 412 response := &persistence.InternalGetTasksResponse{ 413 Tasks: make([]*commonpb.DataBlob, len(rows)), 414 } 415 for i, v := range rows { 416 response.Tasks[i] = persistence.NewDataBlob(v.Data, v.DataEncoding) 417 } 418 if len(rows) == request.PageSize { 419 nextTaskID := rows[len(rows)-1].TaskID + 1 420 if nextTaskID < exclusiveMaxTaskID { 421 token, err := serializeMatchingTaskPageToken(&matchingTaskPageToken{ 422 TaskID: nextTaskID, 423 }) 424 if err != nil { 425 return nil, err 426 } 427 response.NextPageToken = token 428 } 429 } 430 431 return response, nil 432 } 433 434 func (m *sqlTaskManager) CompleteTask( 435 ctx context.Context, 436 request *persistence.CompleteTaskRequest, 437 ) error { 438 nidBytes, err := primitives.ParseUUID(request.TaskQueue.NamespaceID) 439 if err != nil { 440 return serviceerror.NewUnavailable(err.Error()) 441 } 442 443 taskID := request.TaskID 444 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueue.TaskQueueName, request.TaskQueue.TaskQueueType) 445 _, err = m.Db.DeleteFromTasks(ctx, sqlplugin.TasksFilter{ 446 RangeHash: tqHash, 447 TaskQueueID: tqId, 448 TaskID: &taskID}) 449 if err != nil && err != sql.ErrNoRows { 450 return serviceerror.NewUnavailable(err.Error()) 451 } 452 return nil 453 } 454 455 func (m *sqlTaskManager) CompleteTasksLessThan( 456 ctx context.Context, 457 request *persistence.CompleteTasksLessThanRequest, 458 ) (int, error) { 459 nidBytes, err := primitives.ParseUUID(request.NamespaceID) 460 if err != nil { 461 return 0, serviceerror.NewUnavailable(err.Error()) 462 } 463 tqId, tqHash := m.taskQueueIdAndHash(nidBytes, request.TaskQueueName, request.TaskType) 464 result, err := m.Db.DeleteFromTasks(ctx, sqlplugin.TasksFilter{ 465 RangeHash: tqHash, 466 TaskQueueID: tqId, 467 ExclusiveMaxTaskID: &request.ExclusiveMaxTaskID, 468 Limit: &request.Limit, 469 }) 470 if err != nil { 471 return 0, serviceerror.NewUnavailable(err.Error()) 472 } 473 nRows, err := result.RowsAffected() 474 if err != nil { 475 return 0, serviceerror.NewUnavailable(fmt.Sprintf("rowsAffected returned error: %v", err)) 476 } 477 return int(nRows), nil 478 } 479 480 func (m *sqlTaskManager) GetTaskQueueUserData(ctx context.Context, request *persistence.GetTaskQueueUserDataRequest) (*persistence.InternalGetTaskQueueUserDataResponse, error) { 481 namespaceID, err := primitives.ParseUUID(request.NamespaceID) 482 if err != nil { 483 return nil, serviceerror.NewInternal(fmt.Sprintf("failed to parse namespace ID as UUID: %v", err)) 484 } 485 response, err := m.Db.GetTaskQueueUserData(ctx, &sqlplugin.GetTaskQueueUserDataRequest{ 486 NamespaceID: namespaceID, 487 TaskQueueName: request.TaskQueue, 488 }) 489 if err != nil { 490 if err == sql.ErrNoRows { 491 return nil, serviceerror.NewNotFound(fmt.Sprintf("task queue user data not found for %v.%v", request.NamespaceID, request.TaskQueue)) 492 } 493 return nil, err 494 } 495 return &persistence.InternalGetTaskQueueUserDataResponse{ 496 Version: response.Version, 497 UserData: persistence.NewDataBlob(response.Data, response.DataEncoding), 498 }, nil 499 } 500 501 func (m *sqlTaskManager) UpdateTaskQueueUserData(ctx context.Context, request *persistence.InternalUpdateTaskQueueUserDataRequest) error { 502 namespaceID, err := primitives.ParseUUID(request.NamespaceID) 503 if err != nil { 504 return serviceerror.NewInternal(fmt.Sprintf("failed to parse namespace ID as UUID: %v", err)) 505 } 506 err = m.txExecute(ctx, "UpdateTaskQueueUserData", func(tx sqlplugin.Tx) error { 507 err := tx.UpdateTaskQueueUserData(ctx, &sqlplugin.UpdateTaskQueueDataRequest{ 508 NamespaceID: namespaceID, 509 TaskQueueName: request.TaskQueue, 510 Data: request.UserData.Data, 511 DataEncoding: request.UserData.EncodingType.String(), 512 Version: request.Version, 513 }) 514 if m.Db.IsDupEntryError(err) { 515 return &persistence.ConditionFailedError{Msg: err.Error()} 516 } 517 if err != nil { 518 return err 519 } 520 if len(request.BuildIdsAdded) > 0 { 521 err = tx.AddToBuildIdToTaskQueueMapping(ctx, sqlplugin.AddToBuildIdToTaskQueueMapping{ 522 NamespaceID: namespaceID, 523 TaskQueueName: request.TaskQueue, 524 BuildIds: request.BuildIdsAdded, 525 }) 526 if err != nil { 527 return err 528 } 529 } 530 if len(request.BuildIdsRemoved) > 0 { 531 err = tx.RemoveFromBuildIdToTaskQueueMapping(ctx, sqlplugin.RemoveFromBuildIdToTaskQueueMapping{ 532 NamespaceID: namespaceID, 533 TaskQueueName: request.TaskQueue, 534 BuildIds: request.BuildIdsRemoved, 535 }) 536 if err != nil { 537 return err 538 } 539 } 540 return nil 541 }) 542 return err 543 } 544 545 func (m *sqlTaskManager) ListTaskQueueUserDataEntries(ctx context.Context, request *persistence.ListTaskQueueUserDataEntriesRequest) (*persistence.InternalListTaskQueueUserDataEntriesResponse, error) { 546 namespaceID, err := primitives.ParseUUID(request.NamespaceID) 547 if err != nil { 548 return nil, serviceerror.NewInternal(err.Error()) 549 } 550 551 lastQueueName := "" 552 if len(request.NextPageToken) != 0 { 553 token, err := deserializeUserDataListNextPageToken(request.NextPageToken) 554 if err != nil { 555 return nil, err 556 } 557 lastQueueName = token.LastTaskQueueName 558 } 559 560 rows, err := m.Db.ListTaskQueueUserDataEntries(ctx, &sqlplugin.ListTaskQueueUserDataEntriesRequest{ 561 NamespaceID: namespaceID, 562 LastTaskQueueName: lastQueueName, 563 Limit: request.PageSize, 564 }) 565 if err != nil { 566 return nil, serviceerror.NewUnavailable(fmt.Sprintf("ListTaskQueueUserDataEntries operation failed. Failed to get rows. Error: %v", err)) 567 } 568 569 var nextPageToken []byte 570 if len(rows) == request.PageSize { 571 nextPageToken, err = serializeUserDataListNextPageToken(&userDataListNextPageToken{LastTaskQueueName: rows[request.PageSize-1].TaskQueueName}) 572 if err != nil { 573 return nil, serviceerror.NewInternal(err.Error()) 574 } 575 } 576 entries := make([]persistence.InternalTaskQueueUserDataEntry, len(rows)) 577 for i, row := range rows { 578 entries[i].TaskQueue = rows[i].TaskQueueName 579 entries[i].Data = persistence.NewDataBlob(row.Data, row.DataEncoding) 580 entries[i].Version = rows[i].Version 581 } 582 response := &persistence.InternalListTaskQueueUserDataEntriesResponse{ 583 Entries: entries, 584 NextPageToken: nextPageToken, 585 } 586 587 return response, nil 588 } 589 590 func (m *sqlTaskManager) GetTaskQueuesByBuildId(ctx context.Context, request *persistence.GetTaskQueuesByBuildIdRequest) ([]string, error) { 591 namespaceID, err := primitives.ParseUUID(request.NamespaceID) 592 if err != nil { 593 return nil, serviceerror.NewInternal(err.Error()) 594 } 595 return m.Db.GetTaskQueuesByBuildId(ctx, &sqlplugin.GetTaskQueuesByBuildIdRequest{NamespaceID: namespaceID, BuildID: request.BuildID}) 596 } 597 598 func (m *sqlTaskManager) CountTaskQueuesByBuildId(ctx context.Context, request *persistence.CountTaskQueuesByBuildIdRequest) (int, error) { 599 namespaceID, err := primitives.ParseUUID(request.NamespaceID) 600 if err != nil { 601 return 0, serviceerror.NewInternal(err.Error()) 602 } 603 return m.Db.CountTaskQueuesByBuildId(ctx, &sqlplugin.CountTaskQueuesByBuildIdRequest{NamespaceID: namespaceID, BuildID: request.BuildID}) 604 } 605 606 // Returns uint32 hash for a particular TaskQueue/Task given a Namespace, TaskQueueName and TaskQueueType 607 func (m *sqlTaskManager) taskQueueIdAndHash( 608 namespaceID primitives.UUID, 609 name string, 610 taskType enumspb.TaskQueueType, 611 ) ([]byte, uint32) { 612 id := m.taskQueueId(namespaceID, name, taskType) 613 return id, farm.Fingerprint32(id) 614 } 615 616 func (m *sqlTaskManager) taskQueueId( 617 namespaceID primitives.UUID, 618 name string, 619 taskType enumspb.TaskQueueType, 620 ) []byte { 621 idBytes := make([]byte, 0, 16+len(name)+1) 622 idBytes = append(idBytes, namespaceID...) 623 idBytes = append(idBytes, []byte(name)...) 624 idBytes = append(idBytes, uint8(taskType)) 625 return idBytes 626 } 627 628 func lockTaskQueue( 629 ctx context.Context, 630 tx sqlplugin.Tx, 631 tqHash uint32, 632 tqId []byte, 633 oldRangeID int64, 634 ) error { 635 rangeID, err := tx.LockTaskQueues(ctx, sqlplugin.TaskQueuesFilter{ 636 RangeHash: tqHash, 637 TaskQueueID: tqId, 638 }) 639 switch err { 640 case nil: 641 if rangeID != oldRangeID { 642 return &persistence.ConditionFailedError{ 643 Msg: fmt.Sprintf("Task queue range ID was %v when it was should have been %v", rangeID, oldRangeID), 644 } 645 } 646 return nil 647 648 case sql.ErrNoRows: 649 return &persistence.ConditionFailedError{Msg: "Task queue does not exists"} 650 651 default: 652 return serviceerror.NewUnavailable(fmt.Sprintf("Failed to lock task queue. Error: %v", err)) 653 } 654 } 655 656 type matchingTaskPageToken struct { 657 TaskID int64 658 } 659 660 func serializeMatchingTaskPageToken(token *matchingTaskPageToken) ([]byte, error) { 661 return json.Marshal(token) 662 } 663 664 func deserializeMatchingTaskPageToken(payload []byte) (*matchingTaskPageToken, error) { 665 var token matchingTaskPageToken 666 if err := json.Unmarshal(payload, &token); err != nil { 667 return nil, err 668 } 669 return &token, nil 670 } 671 672 type userDataListNextPageToken struct { 673 LastTaskQueueName string 674 } 675 676 func serializeUserDataListNextPageToken(token *userDataListNextPageToken) ([]byte, error) { 677 return json.Marshal(token) 678 } 679 680 func deserializeUserDataListNextPageToken(payload []byte) (*userDataListNextPageToken, error) { 681 var token userDataListNextPageToken 682 if err := json.Unmarshal(payload, &token); err != nil { 683 return nil, err 684 } 685 return &token, nil 686 }