Skip to content

Commit f994ea5

Browse files
authored
Rename namespace persistence API (#2595)
1 parent 7d1d1c1 commit f994ea5

File tree

12 files changed

+261
-4
lines changed

12 files changed

+261
-4
lines changed

common/metrics/defs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ const (
264264
PersistenceUpdateNamespaceScope
265265
// PersistenceDeleteNamespaceScope tracks DeleteNamespace calls made by service to persistence layer
266266
PersistenceDeleteNamespaceScope
267+
// PersistenceRenameNamespaceScope tracks RenameNamespace calls made by service to persistence layer
268+
PersistenceRenameNamespaceScope
267269
// PersistenceDeleteNamespaceByNameScope tracks DeleteNamespaceByName calls made by service to persistence layer
268270
PersistenceDeleteNamespaceByNameScope
269271
// PersistenceListNamespaceScope tracks DeleteNamespaceByName calls made by service to persistence layer
@@ -1261,6 +1263,7 @@ var ScopeDefs = map[ServiceIdx]map[int]scopeDefinition{
12611263
PersistenceGetNamespaceScope: {operation: "GetNamespace"},
12621264
PersistenceUpdateNamespaceScope: {operation: "UpdateNamespace"},
12631265
PersistenceDeleteNamespaceScope: {operation: "DeleteNamespace"},
1266+
PersistenceRenameNamespaceScope: {operation: "RenameNamespace"},
12641267
PersistenceDeleteNamespaceByNameScope: {operation: "DeleteNamespaceByName"},
12651268
PersistenceListNamespaceScope: {operation: "ListNamespace"},
12661269
PersistenceGetMetadataScope: {operation: "GetMetadata"},

common/persistence/cassandra/metadata_store.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ const (
8888
templateNamespaceColumns +
8989
` FROM namespaces ` +
9090
`WHERE namespaces_partition = ? `
91+
92+
templateUpdateNamespaceByIdQuery = `UPDATE namespaces_by_id ` +
93+
`SET name = ? ` +
94+
`WHERE id = ?`
9195
)
9296

9397
type (
@@ -198,6 +202,56 @@ func (m *MetadataStore) UpdateNamespace(request *p.InternalUpdateNamespaceReques
198202
return nil
199203
}
200204

205+
// RenameNamespace should be used with caution.
206+
// Not every namespace can be renamed because namespace name are stored in the database.
207+
// It may leave database in inconsistent state and must be retried until success.
208+
// Step 1. Update row in `namespaces_by_id` table with the new name.
209+
// Step 2. Batch of:
210+
// Insert row into `namespaces` table with new name and new `notification_version`.
211+
// Delete row from `namespaces` table with old name.
212+
// Update `notification_version` in metadata row.
213+
//
214+
// NOTE: `namespaces_by_id` is currently used only for `DescribeNamespace` API and namespace Id collision check.
215+
func (m *MetadataStore) RenameNamespace(request *p.InternalRenameNamespaceRequest) error {
216+
// Step 1.
217+
if updateErr := m.session.Query(templateUpdateNamespaceByIdQuery,
218+
request.Name,
219+
request.Id,
220+
).Exec(); updateErr != nil {
221+
return serviceerror.NewUnavailable(fmt.Sprintf("RenameNamespace operation failed to update 'namespaces_by_id' table. Error: %v", updateErr))
222+
}
223+
224+
// Step 2.
225+
batch := m.session.NewBatch(gocql.LoggedBatch)
226+
batch.Query(templateCreateNamespaceByNameQueryWithinBatchV2,
227+
constNamespacePartition,
228+
request.Id,
229+
request.Name,
230+
request.Namespace.Data,
231+
request.Namespace.EncodingType.String(),
232+
request.NotificationVersion,
233+
request.IsGlobal,
234+
)
235+
batch.Query(templateDeleteNamespaceByNameQueryV2,
236+
constNamespacePartition,
237+
request.PreviousName,
238+
)
239+
m.updateMetadataBatch(batch, request.NotificationVersion)
240+
241+
previous := make(map[string]interface{})
242+
applied, iter, err := m.session.MapExecuteBatchCAS(batch, previous)
243+
if err != nil {
244+
return serviceerror.NewUnavailable(fmt.Sprintf("RenameNamespace operation failed. Error: %v", err))
245+
}
246+
defer func() { _ = iter.Close() }()
247+
248+
if !applied {
249+
return serviceerror.NewUnavailable(fmt.Sprintf("RenameNamespace operation failed because of conditional failure."))
250+
}
251+
252+
return nil
253+
}
254+
201255
func (m *MetadataStore) GetNamespace(request *p.GetNamespaceRequest) (*p.InternalGetNamespaceResponse, error) {
202256
var query gocql.Query
203257
var err error

common/persistence/client/fault_injection.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,13 @@ func (m *FaultInjectionMetadataStore) UpdateNamespace(request *persistence.Inter
720720
return m.baseMetadataStore.UpdateNamespace(request)
721721
}
722722

723+
func (m *FaultInjectionMetadataStore) RenameNamespace(request *persistence.InternalRenameNamespaceRequest) error {
724+
if err := m.ErrorGenerator.Generate(); err != nil {
725+
return err
726+
}
727+
return m.baseMetadataStore.RenameNamespace(request)
728+
}
729+
723730
func (m *FaultInjectionMetadataStore) DeleteNamespace(request *persistence.DeleteNamespaceRequest) error {
724731
if err := m.ErrorGenerator.Generate(); err != nil {
725732
return err

common/persistence/dataInterfaces.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,12 @@ type (
623623
NotificationVersion int64
624624
}
625625

626+
// RenameNamespaceRequest is used to rename namespace.
627+
RenameNamespaceRequest struct {
628+
PreviousName string
629+
NewName string
630+
}
631+
626632
// DeleteNamespaceRequest is used to delete namespace entry from namespaces table
627633
DeleteNamespaceRequest struct {
628634
ID string
@@ -1065,6 +1071,7 @@ type (
10651071
CreateNamespace(request *CreateNamespaceRequest) (*CreateNamespaceResponse, error)
10661072
GetNamespace(request *GetNamespaceRequest) (*GetNamespaceResponse, error)
10671073
UpdateNamespace(request *UpdateNamespaceRequest) error
1074+
RenameNamespace(request *RenameNamespaceRequest) error
10681075
DeleteNamespace(request *DeleteNamespaceRequest) error
10691076
DeleteNamespaceByName(request *DeleteNamespaceByNameRequest) error
10701077
ListNamespaces(request *ListNamespacesRequest) (*ListNamespacesResponse, error)

common/persistence/dataInterfaces_mock.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/persistence/metadata_manager.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type (
4949

5050
var _ MetadataManager = (*metadataManagerImpl)(nil)
5151

52-
//NewMetadataManagerImpl returns new MetadataManager
52+
// NewMetadataManagerImpl returns new MetadataManager
5353
func NewMetadataManagerImpl(
5454
persistence MetadataStore,
5555
serializer serialization.Serializer,
@@ -105,6 +105,41 @@ func (m *metadataManagerImpl) UpdateNamespace(request *UpdateNamespaceRequest) e
105105
})
106106
}
107107

108+
func (m *metadataManagerImpl) RenameNamespace(request *RenameNamespaceRequest) error {
109+
ns, err := m.GetNamespace(&GetNamespaceRequest{
110+
Name: request.PreviousName,
111+
})
112+
if err != nil {
113+
return err
114+
}
115+
116+
metadata, err := m.GetMetadata()
117+
if err != nil {
118+
return err
119+
}
120+
121+
previousName := ns.Namespace.Info.Name
122+
ns.Namespace.Info.Name = request.NewName
123+
124+
nsDataBlob, err := m.serializer.NamespaceDetailToBlob(ns.Namespace, enumspb.ENCODING_TYPE_PROTO3)
125+
if err != nil {
126+
return err
127+
}
128+
129+
renameRequest := &InternalRenameNamespaceRequest{
130+
InternalUpdateNamespaceRequest: &InternalUpdateNamespaceRequest{
131+
Id: ns.Namespace.Info.Id,
132+
Name: ns.Namespace.Info.Name,
133+
Namespace: nsDataBlob,
134+
NotificationVersion: metadata.NotificationVersion,
135+
IsGlobal: ns.IsGlobalNamespace,
136+
},
137+
PreviousName: previousName,
138+
}
139+
140+
return m.persistence.RenameNamespace(renameRequest)
141+
}
142+
108143
func (m *metadataManagerImpl) DeleteNamespace(request *DeleteNamespaceRequest) error {
109144
return m.persistence.DeleteNamespace(request)
110145
}

common/persistence/mock/store_mock.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/persistence/persistence-tests/metadataPersistenceV2Test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,92 @@ func (m *MetadataPersistenceSuiteV2) TestUpdateNamespace() {
777777
m.EqualTimes(time.Unix(0, 0).UTC(), *resp6.Namespace.FailoverEndTime)
778778
}
779779

780+
func (m *MetadataPersistenceSuiteV2) TestRenameNamespace() {
781+
id := uuid.New()
782+
name := "rename-namespace-test-name"
783+
newName := "rename-namespace-test-new-name"
784+
newNewName := "rename-namespace-test-new-new-name"
785+
state := enumspb.NAMESPACE_STATE_REGISTERED
786+
description := "rename-namespace-test-description"
787+
owner := "rename-namespace-test-owner"
788+
data := map[string]string{"k1": "v1"}
789+
retention := int32(10)
790+
historyArchivalState := enumspb.ARCHIVAL_STATE_ENABLED
791+
historyArchivalURI := "test://history/uri"
792+
visibilityArchivalState := enumspb.ARCHIVAL_STATE_ENABLED
793+
visibilityArchivalURI := "test://visibility/uri"
794+
795+
clusterActive := "some random active cluster name"
796+
clusterStandby := "some random standby cluster name"
797+
configVersion := int64(10)
798+
failoverVersion := int64(59)
799+
isGlobalNamespace := true
800+
clusters := []string{clusterActive, clusterStandby}
801+
802+
resp1, err1 := m.CreateNamespace(
803+
&persistencespb.NamespaceInfo{
804+
Id: id,
805+
Name: name,
806+
State: state,
807+
Description: description,
808+
Owner: owner,
809+
Data: data,
810+
},
811+
&persistencespb.NamespaceConfig{
812+
Retention: timestamp.DurationFromDays(retention),
813+
HistoryArchivalState: historyArchivalState,
814+
HistoryArchivalUri: historyArchivalURI,
815+
VisibilityArchivalState: visibilityArchivalState,
816+
VisibilityArchivalUri: visibilityArchivalURI,
817+
},
818+
&persistencespb.NamespaceReplicationConfig{
819+
ActiveClusterName: clusterActive,
820+
Clusters: clusters,
821+
},
822+
isGlobalNamespace,
823+
configVersion,
824+
failoverVersion,
825+
)
826+
m.NoError(err1)
827+
m.EqualValues(id, resp1.ID)
828+
829+
_, err2 := m.GetNamespace(id, "")
830+
m.NoError(err2)
831+
832+
err3 := m.MetadataManager.RenameNamespace(&p.RenameNamespaceRequest{
833+
PreviousName: name,
834+
NewName: newName,
835+
})
836+
m.NoError(err3)
837+
838+
resp4, err4 := m.GetNamespace("", newName)
839+
m.NoError(err4)
840+
m.NotNil(resp4)
841+
m.EqualValues(id, resp4.Namespace.Info.Id)
842+
m.Equal(newName, resp4.Namespace.Info.Name)
843+
m.Equal(isGlobalNamespace, resp4.IsGlobalNamespace)
844+
845+
resp5, err5 := m.GetNamespace(id, "")
846+
m.NoError(err5)
847+
m.NotNil(resp5)
848+
m.EqualValues(id, resp5.Namespace.Info.Id)
849+
m.Equal(newName, resp5.Namespace.Info.Name)
850+
m.Equal(isGlobalNamespace, resp5.IsGlobalNamespace)
851+
852+
err6 := m.MetadataManager.RenameNamespace(&p.RenameNamespaceRequest{
853+
PreviousName: newName,
854+
NewName: newNewName,
855+
})
856+
m.NoError(err6)
857+
858+
resp6, err6 := m.GetNamespace(id, "")
859+
m.NoError(err6)
860+
m.NotNil(resp6)
861+
m.EqualValues(id, resp6.Namespace.Info.Id)
862+
m.Equal(newNewName, resp6.Namespace.Info.Name)
863+
m.Equal(isGlobalNamespace, resp6.IsGlobalNamespace)
864+
}
865+
780866
// TestDeleteNamespace test
781867
func (m *MetadataPersistenceSuiteV2) TestDeleteNamespace() {
782868
id := uuid.New()

common/persistence/persistenceInterface.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type (
8181
CreateNamespace(request *InternalCreateNamespaceRequest) (*CreateNamespaceResponse, error)
8282
GetNamespace(request *GetNamespaceRequest) (*InternalGetNamespaceResponse, error)
8383
UpdateNamespace(request *InternalUpdateNamespaceRequest) error
84+
RenameNamespace(request *InternalRenameNamespaceRequest) error
8485
DeleteNamespace(request *DeleteNamespaceRequest) error
8586
DeleteNamespaceByName(request *DeleteNamespaceByNameRequest) error
8687
ListNamespaces(request *ListNamespacesRequest) (*InternalListNamespacesResponse, error)
@@ -625,6 +626,11 @@ type (
625626
IsGlobal bool
626627
}
627628

629+
InternalRenameNamespaceRequest struct {
630+
*InternalUpdateNamespaceRequest
631+
PreviousName string
632+
}
633+
628634
// InternalListNamespacesResponse is the response for GetNamespace
629635
InternalListNamespacesResponse struct {
630636
Namespaces []*InternalGetNamespaceResponse

common/persistence/persistenceMetricClients.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,20 @@ func (p *metadataPersistenceClient) UpdateNamespace(request *UpdateNamespaceRequ
677677
return err
678678
}
679679

680+
func (p *metadataPersistenceClient) RenameNamespace(request *RenameNamespaceRequest) error {
681+
p.metricClient.IncCounter(metrics.PersistenceRenameNamespaceScope, metrics.PersistenceRequests)
682+
683+
sw := p.metricClient.StartTimer(metrics.PersistenceRenameNamespaceScope, metrics.PersistenceLatency)
684+
err := p.persistence.RenameNamespace(request)
685+
sw.Stop()
686+
687+
if err != nil {
688+
p.updateErrorMetric(metrics.PersistenceRenameNamespaceScope, err)
689+
}
690+
691+
return err
692+
}
693+
680694
func (p *metadataPersistenceClient) DeleteNamespace(request *DeleteNamespaceRequest) error {
681695
p.metricClient.IncCounter(metrics.PersistenceDeleteNamespaceScope, metrics.PersistenceRequests)
682696

@@ -1025,7 +1039,7 @@ func (c *clusterMetadataPersistenceClient) Close() {
10251039
}
10261040

10271041
func (c *clusterMetadataPersistenceClient) ListClusterMetadata(request *ListClusterMetadataRequest) (*ListClusterMetadataResponse, error) {
1028-
//This is a wrapper of GetClusterMetadata API, use the same scope here
1042+
// This is a wrapper of GetClusterMetadata API, use the same scope here
10291043
c.metricClient.IncCounter(metrics.PersistenceListClusterMetadataScope, metrics.PersistenceRequests)
10301044

10311045
sw := c.metricClient.StartTimer(metrics.PersistenceListClusterMetadataScope, metrics.PersistenceLatency)
@@ -1040,7 +1054,7 @@ func (c *clusterMetadataPersistenceClient) ListClusterMetadata(request *ListClus
10401054
}
10411055

10421056
func (c *clusterMetadataPersistenceClient) GetCurrentClusterMetadata() (*GetClusterMetadataResponse, error) {
1043-
//This is a wrapper of GetClusterMetadata API, use the same scope here
1057+
// This is a wrapper of GetClusterMetadata API, use the same scope here
10441058
c.metricClient.IncCounter(metrics.PersistenceGetClusterMetadataScope, metrics.PersistenceRequests)
10451059

10461060
sw := c.metricClient.StartTimer(metrics.PersistenceGetClusterMetadataScope, metrics.PersistenceLatency)

0 commit comments

Comments
 (0)