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  }