go.temporal.io/server@v1.23.0/common/persistence/visibility/store/standard/sql/visibility_store.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  	"context"
    29  	"encoding/json"
    30  	"fmt"
    31  	"time"
    32  
    33  	enumspb "go.temporal.io/api/enums/v1"
    34  	"go.temporal.io/api/serviceerror"
    35  
    36  	"go.temporal.io/server/common/config"
    37  	"go.temporal.io/server/common/log"
    38  	"go.temporal.io/server/common/persistence"
    39  	persistencesql "go.temporal.io/server/common/persistence/sql"
    40  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    41  	"go.temporal.io/server/common/persistence/visibility/manager"
    42  	"go.temporal.io/server/common/persistence/visibility/store"
    43  	"go.temporal.io/server/common/resolver"
    44  )
    45  
    46  type (
    47  	visibilityStore struct {
    48  		sqlStore persistencesql.SqlStore
    49  	}
    50  
    51  	visibilityPageToken struct {
    52  		Time  time.Time
    53  		RunID string
    54  	}
    55  )
    56  
    57  var _ store.VisibilityStore = (*visibilityStore)(nil)
    58  
    59  // NewSQLVisibilityStore creates an instance of VisibilityStore
    60  func NewSQLVisibilityStore(
    61  	cfg config.SQL,
    62  	r resolver.ServiceResolver,
    63  	logger log.Logger,
    64  ) (*visibilityStore, error) {
    65  	refDbConn := persistencesql.NewRefCountedDBConn(sqlplugin.DbKindVisibility, &cfg, r)
    66  	db, err := refDbConn.Get()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	return &visibilityStore{
    71  		sqlStore: persistencesql.NewSqlStore(db, logger),
    72  	}, nil
    73  }
    74  
    75  func (s *visibilityStore) Close() {
    76  	s.sqlStore.Close()
    77  }
    78  
    79  func (s *visibilityStore) GetName() string {
    80  	return s.sqlStore.GetName()
    81  }
    82  
    83  func (s *visibilityStore) GetIndexName() string {
    84  	// GetIndexName is used to get cluster metadata, which in verstions < v1.20
    85  	// were stored in an empty string key.
    86  	return ""
    87  }
    88  
    89  func (s *visibilityStore) ValidateCustomSearchAttributes(
    90  	searchAttributes map[string]any,
    91  ) (map[string]any, error) {
    92  	return searchAttributes, nil
    93  }
    94  
    95  func (s *visibilityStore) RecordWorkflowExecutionStarted(
    96  	ctx context.Context,
    97  	request *store.InternalRecordWorkflowExecutionStartedRequest,
    98  ) error {
    99  	_, err := s.sqlStore.Db.InsertIntoVisibility(ctx, &sqlplugin.VisibilityRow{
   100  		NamespaceID:      request.NamespaceID,
   101  		WorkflowID:       request.WorkflowID,
   102  		RunID:            request.RunID,
   103  		StartTime:        request.StartTime,
   104  		ExecutionTime:    request.ExecutionTime,
   105  		WorkflowTypeName: request.WorkflowTypeName,
   106  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING), // Underlying value (1) is hardcoded in SQL queries.
   107  		Memo:             request.Memo.Data,
   108  		Encoding:         request.Memo.EncodingType.String(),
   109  		TaskQueue:        request.TaskQueue,
   110  	})
   111  
   112  	return err
   113  }
   114  
   115  func (s *visibilityStore) RecordWorkflowExecutionClosed(
   116  	ctx context.Context,
   117  	request *store.InternalRecordWorkflowExecutionClosedRequest,
   118  ) error {
   119  	result, err := s.sqlStore.Db.ReplaceIntoVisibility(ctx, &sqlplugin.VisibilityRow{
   120  		NamespaceID:      request.NamespaceID,
   121  		WorkflowID:       request.WorkflowID,
   122  		RunID:            request.RunID,
   123  		StartTime:        request.StartTime,
   124  		ExecutionTime:    request.ExecutionTime,
   125  		WorkflowTypeName: request.WorkflowTypeName,
   126  		CloseTime:        &request.CloseTime,
   127  		Status:           int32(request.Status),
   128  		HistoryLength:    &request.HistoryLength,
   129  		Memo:             request.Memo.Data,
   130  		Encoding:         request.Memo.EncodingType.String(),
   131  		TaskQueue:        request.TaskQueue,
   132  	})
   133  	if err != nil {
   134  		return err
   135  	}
   136  	noRowsAffected, err := result.RowsAffected()
   137  	if err != nil {
   138  		return fmt.Errorf("RecordWorkflowExecutionClosed rowsAffected error: %v", err)
   139  	}
   140  	if noRowsAffected > 2 { // either adds a new row or deletes old row and adds new row
   141  		return fmt.Errorf("RecordWorkflowExecutionClosed unexpected numRows (%v) updated", noRowsAffected)
   142  	}
   143  	return nil
   144  }
   145  
   146  func (s *visibilityStore) UpsertWorkflowExecution(
   147  	_ context.Context,
   148  	_ *store.InternalUpsertWorkflowExecutionRequest,
   149  ) error {
   150  	// Not OperationNotSupportedErr!
   151  	return nil
   152  }
   153  
   154  func (s *visibilityStore) ListOpenWorkflowExecutions(
   155  	ctx context.Context,
   156  	request *manager.ListWorkflowExecutionsRequest,
   157  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   158  	return s.listWorkflowExecutions(
   159  		"ListOpenWorkflowExecutions",
   160  		request.NextPageToken,
   161  		request.PageSize,
   162  		request.LatestStartTime,
   163  		false,
   164  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   165  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   166  				NamespaceID: request.NamespaceID.String(),
   167  				MinTime:     &request.EarliestStartTime,
   168  				MaxTime:     &readLevel.Time,
   169  				RunID:       &readLevel.RunID,
   170  				PageSize:    &request.PageSize,
   171  				Status:      int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   172  			})
   173  		})
   174  }
   175  
   176  func (s *visibilityStore) ListClosedWorkflowExecutions(
   177  	ctx context.Context,
   178  	request *manager.ListWorkflowExecutionsRequest,
   179  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   180  	return s.listWorkflowExecutions("ListClosedWorkflowExecutions",
   181  		request.NextPageToken,
   182  		request.PageSize,
   183  		request.LatestStartTime,
   184  		true,
   185  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   186  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   187  				NamespaceID: request.NamespaceID.String(),
   188  				MinTime:     &request.EarliestStartTime,
   189  				MaxTime:     &readLevel.Time,
   190  				RunID:       &readLevel.RunID,
   191  				PageSize:    &request.PageSize,
   192  			})
   193  		})
   194  }
   195  
   196  func (s *visibilityStore) ListOpenWorkflowExecutionsByType(
   197  	ctx context.Context,
   198  	request *manager.ListWorkflowExecutionsByTypeRequest,
   199  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   200  	return s.listWorkflowExecutions("ListOpenWorkflowExecutionsByType",
   201  		request.NextPageToken,
   202  		request.PageSize,
   203  		request.LatestStartTime,
   204  		false,
   205  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   206  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   207  				NamespaceID:      request.NamespaceID.String(),
   208  				MinTime:          &request.EarliestStartTime,
   209  				MaxTime:          &readLevel.Time,
   210  				RunID:            &readLevel.RunID,
   211  				WorkflowTypeName: &request.WorkflowTypeName,
   212  				PageSize:         &request.PageSize,
   213  				Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   214  			})
   215  		})
   216  }
   217  
   218  func (s *visibilityStore) ListClosedWorkflowExecutionsByType(
   219  	ctx context.Context,
   220  	request *manager.ListWorkflowExecutionsByTypeRequest,
   221  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   222  	return s.listWorkflowExecutions("ListClosedWorkflowExecutionsByType",
   223  		request.NextPageToken,
   224  		request.PageSize,
   225  		request.LatestStartTime,
   226  		true,
   227  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   228  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   229  				NamespaceID:      request.NamespaceID.String(),
   230  				MinTime:          &request.EarliestStartTime,
   231  				MaxTime:          &readLevel.Time,
   232  				RunID:            &readLevel.RunID,
   233  				WorkflowTypeName: &request.WorkflowTypeName,
   234  				PageSize:         &request.PageSize,
   235  			})
   236  		})
   237  }
   238  
   239  func (s *visibilityStore) ListOpenWorkflowExecutionsByWorkflowID(
   240  	ctx context.Context,
   241  	request *manager.ListWorkflowExecutionsByWorkflowIDRequest,
   242  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   243  	return s.listWorkflowExecutions("ListOpenWorkflowExecutionsByWorkflowID",
   244  		request.NextPageToken,
   245  		request.PageSize,
   246  		request.LatestStartTime,
   247  		false,
   248  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   249  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   250  				NamespaceID: request.NamespaceID.String(),
   251  				MinTime:     &request.EarliestStartTime,
   252  				MaxTime:     &readLevel.Time,
   253  				RunID:       &readLevel.RunID,
   254  				WorkflowID:  &request.WorkflowID,
   255  				PageSize:    &request.PageSize,
   256  				Status:      int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   257  			})
   258  		})
   259  }
   260  
   261  func (s *visibilityStore) ListClosedWorkflowExecutionsByWorkflowID(
   262  	ctx context.Context,
   263  	request *manager.ListWorkflowExecutionsByWorkflowIDRequest,
   264  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   265  	return s.listWorkflowExecutions("ListClosedWorkflowExecutionsByWorkflowID",
   266  		request.NextPageToken,
   267  		request.PageSize,
   268  		request.LatestStartTime,
   269  		true,
   270  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   271  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   272  				NamespaceID: request.NamespaceID.String(),
   273  				MinTime:     &request.EarliestStartTime,
   274  				MaxTime:     &readLevel.Time,
   275  				RunID:       &readLevel.RunID,
   276  				WorkflowID:  &request.WorkflowID,
   277  				PageSize:    &request.PageSize,
   278  			})
   279  		})
   280  }
   281  
   282  func (s *visibilityStore) ListClosedWorkflowExecutionsByStatus(
   283  	ctx context.Context,
   284  	request *manager.ListClosedWorkflowExecutionsByStatusRequest,
   285  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   286  	return s.listWorkflowExecutions("ListClosedWorkflowExecutionsByStatus",
   287  		request.NextPageToken,
   288  		request.PageSize,
   289  		request.LatestStartTime,
   290  		true,
   291  		func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error) {
   292  			return s.sqlStore.Db.SelectFromVisibility(ctx, sqlplugin.VisibilitySelectFilter{
   293  				NamespaceID: request.NamespaceID.String(),
   294  				MinTime:     &request.EarliestStartTime,
   295  				MaxTime:     &readLevel.Time,
   296  				RunID:       &readLevel.RunID,
   297  				Status:      int32(request.Status),
   298  				PageSize:    &request.PageSize,
   299  			})
   300  		})
   301  }
   302  
   303  func (s *visibilityStore) DeleteWorkflowExecution(
   304  	ctx context.Context,
   305  	request *manager.VisibilityDeleteWorkflowExecutionRequest,
   306  ) error {
   307  	_, err := s.sqlStore.Db.DeleteFromVisibility(ctx, sqlplugin.VisibilityDeleteFilter{
   308  		NamespaceID: request.NamespaceID.String(),
   309  		RunID:       request.RunID,
   310  	})
   311  	if err != nil {
   312  		return serviceerror.NewUnavailable(err.Error())
   313  	}
   314  	return nil
   315  }
   316  
   317  func (s *visibilityStore) ListWorkflowExecutions(
   318  	_ context.Context,
   319  	_ *manager.ListWorkflowExecutionsRequestV2,
   320  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   321  	return nil, store.OperationNotSupportedErr
   322  }
   323  
   324  func (s *visibilityStore) ScanWorkflowExecutions(
   325  	_ context.Context,
   326  	_ *manager.ListWorkflowExecutionsRequestV2,
   327  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   328  	return nil, store.OperationNotSupportedErr
   329  }
   330  
   331  func (s *visibilityStore) CountWorkflowExecutions(
   332  	_ context.Context,
   333  	_ *manager.CountWorkflowExecutionsRequest,
   334  ) (*manager.CountWorkflowExecutionsResponse, error) {
   335  	return nil, store.OperationNotSupportedErr
   336  }
   337  
   338  func (s *visibilityStore) GetWorkflowExecution(
   339  	ctx context.Context,
   340  	request *manager.GetWorkflowExecutionRequest,
   341  ) (*store.InternalGetWorkflowExecutionResponse, error) {
   342  	row, err := s.sqlStore.Db.GetFromVisibility(ctx, sqlplugin.VisibilityGetFilter{
   343  		NamespaceID: request.NamespaceID.String(),
   344  		RunID:       request.RunID,
   345  	})
   346  	if err != nil {
   347  		return nil, serviceerror.NewUnavailable(
   348  			fmt.Sprintf("GetWorkflowExecution operation failed. Select failed: %v", err))
   349  	}
   350  	return &store.InternalGetWorkflowExecutionResponse{
   351  		Execution: s.rowToInfo(row),
   352  	}, nil
   353  }
   354  
   355  func (s *visibilityStore) rowToInfo(
   356  	row *sqlplugin.VisibilityRow,
   357  ) *store.InternalWorkflowExecutionInfo {
   358  	if row.ExecutionTime.UnixNano() == 0 {
   359  		row.ExecutionTime = row.StartTime
   360  	}
   361  	info := &store.InternalWorkflowExecutionInfo{
   362  		WorkflowID:    row.WorkflowID,
   363  		RunID:         row.RunID,
   364  		TypeName:      row.WorkflowTypeName,
   365  		StartTime:     row.StartTime,
   366  		ExecutionTime: row.ExecutionTime,
   367  		Memo:          persistence.NewDataBlob(row.Memo, row.Encoding),
   368  		Status:        enumspb.WorkflowExecutionStatus(row.Status),
   369  		TaskQueue:     row.TaskQueue,
   370  	}
   371  	if row.CloseTime != nil {
   372  		info.CloseTime = *row.CloseTime
   373  		info.HistoryLength = *row.HistoryLength
   374  	}
   375  	if row.HistoryLength != nil {
   376  		info.HistoryLength = *row.HistoryLength
   377  	}
   378  	return info
   379  }
   380  
   381  func (s *visibilityStore) listWorkflowExecutions(
   382  	opName string,
   383  	pageToken []byte,
   384  	pageSize int,
   385  	latestTime time.Time,
   386  	closeQuery bool,
   387  	selectOp func(readLevel *visibilityPageToken) ([]sqlplugin.VisibilityRow, error),
   388  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   389  	var readLevel *visibilityPageToken
   390  	var err error
   391  	if len(pageToken) > 0 {
   392  		readLevel, err = s.deserializePageToken(pageToken)
   393  		if err != nil {
   394  			return nil, err
   395  		}
   396  	} else {
   397  		readLevel = &visibilityPageToken{Time: latestTime, RunID: ""}
   398  	}
   399  	rows, err := selectOp(readLevel)
   400  	if err != nil {
   401  		return nil, serviceerror.NewUnavailable(fmt.Sprintf("%v operation failed. Select failed: %v", opName, err))
   402  	}
   403  	if len(rows) == 0 {
   404  		return &store.InternalListWorkflowExecutionsResponse{}, nil
   405  	}
   406  
   407  	var infos = make([]*store.InternalWorkflowExecutionInfo, len(rows))
   408  	for i, row := range rows {
   409  		infos[i] = s.rowToInfo(&row)
   410  	}
   411  
   412  	var nextPageToken []byte
   413  	if len(rows) == pageSize {
   414  		lastRow := rows[len(rows)-1]
   415  		lastTime := lastRow.StartTime
   416  		if closeQuery {
   417  			lastTime = *lastRow.CloseTime
   418  		}
   419  		nextPageToken, err = s.serializePageToken(&visibilityPageToken{
   420  			Time:  lastTime,
   421  			RunID: lastRow.RunID,
   422  		})
   423  		if err != nil {
   424  			return nil, err
   425  		}
   426  	}
   427  	return &store.InternalListWorkflowExecutionsResponse{
   428  		Executions:    infos,
   429  		NextPageToken: nextPageToken,
   430  	}, nil
   431  }
   432  
   433  func (s *visibilityStore) deserializePageToken(
   434  	data []byte,
   435  ) (*visibilityPageToken, error) {
   436  	var token visibilityPageToken
   437  	err := json.Unmarshal(data, &token)
   438  	return &token, err
   439  }
   440  
   441  func (s *visibilityStore) serializePageToken(
   442  	token *visibilityPageToken,
   443  ) ([]byte, error) {
   444  	data, err := json.Marshal(token)
   445  	return data, err
   446  }