go.temporal.io/server@v1.23.0/common/persistence/visibility/store/standard/cassandra/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 cassandra
    26  
    27  import (
    28  	"context"
    29  	"time"
    30  
    31  	"github.com/gocql/gocql"
    32  	enumspb "go.temporal.io/api/enums/v1"
    33  
    34  	"go.temporal.io/server/common/config"
    35  	"go.temporal.io/server/common/log"
    36  	"go.temporal.io/server/common/log/tag"
    37  	"go.temporal.io/server/common/metrics"
    38  	"go.temporal.io/server/common/persistence"
    39  	commongocql "go.temporal.io/server/common/persistence/nosql/nosqlplugin/cassandra/gocql"
    40  	"go.temporal.io/server/common/persistence/visibility/manager"
    41  	"go.temporal.io/server/common/persistence/visibility/store"
    42  	"go.temporal.io/server/common/resolver"
    43  )
    44  
    45  // Fixed namespace values for now
    46  const (
    47  	namespacePartition       = 0
    48  	CassandraPersistenceName = "cassandra"
    49  )
    50  
    51  const (
    52  	templateCreateWorkflowExecutionStarted = `INSERT INTO open_executions (` +
    53  		`namespace_id, namespace_partition, workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_queue) ` +
    54  		`VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
    55  
    56  	templateDeleteWorkflowExecutionStarted = `DELETE FROM open_executions ` +
    57  		`WHERE namespace_id = ? ` +
    58  		`AND namespace_partition = ? ` +
    59  		`AND start_time = ? ` +
    60  		`AND run_id = ?`
    61  
    62  	templateCreateWorkflowExecutionClosed = `INSERT INTO closed_executions (` +
    63  		`namespace_id, namespace_partition, workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_queue) ` +
    64  		`VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
    65  
    66  	templateDeleteWorkflowExecutionClosed = `DELETE FROM closed_executions ` +
    67  		`WHERE namespace_id = ? ` +
    68  		`AND namespace_partition = ? ` +
    69  		`AND close_time = ? ` +
    70  		`AND run_id = ?`
    71  
    72  	templateGetOpenWorkflowExecutions = `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_queue ` +
    73  		`FROM open_executions ` +
    74  		`WHERE namespace_id = ? ` +
    75  		`AND namespace_partition = ? ` +
    76  		`AND start_time >= ? ` +
    77  		`AND start_time <= ? ORDER BY start_time desc`
    78  
    79  	templateGetOpenWorkflowExecutionsByType = `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_queue ` +
    80  		`FROM open_executions ` +
    81  		`WHERE namespace_id = ? ` +
    82  		`AND namespace_partition = ? ` +
    83  		`AND start_time >= ? ` +
    84  		`AND start_time <= ? ` +
    85  		`AND workflow_type_name = ? `
    86  
    87  	templateGetOpenWorkflowExecutionsByID = `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_queue ` +
    88  		`FROM open_executions ` +
    89  		`WHERE namespace_id = ? ` +
    90  		`AND namespace_partition = ? ` +
    91  		`AND start_time >= ? ` +
    92  		`AND start_time <= ? ` +
    93  		`AND workflow_id = ? `
    94  
    95  	templateGetOpenWorkflowExecutionByRunID = `SELECT workflow_id, run_id, start_time, execution_time, workflow_type_name, memo, encoding, task_queue ` +
    96  		`FROM open_executions ` +
    97  		`WHERE namespace_id = ? ` +
    98  		`AND namespace_partition = ? ` +
    99  		`AND start_time = ? ` +
   100  		`AND run_id = ? `
   101  
   102  	templateGetClosedWorkflowExecutions = `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_queue ` +
   103  		`FROM closed_executions ` +
   104  		`WHERE namespace_id = ? ` +
   105  		`AND namespace_partition = ? ` +
   106  		`AND close_time >= ? ` +
   107  		`AND close_time <= ? ORDER BY close_time desc`
   108  
   109  	templateGetClosedWorkflowExecutionsByType = `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_queue ` +
   110  		`FROM closed_executions ` +
   111  		`WHERE namespace_id = ? ` +
   112  		`AND namespace_partition = ? ` +
   113  		`AND close_time >= ? ` +
   114  		`AND close_time <= ? ` +
   115  		`AND workflow_type_name = ? `
   116  
   117  	templateGetClosedWorkflowExecutionsByID = `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_queue ` +
   118  		`FROM closed_executions ` +
   119  		`WHERE namespace_id = ? ` +
   120  		`AND namespace_partition = ? ` +
   121  		`AND close_time >= ? ` +
   122  		`AND close_time <= ? ` +
   123  		`AND workflow_id = ? `
   124  
   125  	templateGetClosedWorkflowExecutionByRunID = `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_queue ` +
   126  		`FROM closed_executions ` +
   127  		`WHERE namespace_id = ? ` +
   128  		`AND namespace_partition = ? ` +
   129  		`AND close_time = ? ` +
   130  		`AND run_id = ? `
   131  
   132  	templateGetClosedWorkflowExecutionsByStatus = `SELECT workflow_id, run_id, start_time, execution_time, close_time, workflow_type_name, status, history_length, memo, encoding, task_queue ` +
   133  		`FROM closed_executions ` +
   134  		`WHERE namespace_id = ? ` +
   135  		`AND namespace_partition = ? ` +
   136  		`AND close_time >= ? ` +
   137  		`AND close_time <= ? ` +
   138  		`AND status = ? `
   139  )
   140  
   141  type (
   142  	visibilityStore struct {
   143  		session      commongocql.Session
   144  		lowConslevel commongocql.Consistency
   145  	}
   146  )
   147  
   148  var _ store.VisibilityStore = (*visibilityStore)(nil)
   149  
   150  func NewVisibilityStore(
   151  	cfg config.Cassandra,
   152  	r resolver.ServiceResolver,
   153  	logger log.Logger,
   154  	metricsHandler metrics.Handler,
   155  ) (*visibilityStore, error) {
   156  	session, err := commongocql.NewSession(
   157  		func() (*gocql.ClusterConfig, error) {
   158  			return commongocql.NewCassandraCluster(cfg, r)
   159  		},
   160  		logger,
   161  		metricsHandler,
   162  	)
   163  	if err != nil {
   164  		logger.Fatal("unable to initialize cassandra session", tag.Error(err))
   165  	}
   166  
   167  	return &visibilityStore{
   168  		session:      session,
   169  		lowConslevel: commongocql.One,
   170  	}, nil
   171  }
   172  
   173  func (v *visibilityStore) GetName() string {
   174  	return CassandraPersistenceName
   175  }
   176  
   177  func (v *visibilityStore) GetIndexName() string {
   178  	// GetIndexName is used to get cluster metadata, which in verstions < v1.20
   179  	// were stored in an empty string key.
   180  	return ""
   181  }
   182  
   183  func (v *visibilityStore) ValidateCustomSearchAttributes(
   184  	searchAttributes map[string]any,
   185  ) (map[string]any, error) {
   186  	return searchAttributes, nil
   187  }
   188  
   189  // Close releases the resources held by this object
   190  func (v *visibilityStore) Close() {
   191  	v.session.Close()
   192  }
   193  
   194  func (v *visibilityStore) RecordWorkflowExecutionStarted(
   195  	ctx context.Context,
   196  	request *store.InternalRecordWorkflowExecutionStartedRequest,
   197  ) error {
   198  
   199  	query := v.session.Query(templateCreateWorkflowExecutionStarted,
   200  		request.NamespaceID,
   201  		namespacePartition,
   202  		request.WorkflowID,
   203  		request.RunID,
   204  		persistence.UnixMilliseconds(request.StartTime),
   205  		persistence.UnixMilliseconds(request.ExecutionTime),
   206  		request.WorkflowTypeName,
   207  		request.Memo.Data,
   208  		request.Memo.EncodingType.String(),
   209  		request.TaskQueue,
   210  	).WithContext(ctx)
   211  	// It is important to specify timestamp for all `open_executions` queries because
   212  	// we are using milliseconds instead of default microseconds. If custom timestamp collides with
   213  	// default timestamp, default one will always win because they are 1000 times bigger.
   214  	query = query.WithTimestamp(persistence.UnixMilliseconds(request.StartTime))
   215  	err := query.Exec()
   216  	return commongocql.ConvertError("RecordWorkflowExecutionStarted", err)
   217  }
   218  
   219  func (v *visibilityStore) RecordWorkflowExecutionClosed(
   220  	ctx context.Context,
   221  	request *store.InternalRecordWorkflowExecutionClosedRequest,
   222  ) error {
   223  	batch := v.session.NewBatch(commongocql.LoggedBatch).WithContext(ctx)
   224  
   225  	// First, remove execution from the open table
   226  	batch.Query(templateDeleteWorkflowExecutionStarted,
   227  		request.NamespaceID,
   228  		namespacePartition,
   229  		persistence.UnixMilliseconds(request.StartTime),
   230  		request.RunID,
   231  	)
   232  
   233  	// Next, add a row in the closed table.
   234  	batch.Query(templateCreateWorkflowExecutionClosed,
   235  		request.NamespaceID,
   236  		namespacePartition,
   237  		request.WorkflowID,
   238  		request.RunID,
   239  		persistence.UnixMilliseconds(request.StartTime),
   240  		persistence.UnixMilliseconds(request.ExecutionTime),
   241  		persistence.UnixMilliseconds(request.CloseTime),
   242  		request.WorkflowTypeName,
   243  		request.Status,
   244  		request.HistoryLength,
   245  		request.Memo.Data,
   246  		request.Memo.EncodingType.String(),
   247  		request.TaskQueue,
   248  	)
   249  
   250  	// RecordWorkflowExecutionStarted is using StartTime as the timestamp for every query in `open_executions` table.
   251  	// Due to the fact that cross DC using mutable state creation time as workflow start time and visibility using event time
   252  	// instead of last update time (https://github.com/uber/cadence/pull/1501) CloseTime can be before StartTime (or very close it).
   253  	// In this case, use (StartTime + minWorkflowDuration) for delete operation to guarantee that it is greater than StartTime
   254  	// and won't be ignored.
   255  
   256  	const minWorkflowDuration = time.Second
   257  	var batchTimestamp time.Time
   258  	if request.CloseTime.Sub(request.StartTime) < minWorkflowDuration {
   259  		batchTimestamp = request.StartTime.Add(minWorkflowDuration)
   260  	} else {
   261  		batchTimestamp = request.CloseTime
   262  	}
   263  
   264  	batch = batch.WithTimestamp(persistence.UnixMilliseconds(batchTimestamp))
   265  	err := v.session.ExecuteBatch(batch)
   266  	return commongocql.ConvertError("RecordWorkflowExecutionClosed", err)
   267  }
   268  
   269  func (v *visibilityStore) UpsertWorkflowExecution(
   270  	_ context.Context,
   271  	_ *store.InternalUpsertWorkflowExecutionRequest,
   272  ) error {
   273  	// Not OperationNotSupportedErr!
   274  	return nil
   275  }
   276  
   277  func (v *visibilityStore) ListOpenWorkflowExecutions(
   278  	ctx context.Context,
   279  	request *manager.ListWorkflowExecutionsRequest,
   280  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   281  	query := v.session.Query(
   282  		templateGetOpenWorkflowExecutions,
   283  		request.NamespaceID.String(),
   284  		namespacePartition,
   285  		persistence.UnixMilliseconds(request.EarliestStartTime),
   286  		persistence.UnixMilliseconds(request.LatestStartTime),
   287  	).Consistency(v.lowConslevel).WithContext(ctx)
   288  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   289  
   290  	response := &store.InternalListWorkflowExecutionsResponse{}
   291  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   292  	wfexecution, has := readOpenWorkflowExecutionRecord(iter)
   293  	for has {
   294  		response.Executions = append(response.Executions, wfexecution)
   295  		wfexecution, has = readOpenWorkflowExecutionRecord(iter)
   296  	}
   297  
   298  	if len(iter.PageState()) > 0 {
   299  		response.NextPageToken = iter.PageState()
   300  	}
   301  	if err := iter.Close(); err != nil {
   302  		return nil, commongocql.ConvertError("ListOpenWorkflowExecutions", err)
   303  	}
   304  	return response, nil
   305  }
   306  
   307  func (v *visibilityStore) ListOpenWorkflowExecutionsByType(
   308  	ctx context.Context,
   309  	request *manager.ListWorkflowExecutionsByTypeRequest,
   310  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   311  	query := v.session.Query(
   312  		templateGetOpenWorkflowExecutionsByType,
   313  		request.NamespaceID.String(),
   314  		namespacePartition,
   315  		persistence.UnixMilliseconds(request.EarliestStartTime),
   316  		persistence.UnixMilliseconds(request.LatestStartTime),
   317  		request.WorkflowTypeName,
   318  	).Consistency(v.lowConslevel).WithContext(ctx)
   319  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   320  
   321  	response := &store.InternalListWorkflowExecutionsResponse{}
   322  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   323  	wfexecution, has := readOpenWorkflowExecutionRecord(iter)
   324  	for has {
   325  		response.Executions = append(response.Executions, wfexecution)
   326  		wfexecution, has = readOpenWorkflowExecutionRecord(iter)
   327  	}
   328  
   329  	if len(iter.PageState()) > 0 {
   330  		response.NextPageToken = iter.PageState()
   331  	}
   332  	if err := iter.Close(); err != nil {
   333  		return nil, commongocql.ConvertError("ListOpenWorkflowExecutionsByType", err)
   334  	}
   335  	return response, nil
   336  }
   337  
   338  func (v *visibilityStore) ListOpenWorkflowExecutionsByWorkflowID(
   339  	ctx context.Context,
   340  	request *manager.ListWorkflowExecutionsByWorkflowIDRequest,
   341  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   342  	query := v.session.Query(
   343  		templateGetOpenWorkflowExecutionsByID,
   344  		request.NamespaceID.String(),
   345  		namespacePartition,
   346  		persistence.UnixMilliseconds(request.EarliestStartTime),
   347  		persistence.UnixMilliseconds(request.LatestStartTime),
   348  		request.WorkflowID,
   349  	).Consistency(v.lowConslevel).WithContext(ctx)
   350  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   351  
   352  	response := &store.InternalListWorkflowExecutionsResponse{}
   353  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   354  	wfexecution, has := readOpenWorkflowExecutionRecord(iter)
   355  	for has {
   356  		response.Executions = append(response.Executions, wfexecution)
   357  		wfexecution, has = readOpenWorkflowExecutionRecord(iter)
   358  	}
   359  
   360  	if len(iter.PageState()) > 0 {
   361  		response.NextPageToken = iter.PageState()
   362  	}
   363  	if err := iter.Close(); err != nil {
   364  		return nil, commongocql.ConvertError("ListOpenWorkflowExecutionsByWorkflowID", err)
   365  	}
   366  	return response, nil
   367  }
   368  
   369  func (v *visibilityStore) ListClosedWorkflowExecutions(
   370  	ctx context.Context,
   371  	request *manager.ListWorkflowExecutionsRequest,
   372  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   373  	query := v.session.Query(templateGetClosedWorkflowExecutions,
   374  		request.NamespaceID.String(),
   375  		namespacePartition,
   376  		persistence.UnixMilliseconds(request.EarliestStartTime),
   377  		persistence.UnixMilliseconds(request.LatestStartTime),
   378  	).Consistency(v.lowConslevel).WithContext(ctx)
   379  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   380  
   381  	response := &store.InternalListWorkflowExecutionsResponse{}
   382  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   383  	wfexecution, has := readClosedWorkflowExecutionRecord(iter)
   384  	for has {
   385  		response.Executions = append(response.Executions, wfexecution)
   386  		wfexecution, has = readClosedWorkflowExecutionRecord(iter)
   387  	}
   388  
   389  	if len(iter.PageState()) > 0 {
   390  		response.NextPageToken = iter.PageState()
   391  	}
   392  	if err := iter.Close(); err != nil {
   393  		return nil, commongocql.ConvertError("ListClosedWorkflowExecutions", err)
   394  	}
   395  	return response, nil
   396  }
   397  
   398  func (v *visibilityStore) ListClosedWorkflowExecutionsByType(
   399  	ctx context.Context,
   400  	request *manager.ListWorkflowExecutionsByTypeRequest,
   401  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   402  	query := v.session.Query(
   403  		templateGetClosedWorkflowExecutionsByType,
   404  		request.NamespaceID.String(),
   405  		namespacePartition,
   406  		persistence.UnixMilliseconds(request.EarliestStartTime),
   407  		persistence.UnixMilliseconds(request.LatestStartTime),
   408  		request.WorkflowTypeName,
   409  	).Consistency(v.lowConslevel).WithContext(ctx)
   410  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   411  
   412  	response := &store.InternalListWorkflowExecutionsResponse{}
   413  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   414  	wfexecution, has := readClosedWorkflowExecutionRecord(iter)
   415  	for has {
   416  		response.Executions = append(response.Executions, wfexecution)
   417  		wfexecution, has = readClosedWorkflowExecutionRecord(iter)
   418  	}
   419  
   420  	if len(iter.PageState()) > 0 {
   421  		response.NextPageToken = iter.PageState()
   422  	}
   423  	if err := iter.Close(); err != nil {
   424  		return nil, commongocql.ConvertError("ListClosedWorkflowExecutionsByType", err)
   425  	}
   426  	return response, nil
   427  }
   428  
   429  func (v *visibilityStore) ListClosedWorkflowExecutionsByWorkflowID(
   430  	ctx context.Context,
   431  	request *manager.ListWorkflowExecutionsByWorkflowIDRequest,
   432  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   433  	query := v.session.Query(templateGetClosedWorkflowExecutionsByID,
   434  		request.NamespaceID.String(),
   435  		namespacePartition,
   436  		persistence.UnixMilliseconds(request.EarliestStartTime),
   437  		persistence.UnixMilliseconds(request.LatestStartTime),
   438  		request.WorkflowID,
   439  	).Consistency(v.lowConslevel).WithContext(ctx)
   440  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   441  
   442  	response := &store.InternalListWorkflowExecutionsResponse{}
   443  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   444  	wfexecution, has := readClosedWorkflowExecutionRecord(iter)
   445  	for has {
   446  		response.Executions = append(response.Executions, wfexecution)
   447  		wfexecution, has = readClosedWorkflowExecutionRecord(iter)
   448  	}
   449  
   450  	if len(iter.PageState()) > 0 {
   451  		response.NextPageToken = iter.PageState()
   452  	}
   453  	if err := iter.Close(); err != nil {
   454  		return nil, commongocql.ConvertError("ListClosedWorkflowExecutionsByWorkflowID", err)
   455  	}
   456  	return response, nil
   457  }
   458  
   459  func (v *visibilityStore) ListClosedWorkflowExecutionsByStatus(
   460  	ctx context.Context,
   461  	request *manager.ListClosedWorkflowExecutionsByStatusRequest,
   462  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   463  	query := v.session.Query(templateGetClosedWorkflowExecutionsByStatus,
   464  		request.NamespaceID.String(),
   465  		namespacePartition,
   466  		persistence.UnixMilliseconds(request.EarliestStartTime),
   467  		persistence.UnixMilliseconds(request.LatestStartTime),
   468  		request.Status,
   469  	).Consistency(v.lowConslevel).WithContext(ctx)
   470  	iter := query.PageSize(request.PageSize).PageState(request.NextPageToken).Iter()
   471  
   472  	response := &store.InternalListWorkflowExecutionsResponse{}
   473  	response.Executions = make([]*store.InternalWorkflowExecutionInfo, 0, request.PageSize)
   474  	wfexecution, has := readClosedWorkflowExecutionRecord(iter)
   475  	for has {
   476  		response.Executions = append(response.Executions, wfexecution)
   477  		wfexecution, has = readClosedWorkflowExecutionRecord(iter)
   478  	}
   479  
   480  	if len(iter.PageState()) > 0 {
   481  		response.NextPageToken = iter.PageState()
   482  	}
   483  	if err := iter.Close(); err != nil {
   484  		return nil, commongocql.ConvertError("ListClosedWorkflowExecutionsByStatus", err)
   485  	}
   486  	return response, nil
   487  }
   488  
   489  func (v *visibilityStore) DeleteWorkflowExecution(
   490  	ctx context.Context,
   491  	request *manager.VisibilityDeleteWorkflowExecutionRequest,
   492  ) error {
   493  	var query commongocql.Query
   494  	if !request.StartTime.IsZero() {
   495  		query = v.session.Query(templateDeleteWorkflowExecutionStarted,
   496  			request.NamespaceID.String(),
   497  			namespacePartition,
   498  			persistence.UnixMilliseconds(request.StartTime),
   499  			request.RunID,
   500  		).WithContext(ctx)
   501  	} else if !request.CloseTime.IsZero() {
   502  		query = v.session.Query(templateDeleteWorkflowExecutionClosed,
   503  			request.NamespaceID.String(),
   504  			namespacePartition,
   505  			persistence.UnixMilliseconds(request.CloseTime),
   506  			request.RunID,
   507  		).WithContext(ctx)
   508  	} else {
   509  		panic("Cassandra visibility store: DeleteWorkflowExecution: both StartTime and CloseTime are nil")
   510  	}
   511  
   512  	if err := query.Exec(); err != nil {
   513  		return commongocql.ConvertError("DeleteWorkflowExecution", err)
   514  	}
   515  	return nil
   516  }
   517  
   518  func (v *visibilityStore) ListWorkflowExecutions(
   519  	_ context.Context,
   520  	_ *manager.ListWorkflowExecutionsRequestV2,
   521  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   522  	return nil, store.OperationNotSupportedErr
   523  }
   524  
   525  func (v *visibilityStore) ScanWorkflowExecutions(
   526  	_ context.Context,
   527  	_ *manager.ListWorkflowExecutionsRequestV2,
   528  ) (*store.InternalListWorkflowExecutionsResponse, error) {
   529  	return nil, store.OperationNotSupportedErr
   530  }
   531  
   532  func (v *visibilityStore) CountWorkflowExecutions(
   533  	_ context.Context,
   534  	_ *manager.CountWorkflowExecutionsRequest,
   535  ) (*manager.CountWorkflowExecutionsResponse, error) {
   536  	return nil, store.OperationNotSupportedErr
   537  }
   538  
   539  func (v *visibilityStore) GetWorkflowExecution(
   540  	ctx context.Context,
   541  	request *manager.GetWorkflowExecutionRequest,
   542  ) (*store.InternalGetWorkflowExecutionResponse, error) {
   543  	if request.StartTime.IsZero() && request.CloseTime.IsZero() {
   544  		return nil, store.OperationNotSupportedErr
   545  	}
   546  	var wfexecution *store.InternalWorkflowExecutionInfo
   547  	var err error
   548  	if !request.CloseTime.IsZero() {
   549  		wfexecution, err = v.getClosedWorkflowExecution(ctx, request)
   550  	} else {
   551  		wfexecution, err = v.getOpenWorkflowExecution(ctx, request)
   552  	}
   553  	if err != nil {
   554  		return nil, err
   555  	}
   556  	return &store.InternalGetWorkflowExecutionResponse{
   557  		Execution: wfexecution,
   558  	}, nil
   559  }
   560  
   561  func (v *visibilityStore) getOpenWorkflowExecution(
   562  	ctx context.Context,
   563  	request *manager.GetWorkflowExecutionRequest,
   564  ) (*store.InternalWorkflowExecutionInfo, error) {
   565  	if request.StartTime.IsZero() {
   566  		return nil, store.OperationNotSupportedErr
   567  	}
   568  	query := v.session.Query(
   569  		templateGetOpenWorkflowExecutionByRunID,
   570  		request.NamespaceID.String(),
   571  		namespacePartition,
   572  		persistence.UnixMilliseconds(request.StartTime),
   573  		request.RunID,
   574  	).Consistency(v.lowConslevel).WithContext(ctx)
   575  	iter := query.PageSize(1).Iter()
   576  	wfexecution, _ := readOpenWorkflowExecutionRecord(iter)
   577  	return wfexecution, nil
   578  }
   579  
   580  func (v *visibilityStore) getClosedWorkflowExecution(
   581  	ctx context.Context,
   582  	request *manager.GetWorkflowExecutionRequest,
   583  ) (*store.InternalWorkflowExecutionInfo, error) {
   584  	if request.CloseTime.IsZero() {
   585  		return nil, store.OperationNotSupportedErr
   586  	}
   587  	query := v.session.Query(
   588  		templateGetClosedWorkflowExecutionByRunID,
   589  		request.NamespaceID.String(),
   590  		namespacePartition,
   591  		persistence.UnixMilliseconds(request.CloseTime),
   592  		request.RunID,
   593  	).Consistency(v.lowConslevel).WithContext(ctx)
   594  	iter := query.PageSize(1).Iter()
   595  	wfexecution, _ := readClosedWorkflowExecutionRecord(iter)
   596  	return wfexecution, nil
   597  }
   598  
   599  func readOpenWorkflowExecutionRecord(iter commongocql.Iter) (*store.InternalWorkflowExecutionInfo, bool) {
   600  	var workflowID string
   601  	var runID string
   602  	var typeName string
   603  	var startTime time.Time
   604  	var executionTime time.Time
   605  	var memo []byte
   606  	var encoding string
   607  	var taskQueue string
   608  	if iter.Scan(&workflowID, &runID, &startTime, &executionTime, &typeName, &memo, &encoding, &taskQueue) {
   609  		record := &store.InternalWorkflowExecutionInfo{
   610  			WorkflowID:    workflowID,
   611  			RunID:         runID,
   612  			TypeName:      typeName,
   613  			StartTime:     startTime,
   614  			ExecutionTime: executionTime,
   615  			Memo:          persistence.NewDataBlob(memo, encoding),
   616  			TaskQueue:     taskQueue,
   617  			Status:        enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING,
   618  		}
   619  		return record, true
   620  	}
   621  	return nil, false
   622  }
   623  
   624  func readClosedWorkflowExecutionRecord(iter commongocql.Iter) (*store.InternalWorkflowExecutionInfo, bool) {
   625  	var workflowID string
   626  	var runID string
   627  	var typeName string
   628  	var startTime time.Time
   629  	var executionTime time.Time
   630  	var closeTime time.Time
   631  	var status enumspb.WorkflowExecutionStatus
   632  	var historyLength int64
   633  	var memo []byte
   634  	var encoding string
   635  	var taskQueue string
   636  	if iter.Scan(&workflowID, &runID, &startTime, &executionTime, &closeTime, &typeName, &status, &historyLength, &memo, &encoding, &taskQueue) {
   637  		record := &store.InternalWorkflowExecutionInfo{
   638  			WorkflowID:    workflowID,
   639  			RunID:         runID,
   640  			TypeName:      typeName,
   641  			StartTime:     startTime,
   642  			ExecutionTime: executionTime,
   643  			CloseTime:     closeTime,
   644  			Status:        status,
   645  			HistoryLength: historyLength,
   646  			Memo:          persistence.NewDataBlob(memo, encoding),
   647  			TaskQueue:     taskQueue,
   648  		}
   649  		return record, true
   650  	}
   651  	return nil, false
   652  }