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 }