From d3fc5a19c900a9d4934edf8326bd8c4c3a78c363 Mon Sep 17 00:00:00 2001 From: ystaticy Date: Tue, 26 May 2026 18:14:01 +0800 Subject: [PATCH 1/4] add update keyspce config Signed-off-by: ystaticy --- client/http/interface.go | 19 ++++-- client/http/request_info.go | 1 + client/http/types.go | 11 ++++ tests/integrations/client/http_client_test.go | 61 +++++++++++++++++++ 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/client/http/interface.go b/client/http/interface.go index 9635e40691b..2099f3c8300 100644 --- a/client/http/interface.go +++ b/client/http/interface.go @@ -120,6 +120,8 @@ type Client interface { /* Keyspace interface */ + // UpdateKeyspaceConfig patches the keyspace config. + UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) error // UpdateKeyspaceGCManagementType update the `gc_management_type` in keyspace meta config. // If `gc_management_type` is `global_gc`, it means the current keyspace requires a tidb without 'keyspace-name' // configured to run a global gc worker to calculate a global gc safe point. @@ -1143,19 +1145,28 @@ func (c *client) DeleteOperators(ctx context.Context) error { WithMethod(http.MethodDelete)) } -// UpdateKeyspaceGCManagementType patches the keyspace config. -func (c *client) UpdateKeyspaceGCManagementType(ctx context.Context, keyspaceName string, keyspaceGCmanagementType *KeyspaceGCManagementTypeConfig) error { - keyspaceConfigPatchJSON, err := json.Marshal(keyspaceGCmanagementType) +func (c *client) updateKeyspaceConfig(ctx context.Context, reqName, keyspaceName string, params any) error { + keyspaceConfigPatchJSON, err := json.Marshal(params) if err != nil { return errors.Trace(err) } return c.request(ctx, newRequestInfo(). - WithName(UpdateKeyspaceGCManagementTypeName). + WithName(reqName). WithURI(GetUpdateKeyspaceConfigURL(keyspaceName)). WithMethod(http.MethodPatch). WithBody(keyspaceConfigPatchJSON)) } +// UpdateKeyspaceConfig patches the keyspace config. +func (c *client) UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) error { + return c.updateKeyspaceConfig(ctx, UpdateKeyspaceConfigName, keyspaceName, params) +} + +// UpdateKeyspaceGCManagementType patches the keyspace config. +func (c *client) UpdateKeyspaceGCManagementType(ctx context.Context, keyspaceName string, keyspaceGCmanagementType *KeyspaceGCManagementTypeConfig) error { + return c.updateKeyspaceConfig(ctx, UpdateKeyspaceGCManagementTypeName, keyspaceName, keyspaceGCmanagementType) +} + // GetKeyspaceMetaByName get the given keyspace meta. func (c *client) GetKeyspaceMetaByName(ctx context.Context, keyspaceName string) (*keyspacepb.KeyspaceMeta, error) { var ( diff --git a/client/http/request_info.go b/client/http/request_info.go index f3301628bee..3fb01291f6c 100644 --- a/client/http/request_info.go +++ b/client/http/request_info.go @@ -93,6 +93,7 @@ const ( deletePitrRestoreModeMarkName = "DeletePitrRestoreModeMark" createOperators = "CreateOperators" deleteOperators = "DeleteOperators" + UpdateKeyspaceConfigName = "UpdateKeyspaceConfig" UpdateKeyspaceGCManagementTypeName = "UpdateKeyspaceGCManagementType" GetKeyspaceMetaByNameName = "GetKeyspaceMetaByName" GetKeyspaceMetaByIDName = "GetKeyspaceMetaByID" diff --git a/client/http/types.go b/client/http/types.go index 6acef7bcea2..808e054f256 100644 --- a/client/http/types.go +++ b/client/http/types.go @@ -675,6 +675,17 @@ type KeyspaceGCManagementTypeConfig struct { Config KeyspaceGCManagementType `json:"config"` } +// UpdateKeyspaceConfigParams represents parameters needed to modify target keyspace configs. +// A map of string to string pointer is used to differentiate between json null and "", +// which will both be set to "" if value type is string during marshaling. +type UpdateKeyspaceConfigParams struct { + Config map[string]*string `json:"config"` + // Preconditions specifies prerequisites for updating config, using a JSON-merge-patch-like encoding: + // - key -> nil means the key must be absent. + // - key -> "value" means the key must exist and equal "value". + Preconditions map[string]*string `json:"preconditions,omitempty"` +} + // tempKeyspaceMeta is the keyspace meta struct that returned from the http interface. type tempKeyspaceMeta struct { ID uint32 `json:"id"` diff --git a/tests/integrations/client/http_client_test.go b/tests/integrations/client/http_client_test.go index dc5f7af4bb0..e5210bbc02a 100644 --- a/tests/integrations/client/http_client_test.go +++ b/tests/integrations/client/http_client_test.go @@ -1033,6 +1033,67 @@ func (suite *httpClientTestSuite) TestUpdateKeyspaceGCManagementType() { re.Error(err) } +func (suite *httpClientTestSuite) TestUpdateKeyspaceConfig() { + re := suite.Require() + client := suite.client + ctx, cancel := context.WithCancel(suite.ctx) + defer cancel() + + var keyspaceName string + if kerneltype.IsNextGen() { + keyspaceName = constant.SystemKeyspaceName + } else { + keyspaceName = constant.DefaultKeyspaceName + } + + configKey := "http_client_test_update_keyspace_config" + initialValue := "v1" + err := client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ + Config: map[string]*string{ + configKey: &initialValue, + }, + Preconditions: map[string]*string{ + configKey: nil, + }, + }) + re.NoError(err) + + keyspaceMetaRes, err := client.GetKeyspaceMetaByName(ctx, keyspaceName) + re.NoError(err) + val, ok := keyspaceMetaRes.Config[configKey] + re.True(ok) + re.Equal(initialValue, val) + + wrongExpected := "wrong" + nextValue := "v2" + err = client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ + Config: map[string]*string{ + configKey: &nextValue, + }, + Preconditions: map[string]*string{ + configKey: &wrongExpected, + }, + }) + re.Error(err) + re.Contains(err.Error(), "409 Conflict") + re.Contains(err.Error(), "precondition failed") + + err = client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ + Config: map[string]*string{ + configKey: nil, + }, + Preconditions: map[string]*string{ + configKey: &initialValue, + }, + }) + re.NoError(err) + + keyspaceMetaRes, err = client.GetKeyspaceMetaByName(ctx, keyspaceName) + re.NoError(err) + _, ok = keyspaceMetaRes.Config[configKey] + re.False(ok) +} + func (suite *httpClientTestSuite) TestGetKeyspaceMetaByID() { re := suite.Require() client := suite.client From dfaa4d172b143ca4c18420ea00b81477c88e784f Mon Sep 17 00:00:00 2001 From: ystaticy Date: Fri, 29 May 2026 15:59:57 +0800 Subject: [PATCH 2/4] update UpdateKeyspaceConfig return Signed-off-by: ystaticy --- client/http/interface.go | 63 ++++++------------- client/http/types.go | 16 +++++ tests/integrations/client/http_client_test.go | 12 +++- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/client/http/interface.go b/client/http/interface.go index 2099f3c8300..520f4bbe078 100644 --- a/client/http/interface.go +++ b/client/http/interface.go @@ -120,8 +120,8 @@ type Client interface { /* Keyspace interface */ - // UpdateKeyspaceConfig patches the keyspace config. - UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) error + // UpdateKeyspaceConfig patches the keyspace config and returns the updated keyspace meta. + UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) (*keyspacepb.KeyspaceMeta, error) // UpdateKeyspaceGCManagementType update the `gc_management_type` in keyspace meta config. // If `gc_management_type` is `global_gc`, it means the current keyspace requires a tidb without 'keyspace-name' // configured to run a global gc worker to calculate a global gc safe point. @@ -1145,34 +1145,38 @@ func (c *client) DeleteOperators(ctx context.Context) error { WithMethod(http.MethodDelete)) } -func (c *client) updateKeyspaceConfig(ctx context.Context, reqName, keyspaceName string, params any) error { +func (c *client) updateKeyspaceConfig(ctx context.Context, reqName, keyspaceName string, params any) (*keyspacepb.KeyspaceMeta, error) { + var tempMeta tempKeyspaceMeta keyspaceConfigPatchJSON, err := json.Marshal(params) if err != nil { - return errors.Trace(err) + return nil, errors.Trace(err) } - return c.request(ctx, newRequestInfo(). + err = c.request(ctx, newRequestInfo(). WithName(reqName). WithURI(GetUpdateKeyspaceConfigURL(keyspaceName)). WithMethod(http.MethodPatch). - WithBody(keyspaceConfigPatchJSON)) + WithBody(keyspaceConfigPatchJSON). + WithResp(&tempMeta)) + if err != nil { + return nil, err + } + return tempMeta.toPB() } // UpdateKeyspaceConfig patches the keyspace config. -func (c *client) UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) error { +func (c *client) UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) (*keyspacepb.KeyspaceMeta, error) { return c.updateKeyspaceConfig(ctx, UpdateKeyspaceConfigName, keyspaceName, params) } // UpdateKeyspaceGCManagementType patches the keyspace config. func (c *client) UpdateKeyspaceGCManagementType(ctx context.Context, keyspaceName string, keyspaceGCmanagementType *KeyspaceGCManagementTypeConfig) error { - return c.updateKeyspaceConfig(ctx, UpdateKeyspaceGCManagementTypeName, keyspaceName, keyspaceGCmanagementType) + _, err := c.updateKeyspaceConfig(ctx, UpdateKeyspaceGCManagementTypeName, keyspaceName, keyspaceGCmanagementType) + return err } // GetKeyspaceMetaByName get the given keyspace meta. func (c *client) GetKeyspaceMetaByName(ctx context.Context, keyspaceName string) (*keyspacepb.KeyspaceMeta, error) { - var ( - tempKeyspaceMeta tempKeyspaceMeta - keyspaceMetaPB keyspacepb.KeyspaceMeta - ) + var tempKeyspaceMeta tempKeyspaceMeta err := c.request(ctx, newRequestInfo(). WithName(GetKeyspaceMetaByNameName). WithURI(GetKeyspaceMetaByNameURL(keyspaceName)). @@ -1183,28 +1187,12 @@ func (c *client) GetKeyspaceMetaByName(ctx context.Context, keyspaceName string) return nil, err } - keyspaceState, err := stringToKeyspaceState(tempKeyspaceMeta.State) - if err != nil { - return nil, err - } - - keyspaceMetaPB = keyspacepb.KeyspaceMeta{ - Name: tempKeyspaceMeta.Name, - Id: tempKeyspaceMeta.ID, - Config: tempKeyspaceMeta.Config, - CreatedAt: tempKeyspaceMeta.CreatedAt, - StateChangedAt: tempKeyspaceMeta.StateChangedAt, - State: keyspaceState, - } - return &keyspaceMetaPB, nil + return tempKeyspaceMeta.toPB() } // GetKeyspaceMetaByID get the given keyspace meta. func (c *client) GetKeyspaceMetaByID(ctx context.Context, keyspaceID uint32) (*keyspacepb.KeyspaceMeta, error) { - var ( - tempKeyspaceMeta tempKeyspaceMeta - keyspaceMetaPB keyspacepb.KeyspaceMeta - ) + var tempKeyspaceMeta tempKeyspaceMeta err := c.request(ctx, newRequestInfo(). WithName(GetKeyspaceMetaByIDName). WithURI(GetKeyspaceMetaByIDURL(keyspaceID)). @@ -1215,20 +1203,7 @@ func (c *client) GetKeyspaceMetaByID(ctx context.Context, keyspaceID uint32) (*k return nil, err } - keyspaceState, err := stringToKeyspaceState(tempKeyspaceMeta.State) - if err != nil { - return nil, err - } - - keyspaceMetaPB = keyspacepb.KeyspaceMeta{ - Name: tempKeyspaceMeta.Name, - Id: tempKeyspaceMeta.ID, - Config: tempKeyspaceMeta.Config, - CreatedAt: tempKeyspaceMeta.CreatedAt, - StateChangedAt: tempKeyspaceMeta.StateChangedAt, - State: keyspaceState, - } - return &keyspaceMetaPB, nil + return tempKeyspaceMeta.toPB() } // GetGCSafePoint gets the GC safe point list. diff --git a/client/http/types.go b/client/http/types.go index 808e054f256..29a2cba605f 100644 --- a/client/http/types.go +++ b/client/http/types.go @@ -696,6 +696,22 @@ type tempKeyspaceMeta struct { Config map[string]string `json:"config"` } +func (meta *tempKeyspaceMeta) toPB() (*keyspacepb.KeyspaceMeta, error) { + keyspaceState, err := stringToKeyspaceState(meta.State) + if err != nil { + return nil, err + } + + return &keyspacepb.KeyspaceMeta{ + Name: meta.Name, + Id: meta.ID, + Config: meta.Config, + CreatedAt: meta.CreatedAt, + StateChangedAt: meta.StateChangedAt, + State: keyspaceState, + }, nil +} + func stringToKeyspaceState(str string) (keyspacepb.KeyspaceState, error) { switch str { case "ENABLED": diff --git a/tests/integrations/client/http_client_test.go b/tests/integrations/client/http_client_test.go index e5210bbc02a..03c0399883c 100644 --- a/tests/integrations/client/http_client_test.go +++ b/tests/integrations/client/http_client_test.go @@ -1048,7 +1048,7 @@ func (suite *httpClientTestSuite) TestUpdateKeyspaceConfig() { configKey := "http_client_test_update_keyspace_config" initialValue := "v1" - err := client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ + updatedMeta, err := client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ Config: map[string]*string{ configKey: &initialValue, }, @@ -1057,6 +1057,9 @@ func (suite *httpClientTestSuite) TestUpdateKeyspaceConfig() { }, }) re.NoError(err) + re.NotNil(updatedMeta) + re.Equal(keyspaceName, updatedMeta.GetName()) + re.Equal(initialValue, updatedMeta.GetConfig()[configKey]) keyspaceMetaRes, err := client.GetKeyspaceMetaByName(ctx, keyspaceName) re.NoError(err) @@ -1066,7 +1069,7 @@ func (suite *httpClientTestSuite) TestUpdateKeyspaceConfig() { wrongExpected := "wrong" nextValue := "v2" - err = client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ + _, err = client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ Config: map[string]*string{ configKey: &nextValue, }, @@ -1078,7 +1081,7 @@ func (suite *httpClientTestSuite) TestUpdateKeyspaceConfig() { re.Contains(err.Error(), "409 Conflict") re.Contains(err.Error(), "precondition failed") - err = client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ + updatedMeta, err = client.UpdateKeyspaceConfig(ctx, keyspaceName, &pd.UpdateKeyspaceConfigParams{ Config: map[string]*string{ configKey: nil, }, @@ -1087,6 +1090,9 @@ func (suite *httpClientTestSuite) TestUpdateKeyspaceConfig() { }, }) re.NoError(err) + re.NotNil(updatedMeta) + _, ok = updatedMeta.Config[configKey] + re.False(ok) keyspaceMetaRes, err = client.GetKeyspaceMetaByName(ctx, keyspaceName) re.NoError(err) From 3761025271bfbac464e054cd7e79808a21d4310f Mon Sep 17 00:00:00 2001 From: ystaticy Date: Fri, 29 May 2026 16:58:13 +0800 Subject: [PATCH 3/4] make check Signed-off-by: ystaticy --- client/http/interface.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/http/interface.go b/client/http/interface.go index 520f4bbe078..4ef1c884fdd 100644 --- a/client/http/interface.go +++ b/client/http/interface.go @@ -1145,7 +1145,7 @@ func (c *client) DeleteOperators(ctx context.Context) error { WithMethod(http.MethodDelete)) } -func (c *client) updateKeyspaceConfig(ctx context.Context, reqName, keyspaceName string, params any) (*keyspacepb.KeyspaceMeta, error) { +func (c *client) patchKeyspaceConfig(ctx context.Context, reqName, keyspaceName string, params any) (*keyspacepb.KeyspaceMeta, error) { var tempMeta tempKeyspaceMeta keyspaceConfigPatchJSON, err := json.Marshal(params) if err != nil { @@ -1165,12 +1165,12 @@ func (c *client) updateKeyspaceConfig(ctx context.Context, reqName, keyspaceName // UpdateKeyspaceConfig patches the keyspace config. func (c *client) UpdateKeyspaceConfig(ctx context.Context, keyspaceName string, params *UpdateKeyspaceConfigParams) (*keyspacepb.KeyspaceMeta, error) { - return c.updateKeyspaceConfig(ctx, UpdateKeyspaceConfigName, keyspaceName, params) + return c.patchKeyspaceConfig(ctx, UpdateKeyspaceConfigName, keyspaceName, params) } // UpdateKeyspaceGCManagementType patches the keyspace config. func (c *client) UpdateKeyspaceGCManagementType(ctx context.Context, keyspaceName string, keyspaceGCmanagementType *KeyspaceGCManagementTypeConfig) error { - _, err := c.updateKeyspaceConfig(ctx, UpdateKeyspaceGCManagementTypeName, keyspaceName, keyspaceGCmanagementType) + _, err := c.patchKeyspaceConfig(ctx, UpdateKeyspaceGCManagementTypeName, keyspaceName, keyspaceGCmanagementType) return err } From ec751163c67d63d2aadd8a1af7e9ed71eded09e9 Mon Sep 17 00:00:00 2001 From: ystaticy Date: Fri, 29 May 2026 21:53:39 +0800 Subject: [PATCH 4/4] fix comments Signed-off-by: ystaticy --- client/http/client.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/http/client.go b/client/http/client.go index 67797ba502d..2236baae4ed 100644 --- a/client/http/client.go +++ b/client/http/client.go @@ -155,7 +155,7 @@ func (ci *clientInner) requestWithRetry( continue } statusCode, err = ci.doRequest(ctx, serverURL, reqInfo, headerOpts...) - if err == nil || noNeedRetry(statusCode) { + if err == nil || noNeedRetryForRequest(reqInfo, statusCode) { return err } log.Debug("[pd] http request url failed", append(logFields, @@ -177,7 +177,7 @@ func (ci *clientInner) requestWithRetry( // Set the retryable checker for the backoffer if it's not set. bo.SetRetryableChecker(func(err error) bool { // Backoffer also needs to check the status code to determine whether to retry. - return err != nil && !noNeedRetry(statusCode) + return err != nil && !noNeedRetryForRequest(reqInfo, statusCode) }, false) return bo.Exec(ctx, execFunc) } @@ -188,6 +188,13 @@ func noNeedRetry(statusCode int) bool { statusCode == http.StatusBadRequest } +func noNeedRetryForRequest(reqInfo *requestInfo, statusCode int) bool { + if noNeedRetry(statusCode) { + return true + } + return reqInfo.name == UpdateKeyspaceConfigName && statusCode == http.StatusConflict +} + func (ci *clientInner) doRequest( ctx context.Context, serverURL string, reqInfo *requestInfo,