go.temporal.io/server@v1.23.0/common/persistence/sql/metadata.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 "database/sql" 30 "fmt" 31 32 "go.temporal.io/api/serviceerror" 33 34 "go.temporal.io/server/common/log" 35 "go.temporal.io/server/common/persistence" 36 "go.temporal.io/server/common/persistence/sql/sqlplugin" 37 "go.temporal.io/server/common/primitives" 38 ) 39 40 type sqlMetadataManagerV2 struct { 41 SqlStore 42 activeClusterName string 43 } 44 45 // newMetadataPersistenceV2 creates an instance of sqlMetadataManagerV2 46 func newMetadataPersistenceV2( 47 db sqlplugin.DB, 48 currentClusterName string, 49 logger log.Logger, 50 ) (persistence.MetadataStore, error) { 51 return &sqlMetadataManagerV2{ 52 SqlStore: NewSqlStore(db, logger), 53 activeClusterName: currentClusterName, 54 }, nil 55 } 56 57 func (m *sqlMetadataManagerV2) CreateNamespace( 58 ctx context.Context, 59 request *persistence.InternalCreateNamespaceRequest, 60 ) (*persistence.CreateNamespaceResponse, error) { 61 idBytes, err := primitives.ParseUUID(request.ID) 62 if err != nil { 63 return nil, err 64 } 65 66 var resp *persistence.CreateNamespaceResponse 67 err = m.txExecute(ctx, "CreateNamespace", func(tx sqlplugin.Tx) error { 68 metadata, err := lockMetadata(ctx, tx) 69 if err != nil { 70 return err 71 } 72 if _, err := tx.InsertIntoNamespace(ctx, &sqlplugin.NamespaceRow{ 73 Name: request.Name, 74 ID: idBytes, 75 Data: request.Namespace.Data, 76 DataEncoding: request.Namespace.EncodingType.String(), 77 IsGlobal: request.IsGlobal, 78 NotificationVersion: metadata.NotificationVersion, 79 }); err != nil { 80 if m.Db.IsDupEntryError(err) { 81 return serviceerror.NewNamespaceAlreadyExists(fmt.Sprintf("name: %v", request.Name)) 82 } 83 return err 84 } 85 if err := updateMetadata(ctx, 86 tx, 87 metadata.NotificationVersion, 88 ); err != nil { 89 return err 90 } 91 resp = &persistence.CreateNamespaceResponse{ID: request.ID} 92 return nil 93 }) 94 return resp, err 95 } 96 97 func (m *sqlMetadataManagerV2) GetNamespace( 98 ctx context.Context, 99 request *persistence.GetNamespaceRequest, 100 ) (*persistence.InternalGetNamespaceResponse, error) { 101 idBytes, err := primitives.ParseUUID(request.ID) 102 if err != nil { 103 return nil, err 104 } 105 filter := sqlplugin.NamespaceFilter{} 106 switch { 107 case request.Name != "" && request.ID != "": 108 return nil, serviceerror.NewInvalidArgument("GetNamespace operation failed. Both ID and Name specified in request.") 109 case request.Name != "": 110 filter.Name = &request.Name 111 case len(request.ID) != 0: 112 filter.ID = &idBytes 113 default: 114 return nil, serviceerror.NewInvalidArgument("GetNamespace operation failed. Both ID and Name are empty.") 115 } 116 117 rows, err := m.Db.SelectFromNamespace(ctx, filter) 118 if err != nil { 119 switch err { 120 case sql.ErrNoRows: 121 // We did not return in the above for-loop because there were no rows. 122 identity := request.Name 123 if len(request.ID) > 0 { 124 identity = request.ID 125 } 126 127 return nil, serviceerror.NewNamespaceNotFound(identity) 128 default: 129 return nil, serviceerror.NewUnavailable(fmt.Sprintf("GetNamespace operation failed. Error %v", err)) 130 } 131 } 132 133 response, err := m.namespaceRowToGetNamespaceResponse(&rows[0]) 134 if err != nil { 135 return nil, err 136 } 137 138 return response, nil 139 } 140 141 func (m *sqlMetadataManagerV2) namespaceRowToGetNamespaceResponse(row *sqlplugin.NamespaceRow) (*persistence.InternalGetNamespaceResponse, error) { 142 return &persistence.InternalGetNamespaceResponse{ 143 Namespace: persistence.NewDataBlob(row.Data, row.DataEncoding), 144 IsGlobal: row.IsGlobal, 145 NotificationVersion: row.NotificationVersion, 146 }, nil 147 } 148 149 func (m *sqlMetadataManagerV2) UpdateNamespace( 150 ctx context.Context, 151 request *persistence.InternalUpdateNamespaceRequest, 152 ) error { 153 return m.updateNamespace(ctx, request, "UpdateNamespace") 154 } 155 156 func (m *sqlMetadataManagerV2) RenameNamespace( 157 ctx context.Context, 158 request *persistence.InternalRenameNamespaceRequest, 159 ) error { 160 return m.updateNamespace(ctx, request.InternalUpdateNamespaceRequest, "RenameNamespace") 161 } 162 163 func (m *sqlMetadataManagerV2) updateNamespace( 164 ctx context.Context, 165 request *persistence.InternalUpdateNamespaceRequest, 166 operationName string, 167 ) error { 168 idBytes, err := primitives.ParseUUID(request.Id) 169 if err != nil { 170 return err 171 } 172 173 return m.txExecute(ctx, operationName, func(tx sqlplugin.Tx) error { 174 metadata, err := lockMetadata(ctx, tx) 175 if err != nil { 176 return err 177 } 178 if metadata.NotificationVersion != request.NotificationVersion { 179 return fmt.Errorf( 180 "conditional update error: expect: %v, actual: %v", 181 request.NotificationVersion, 182 metadata.NotificationVersion, 183 ) 184 } 185 result, err := tx.UpdateNamespace(ctx, &sqlplugin.NamespaceRow{ 186 Name: request.Name, 187 ID: idBytes, 188 Data: request.Namespace.Data, 189 DataEncoding: request.Namespace.EncodingType.String(), 190 NotificationVersion: request.NotificationVersion, 191 IsGlobal: request.IsGlobal, 192 }) 193 if err != nil { 194 return err 195 } 196 noRowsAffected, err := result.RowsAffected() 197 if err != nil { 198 return fmt.Errorf("rowsAffected error: %v", err) 199 } 200 if noRowsAffected != 1 { 201 return fmt.Errorf("%v rows updated instead of one", noRowsAffected) 202 } 203 return updateMetadata(ctx, tx, metadata.NotificationVersion) 204 }) 205 } 206 207 func (m *sqlMetadataManagerV2) DeleteNamespace( 208 ctx context.Context, 209 request *persistence.DeleteNamespaceRequest, 210 ) error { 211 idBytes, err := primitives.ParseUUID(request.ID) 212 if err != nil { 213 return err 214 } 215 216 return m.txExecute(ctx, "DeleteNamespace", func(tx sqlplugin.Tx) error { 217 _, err := tx.DeleteFromNamespace(ctx, sqlplugin.NamespaceFilter{ 218 ID: &idBytes, 219 }) 220 return err 221 }) 222 } 223 224 func (m *sqlMetadataManagerV2) DeleteNamespaceByName( 225 ctx context.Context, 226 request *persistence.DeleteNamespaceByNameRequest, 227 ) error { 228 return m.txExecute(ctx, "DeleteNamespaceByName", func(tx sqlplugin.Tx) error { 229 _, err := tx.DeleteFromNamespace(ctx, sqlplugin.NamespaceFilter{ 230 Name: &request.Name, 231 }) 232 return err 233 }) 234 } 235 236 func (m *sqlMetadataManagerV2) GetMetadata( 237 ctx context.Context, 238 ) (*persistence.GetMetadataResponse, error) { 239 row, err := m.Db.SelectFromNamespaceMetadata(ctx) 240 if err != nil { 241 return nil, serviceerror.NewUnavailable(fmt.Sprintf("GetMetadata operation failed. Error: %v", err)) 242 } 243 return &persistence.GetMetadataResponse{NotificationVersion: row.NotificationVersion}, nil 244 } 245 246 func (m *sqlMetadataManagerV2) ListNamespaces( 247 ctx context.Context, 248 request *persistence.InternalListNamespacesRequest, 249 ) (*persistence.InternalListNamespacesResponse, error) { 250 var pageToken *primitives.UUID 251 if request.NextPageToken != nil { 252 token := primitives.UUID(request.NextPageToken) 253 pageToken = &token 254 } 255 rows, err := m.Db.SelectFromNamespace(ctx, sqlplugin.NamespaceFilter{ 256 GreaterThanID: pageToken, 257 PageSize: &request.PageSize, 258 }) 259 if err != nil { 260 if err == sql.ErrNoRows { 261 return &persistence.InternalListNamespacesResponse{}, nil 262 } 263 return nil, serviceerror.NewUnavailable(fmt.Sprintf("ListNamespaces operation failed. Failed to get namespace rows. Error: %v", err)) 264 } 265 266 var namespaces []*persistence.InternalGetNamespaceResponse 267 for _, row := range rows { 268 resp, err := m.namespaceRowToGetNamespaceResponse(&row) 269 if err != nil { 270 return nil, err 271 } 272 namespaces = append(namespaces, resp) 273 } 274 275 resp := &persistence.InternalListNamespacesResponse{Namespaces: namespaces} 276 if len(rows) >= request.PageSize { 277 resp.NextPageToken = rows[len(rows)-1].ID 278 } 279 280 return resp, nil 281 } 282 283 func updateMetadata( 284 ctx context.Context, 285 tx sqlplugin.Tx, 286 oldNotificationVersion int64, 287 ) error { 288 result, err := tx.UpdateNamespaceMetadata(ctx, &sqlplugin.NamespaceMetadataRow{ 289 NotificationVersion: oldNotificationVersion, 290 }) 291 if err != nil { 292 return serviceerror.NewUnavailable(fmt.Sprintf("Failed to update namespace metadata. Error: %v", err)) 293 } 294 295 rowsAffected, err := result.RowsAffected() 296 if err != nil { 297 return serviceerror.NewUnavailable(fmt.Sprintf("Could not verify whether namespace metadata update occurred. Error: %v", err)) 298 } else if rowsAffected != 1 { 299 return serviceerror.NewUnavailable(fmt.Sprintf("Failed to update namespace metadata. <>1 rows affected. Error: %v", err)) 300 } 301 302 return nil 303 } 304 305 func lockMetadata( 306 ctx context.Context, 307 tx sqlplugin.Tx, 308 ) (*sqlplugin.NamespaceMetadataRow, error) { 309 row, err := tx.LockNamespaceMetadata(ctx) 310 if err != nil { 311 return nil, serviceerror.NewUnavailable(fmt.Sprintf("Failed to lock namespace metadata. Error: %v", err)) 312 } 313 return row, nil 314 }