go.temporal.io/server@v1.23.0/common/persistence/visibility/store/standard/visibility_store.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2021 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 standard 26 27 import ( 28 "context" 29 "encoding/json" 30 "fmt" 31 "reflect" 32 33 enumspb "go.temporal.io/api/enums/v1" 34 35 "go.temporal.io/server/common/persistence/visibility/manager" 36 "go.temporal.io/server/common/persistence/visibility/store" 37 "go.temporal.io/server/common/searchattribute" 38 ) 39 40 type ( 41 standardStore struct { 42 store store.VisibilityStore 43 searchAttributesProvider searchattribute.Provider 44 searchAttributesMapperProvider searchattribute.MapperProvider 45 } 46 47 // We wrap the token with a boolean to indicate if it is from list open workflows or list closed workflows, 48 // so we know where to continue from for the next call. 49 nextPageToken struct { 50 ForOpenWorkflows bool `json:"isOpen"` 51 Token []byte 52 } 53 54 listRequest interface { 55 OverrideToken(token []byte) 56 GetToken() []byte 57 OverridePageSize(pageSize int) 58 GetPageSize() int 59 } 60 ) 61 62 var _ store.VisibilityStore = (*standardStore)(nil) 63 var _ listRequest = (*manager.ListWorkflowExecutionsRequest)(nil) 64 65 func NewVisibilityStore( 66 visibilityStore store.VisibilityStore, 67 searchAttributesProvider searchattribute.Provider, 68 searchAttributesMapperProvider searchattribute.MapperProvider, 69 ) store.VisibilityStore { 70 return &standardStore{ 71 store: visibilityStore, 72 searchAttributesProvider: searchAttributesProvider, 73 searchAttributesMapperProvider: searchAttributesMapperProvider, 74 } 75 } 76 77 func (s *standardStore) Close() { 78 s.store.Close() 79 } 80 81 func (s *standardStore) GetName() string { 82 return s.store.GetName() 83 } 84 85 func (s *standardStore) GetIndexName() string { 86 // GetIndexName is used to get cluster metadata, which in verstions < v1.20 87 // were stored in an empty string key. 88 return "" 89 } 90 91 func (s *standardStore) ValidateCustomSearchAttributes( 92 searchAttributes map[string]any, 93 ) (map[string]any, error) { 94 return searchAttributes, nil 95 } 96 97 func (s *standardStore) RecordWorkflowExecutionStarted( 98 ctx context.Context, 99 request *store.InternalRecordWorkflowExecutionStartedRequest, 100 ) error { 101 return s.store.RecordWorkflowExecutionStarted(ctx, request) 102 } 103 104 func (s *standardStore) RecordWorkflowExecutionClosed( 105 ctx context.Context, 106 request *store.InternalRecordWorkflowExecutionClosedRequest, 107 ) error { 108 return s.store.RecordWorkflowExecutionClosed(ctx, request) 109 } 110 111 func (s *standardStore) UpsertWorkflowExecution( 112 ctx context.Context, 113 request *store.InternalUpsertWorkflowExecutionRequest, 114 ) error { 115 return s.store.UpsertWorkflowExecution(ctx, request) 116 } 117 118 func (s *standardStore) DeleteWorkflowExecution( 119 ctx context.Context, 120 request *manager.VisibilityDeleteWorkflowExecutionRequest, 121 ) error { 122 return s.store.DeleteWorkflowExecution(ctx, request) 123 } 124 125 func (s *standardStore) ListOpenWorkflowExecutions( 126 ctx context.Context, 127 request *manager.ListWorkflowExecutionsRequest, 128 ) (*store.InternalListWorkflowExecutionsResponse, error) { 129 return s.store.ListOpenWorkflowExecutions(ctx, request) 130 } 131 132 func (s *standardStore) ListClosedWorkflowExecutions( 133 ctx context.Context, 134 request *manager.ListWorkflowExecutionsRequest, 135 ) (*store.InternalListWorkflowExecutionsResponse, error) { 136 return s.store.ListClosedWorkflowExecutions(ctx, request) 137 } 138 139 func (s *standardStore) ListOpenWorkflowExecutionsByType( 140 ctx context.Context, 141 request *manager.ListWorkflowExecutionsByTypeRequest, 142 ) (*store.InternalListWorkflowExecutionsResponse, error) { 143 return s.store.ListOpenWorkflowExecutionsByType(ctx, request) 144 } 145 146 func (s *standardStore) ListClosedWorkflowExecutionsByType( 147 ctx context.Context, 148 request *manager.ListWorkflowExecutionsByTypeRequest, 149 ) (*store.InternalListWorkflowExecutionsResponse, error) { 150 return s.store.ListClosedWorkflowExecutionsByType(ctx, request) 151 } 152 153 func (s *standardStore) ListOpenWorkflowExecutionsByWorkflowID( 154 ctx context.Context, 155 request *manager.ListWorkflowExecutionsByWorkflowIDRequest, 156 ) (*store.InternalListWorkflowExecutionsResponse, error) { 157 return s.store.ListOpenWorkflowExecutionsByWorkflowID(ctx, request) 158 } 159 160 func (s *standardStore) ListClosedWorkflowExecutionsByWorkflowID( 161 ctx context.Context, 162 request *manager.ListWorkflowExecutionsByWorkflowIDRequest, 163 ) (*store.InternalListWorkflowExecutionsResponse, error) { 164 return s.store.ListClosedWorkflowExecutionsByWorkflowID(ctx, request) 165 } 166 167 func (s *standardStore) ListClosedWorkflowExecutionsByStatus( 168 ctx context.Context, 169 request *manager.ListClosedWorkflowExecutionsByStatusRequest, 170 ) (*store.InternalListWorkflowExecutionsResponse, error) { 171 return s.store.ListClosedWorkflowExecutionsByStatus(ctx, request) 172 } 173 174 func (s *standardStore) ScanWorkflowExecutions( 175 ctx context.Context, 176 request *manager.ListWorkflowExecutionsRequestV2, 177 ) (*store.InternalListWorkflowExecutionsResponse, error) { 178 return s.store.ScanWorkflowExecutions(ctx, request) 179 } 180 181 func (s *standardStore) CountWorkflowExecutions( 182 ctx context.Context, 183 request *manager.CountWorkflowExecutionsRequest, 184 ) (*manager.CountWorkflowExecutionsResponse, error) { 185 return s.store.CountWorkflowExecutions(ctx, request) 186 } 187 188 func (s *standardStore) GetWorkflowExecution( 189 ctx context.Context, 190 request *manager.GetWorkflowExecutionRequest, 191 ) (*store.InternalGetWorkflowExecutionResponse, error) { 192 return s.store.GetWorkflowExecution(ctx, request) 193 } 194 195 func (s *standardStore) ListWorkflowExecutions( 196 ctx context.Context, 197 request *manager.ListWorkflowExecutionsRequestV2, 198 ) (*store.InternalListWorkflowExecutionsResponse, error) { 199 typeMap, err := s.searchAttributesProvider.GetSearchAttributes(s.GetIndexName(), false) 200 if err != nil { 201 return nil, err 202 } 203 204 converter := newQueryConverter(request.Namespace, typeMap, s.searchAttributesMapperProvider) 205 filter, err := converter.GetFilter(request.Query) 206 if err != nil { 207 return nil, err 208 } 209 210 baseReq := &manager.ListWorkflowExecutionsRequest{ 211 NamespaceID: request.NamespaceID, 212 Namespace: request.Namespace, 213 PageSize: request.PageSize, 214 NextPageToken: request.NextPageToken, 215 EarliestStartTime: *filter.MinTime, 216 LatestStartTime: *filter.MaxTime, 217 } 218 219 // Only a limited query patterns are supported due to the way we set up 220 // visibility tables in Cassandra. 221 // Check validation logic in query interceptor for details. 222 if filter.WorkflowID != nil { 223 request := &manager.ListWorkflowExecutionsByWorkflowIDRequest{ 224 ListWorkflowExecutionsRequest: baseReq, 225 WorkflowID: *filter.WorkflowID, 226 } 227 return s.listWorkflowExecutionsHelper( 228 ctx, 229 request, 230 s.listOpenWorkflowExecutionsByWorkflowID, 231 s.listClosedWorkflowExecutionsByWorkflowID) 232 } else if filter.WorkflowTypeName != nil { 233 request := &manager.ListWorkflowExecutionsByTypeRequest{ 234 ListWorkflowExecutionsRequest: baseReq, 235 WorkflowTypeName: *filter.WorkflowTypeName, 236 } 237 return s.listWorkflowExecutionsHelper( 238 ctx, 239 request, 240 s.listOpenWorkflowExecutionsByType, 241 s.listClosedWorkflowExecutionsByType) 242 } else if filter.Status != int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED) { 243 if filter.Status == int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING) { 244 return s.ListOpenWorkflowExecutions(ctx, baseReq) 245 } else { 246 request := &manager.ListClosedWorkflowExecutionsByStatusRequest{ 247 ListWorkflowExecutionsRequest: baseReq, 248 Status: enumspb.WorkflowExecutionStatus(filter.Status), 249 } 250 return s.ListClosedWorkflowExecutionsByStatus(ctx, request) 251 } 252 } else { 253 return s.listWorkflowExecutionsHelper( 254 ctx, 255 baseReq, 256 s.listOpenWorkflowExecutions, 257 s.listClosedWorkflowExecutions) 258 } 259 } 260 261 func (s *standardStore) listWorkflowExecutionsHelper( 262 ctx context.Context, 263 request listRequest, 264 listOpenFunc func(ctx context.Context, request listRequest) (*store.InternalListWorkflowExecutionsResponse, error), 265 listCloseFunc func(ctx context.Context, request listRequest) (*store.InternalListWorkflowExecutionsResponse, error), 266 ) (*store.InternalListWorkflowExecutionsResponse, error) { 267 268 var token nextPageToken 269 if len(request.GetToken()) == 0 { 270 token = nextPageToken{ 271 ForOpenWorkflows: true, 272 } 273 } else { 274 err := json.Unmarshal(request.GetToken(), &token) 275 if err != nil { 276 return nil, fmt.Errorf("invalid next page token: %v", err) 277 } 278 request.OverrideToken(token.Token) 279 } 280 281 resp := &store.InternalListWorkflowExecutionsResponse{} 282 283 if token.ForOpenWorkflows { 284 listOpenResp, err := listOpenFunc(ctx, request) 285 if err != nil { 286 return nil, err 287 } 288 289 if len(listOpenResp.Executions) > 0 { 290 request.OverridePageSize(request.GetPageSize() - len(listOpenResp.Executions)) 291 resp.Executions = append(resp.Executions, listOpenResp.Executions...) 292 } 293 294 if request.GetPageSize() == 0 { 295 token.Token = listOpenResp.NextPageToken 296 297 token, err := json.Marshal(token) 298 if err != nil { 299 return nil, fmt.Errorf("failed to marshal next page token: %v", err) 300 } 301 302 resp.NextPageToken = token 303 return resp, nil 304 } else { 305 token.ForOpenWorkflows = false 306 request.OverrideToken(nil) 307 } 308 } 309 310 listCloseResp, err := listCloseFunc(ctx, request) 311 if err != nil { 312 return nil, err 313 } 314 resp.Executions = append(resp.Executions, listCloseResp.Executions...) 315 316 if listCloseResp.NextPageToken == nil { 317 resp.NextPageToken = nil 318 } else { 319 token.Token = listCloseResp.NextPageToken 320 token, err := json.Marshal(token) 321 if err != nil { 322 return nil, fmt.Errorf("failed to marshal next page token: %v", err) 323 } 324 325 resp.NextPageToken = token 326 } 327 328 return resp, nil 329 } 330 331 func (s *standardStore) listOpenWorkflowExecutionsByWorkflowID( 332 ctx context.Context, 333 request listRequest, 334 ) (*store.InternalListWorkflowExecutionsResponse, error) { 335 actualRequest, ok := request.(*manager.ListWorkflowExecutionsByWorkflowIDRequest) 336 if !ok { 337 panic(fmt.Errorf("wrong request type %v for listOpenWorkflowExecutionsByWorkflowID", reflect.TypeOf(request))) 338 } 339 340 return s.ListOpenWorkflowExecutionsByWorkflowID(ctx, actualRequest) 341 } 342 343 func (s *standardStore) listOpenWorkflowExecutionsByType( 344 ctx context.Context, 345 request listRequest, 346 ) (*store.InternalListWorkflowExecutionsResponse, error) { 347 actualRequest, ok := request.(*manager.ListWorkflowExecutionsByTypeRequest) 348 if !ok { 349 panic(fmt.Errorf("wrong request type %v for listOpenWorkflowExecutionsByType", reflect.TypeOf(request))) 350 } 351 352 return s.ListOpenWorkflowExecutionsByType(ctx, actualRequest) 353 } 354 355 func (s *standardStore) listOpenWorkflowExecutions( 356 ctx context.Context, 357 request listRequest, 358 ) (*store.InternalListWorkflowExecutionsResponse, error) { 359 actualRequest, ok := request.(*manager.ListWorkflowExecutionsRequest) 360 if !ok { 361 panic(fmt.Errorf("wrong request type %v for listOpenWorkflowExecutions", reflect.TypeOf(request))) 362 } 363 364 return s.ListOpenWorkflowExecutions(ctx, actualRequest) 365 } 366 367 func (s *standardStore) listClosedWorkflowExecutionsByWorkflowID( 368 ctx context.Context, 369 request listRequest, 370 ) (*store.InternalListWorkflowExecutionsResponse, error) { 371 actualRequest, ok := request.(*manager.ListWorkflowExecutionsByWorkflowIDRequest) 372 if !ok { 373 panic(fmt.Errorf("wrong request type %v for listClosedWorkflowExecutionsByWorkflowID", reflect.TypeOf(request))) 374 } 375 376 return s.ListClosedWorkflowExecutionsByWorkflowID(ctx, actualRequest) 377 } 378 379 func (s *standardStore) listClosedWorkflowExecutionsByType( 380 ctx context.Context, 381 request listRequest, 382 ) (*store.InternalListWorkflowExecutionsResponse, error) { 383 actualRequest, ok := request.(*manager.ListWorkflowExecutionsByTypeRequest) 384 if !ok { 385 panic(fmt.Errorf("wrong request type %v for listClosedWorkflowExecutionsByType", reflect.TypeOf(request))) 386 } 387 388 return s.ListClosedWorkflowExecutionsByType(ctx, actualRequest) 389 } 390 391 func (s *standardStore) listClosedWorkflowExecutions( 392 ctx context.Context, 393 request listRequest, 394 ) (*store.InternalListWorkflowExecutionsResponse, error) { 395 actualRequest, ok := request.(*manager.ListWorkflowExecutionsRequest) 396 if !ok { 397 panic(fmt.Errorf("wrong request type %v for listClosedWorkflowExecutions", reflect.TypeOf(request))) 398 } 399 400 return s.ListClosedWorkflowExecutions(ctx, actualRequest) 401 }