go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/mysql/queue.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  
    31  	"go.temporal.io/server/common/persistence"
    32  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    33  )
    34  
    35  const (
    36  	templateEnqueueMessageQuery      = `INSERT INTO queue (queue_type, message_id, message_payload, message_encoding) VALUES(:queue_type, :message_id, :message_payload, :message_encoding)`
    37  	templateGetMessageQuery          = `SELECT message_id, message_payload, message_encoding FROM queue WHERE queue_type = ? and message_id = ?`
    38  	templateGetMessagesQuery         = `SELECT message_id, message_payload, message_encoding FROM queue WHERE queue_type = ? and message_id > ? and message_id <= ? ORDER BY message_id ASC LIMIT ?`
    39  	templateDeleteMessageQuery       = `DELETE FROM queue WHERE queue_type = ? and message_id = ?`
    40  	templateRangeDeleteMessagesQuery = `DELETE FROM queue WHERE queue_type = ? and message_id > ? and message_id <= ?`
    41  
    42  	// Note that even though this query takes a range lock that serializes all writes, it will return multiple rows
    43  	// whenever more than one enqueue-er blocks. This is why we max().
    44  	templateGetLastMessageIDQuery = `SELECT MAX(message_id) FROM queue WHERE message_id >= (SELECT message_id FROM queue WHERE queue_type=? ORDER BY message_id DESC LIMIT 1) FOR UPDATE`
    45  
    46  	templateCreateQueueMetadataQuery = `INSERT INTO queue_metadata (queue_type, data, data_encoding, version) VALUES(:queue_type, :data, :data_encoding, :version)`
    47  	templateUpdateQueueMetadataQuery = `UPDATE queue_metadata SET data = :data, data_encoding = :data_encoding, version= :version+1 WHERE queue_type = :queue_type and version = :version`
    48  	templateGetQueueMetadataQuery    = `SELECT data, data_encoding, version from queue_metadata WHERE queue_type = ?`
    49  	templateLockQueueMetadataQuery   = templateGetQueueMetadataQuery + " FOR UPDATE"
    50  )
    51  
    52  // InsertIntoMessages inserts a new row into queue table
    53  func (mdb *db) InsertIntoMessages(
    54  	ctx context.Context,
    55  	row []sqlplugin.QueueMessageRow,
    56  ) (sql.Result, error) {
    57  	return mdb.conn.NamedExecContext(ctx,
    58  		templateEnqueueMessageQuery,
    59  		row,
    60  	)
    61  }
    62  
    63  func (mdb *db) SelectFromMessages(
    64  	ctx context.Context,
    65  	filter sqlplugin.QueueMessagesFilter,
    66  ) ([]sqlplugin.QueueMessageRow, error) {
    67  	var rows []sqlplugin.QueueMessageRow
    68  	err := mdb.conn.SelectContext(ctx,
    69  		&rows,
    70  		templateGetMessageQuery,
    71  		filter.QueueType,
    72  		filter.MessageID,
    73  	)
    74  	return rows, err
    75  }
    76  
    77  func (mdb *db) RangeSelectFromMessages(
    78  	ctx context.Context,
    79  	filter sqlplugin.QueueMessagesRangeFilter,
    80  ) ([]sqlplugin.QueueMessageRow, error) {
    81  	var rows []sqlplugin.QueueMessageRow
    82  	err := mdb.conn.SelectContext(ctx,
    83  		&rows,
    84  		templateGetMessagesQuery,
    85  		filter.QueueType,
    86  		filter.MinMessageID,
    87  		filter.MaxMessageID,
    88  		filter.PageSize,
    89  	)
    90  	return rows, err
    91  }
    92  
    93  // DeleteFromMessages deletes message with a messageID from the queue
    94  func (mdb *db) DeleteFromMessages(
    95  	ctx context.Context,
    96  	filter sqlplugin.QueueMessagesFilter,
    97  ) (sql.Result, error) {
    98  	return mdb.conn.ExecContext(ctx,
    99  		templateDeleteMessageQuery,
   100  		filter.QueueType,
   101  		filter.MessageID,
   102  	)
   103  }
   104  
   105  // RangeDeleteFromMessages deletes messages before messageID from the queue
   106  func (mdb *db) RangeDeleteFromMessages(
   107  	ctx context.Context,
   108  	filter sqlplugin.QueueMessagesRangeFilter,
   109  ) (sql.Result, error) {
   110  	return mdb.conn.ExecContext(ctx,
   111  		templateRangeDeleteMessagesQuery,
   112  		filter.QueueType,
   113  		filter.MinMessageID,
   114  		filter.MaxMessageID,
   115  	)
   116  }
   117  
   118  // GetLastEnqueuedMessageIDForUpdate returns the last enqueued message ID
   119  func (mdb *db) GetLastEnqueuedMessageIDForUpdate(
   120  	ctx context.Context,
   121  	queueType persistence.QueueType,
   122  ) (int64, error) {
   123  	var lastMessageID *int64
   124  	err := mdb.conn.GetContext(ctx,
   125  		&lastMessageID,
   126  		templateGetLastMessageIDQuery,
   127  		queueType,
   128  	)
   129  	if lastMessageID == nil {
   130  		// The layer of code above us expects ErrNoRows when the queue is empty. MAX() yields
   131  		// null when the queue is empty, so we need to turn that into the correct error.
   132  		return 0, sql.ErrNoRows
   133  	} else {
   134  		return *lastMessageID, err
   135  	}
   136  }
   137  
   138  func (mdb *db) InsertIntoQueueMetadata(
   139  	ctx context.Context,
   140  	row *sqlplugin.QueueMetadataRow,
   141  ) (sql.Result, error) {
   142  	return mdb.conn.NamedExecContext(ctx,
   143  		templateCreateQueueMetadataQuery,
   144  		row,
   145  	)
   146  }
   147  
   148  func (mdb *db) UpdateQueueMetadata(
   149  	ctx context.Context,
   150  	row *sqlplugin.QueueMetadataRow,
   151  ) (sql.Result, error) {
   152  	return mdb.conn.NamedExecContext(ctx,
   153  		templateUpdateQueueMetadataQuery,
   154  		row,
   155  	)
   156  }
   157  
   158  func (mdb *db) SelectFromQueueMetadata(
   159  	ctx context.Context,
   160  	filter sqlplugin.QueueMetadataFilter,
   161  ) (*sqlplugin.QueueMetadataRow, error) {
   162  	var row sqlplugin.QueueMetadataRow
   163  	err := mdb.conn.GetContext(ctx,
   164  		&row,
   165  		templateGetQueueMetadataQuery,
   166  		filter.QueueType,
   167  	)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	return &row, nil
   172  }
   173  
   174  func (mdb *db) LockQueueMetadata(
   175  	ctx context.Context,
   176  	filter sqlplugin.QueueMetadataFilter,
   177  ) (*sqlplugin.QueueMetadataRow, error) {
   178  	var row sqlplugin.QueueMetadataRow
   179  	err := mdb.conn.GetContext(ctx,
   180  		&row,
   181  		templateLockQueueMetadataQuery,
   182  		filter.QueueType,
   183  	)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return &row, nil
   188  }