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  }