From 11cb9ad8088bb0ef049b745ada7828ce1cf01143 Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Tue, 4 Aug 2026 01:05:05 +0800 Subject: [PATCH 1/2] tikv: support CES keyspace-level transaction safepoint Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus Signed-off-by: Ping Yu --- tikv/compatible_txn_safe_point_loader.go | 20 ++++-- tikv/compatible_txn_safe_point_loader_test.go | 61 +++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 tikv/compatible_txn_safe_point_loader_test.go diff --git a/tikv/compatible_txn_safe_point_loader.go b/tikv/compatible_txn_safe_point_loader.go index 013a7e38c2..ad4884c5ce 100644 --- a/tikv/compatible_txn_safe_point_loader.go +++ b/tikv/compatible_txn_safe_point_loader.go @@ -10,6 +10,7 @@ import ( "sync/atomic" "time" + "github.com/pingcap/kvproto/pkg/keyspacepb" "github.com/pkg/errors" "github.com/tikv/client-go/v2/internal/apicodec" "github.com/tikv/client-go/v2/util" @@ -23,6 +24,19 @@ const ( keyspaceLevelTxnSafePointPath = "/keyspaces/tidb/%d/tidb/store/gcworker/saved_safe_point" ) +// IsCESKeyspaceLevelGC reports whether a keyspace uses the CES keyspace-level GC metadata format. +// TODO: Replace this implementation with pd.IsCESKeyspaceLevelGC after API v3 support is merged into client-go. +func IsCESKeyspaceLevelGC(keyspaceMeta *keyspacepb.KeyspaceMeta) bool { + return keyspaceMeta != nil && keyspaceMeta.Config != nil && keyspaceMeta.Config["safe_point_version"] == "v2" +} + +func compatibleTxnSafePointPath(keyspaceMeta *keyspacepb.KeyspaceMeta) string { + if pd.IsKeyspaceUsingKeyspaceLevelGC(keyspaceMeta) || IsCESKeyspaceLevelGC(keyspaceMeta) { + return fmt.Sprintf(keyspaceLevelTxnSafePointPath, keyspaceMeta.Id) + } + return unifiedTxnSafePointPath +} + // compatibleTxnSafePointLoader is used to load txn safe point from etcd for old versions where the GetGCState API // is not yet supported. // @@ -101,11 +115,7 @@ func (l *compatibleTxnSafePointLoader) loadTxnSafePoint(ctx context.Context) (ui } } - key := unifiedTxnSafePointPath - keyspaceMeta := l.codec.GetKeyspaceMeta() - if pd.IsKeyspaceUsingKeyspaceLevelGC(keyspaceMeta) { - key = fmt.Sprintf(keyspaceLevelTxnSafePointPath, keyspaceMeta.Id) - } + key := compatibleTxnSafePointPath(l.codec.GetKeyspaceMeta()) // Follow the same implementation as the EtcdSafePointKV by setting the timeout 5 seconds. ctx, cancel := context.WithTimeout(ctx, time.Second*5) diff --git a/tikv/compatible_txn_safe_point_loader_test.go b/tikv/compatible_txn_safe_point_loader_test.go new file mode 100644 index 0000000000..36ae2a9f5e --- /dev/null +++ b/tikv/compatible_txn_safe_point_loader_test.go @@ -0,0 +1,61 @@ +package tikv + +import ( + "testing" + + "github.com/pingcap/kvproto/pkg/keyspacepb" + "github.com/stretchr/testify/require" +) + +func TestIsCESKeyspaceLevelGC(t *testing.T) { + testCases := []struct { + name string + meta *keyspacepb.KeyspaceMeta + want bool + }{ + {name: "nil keyspace"}, + {name: "missing config", meta: &keyspacepb.KeyspaceMeta{}}, + {name: "CES keyspace-level GC", meta: &keyspacepb.KeyspaceMeta{Config: map[string]string{"safe_point_version": "v2"}}, want: true}, + {name: "case-sensitive version", meta: &keyspacepb.KeyspaceMeta{Config: map[string]string{"safe_point_version": "V2"}}}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.want, IsCESKeyspaceLevelGC(tc.meta)) + }) + } +} + +func TestCompatibleTxnSafePointPath(t *testing.T) { + testCases := []struct { + name string + meta *keyspacepb.KeyspaceMeta + want string + }{ + { + name: "null keyspace", + want: unifiedTxnSafePointPath, + }, + { + name: "native keyspace-level GC", + meta: &keyspacepb.KeyspaceMeta{Id: 1, Config: map[string]string{"gc_management_type": "keyspace_level"}}, + want: "/keyspaces/tidb/1/tidb/store/gcworker/saved_safe_point", + }, + { + name: "CES keyspace-level GC", + meta: &keyspacepb.KeyspaceMeta{Id: 2, Config: map[string]string{"safe_point_version": "v2"}}, + want: "/keyspaces/tidb/2/tidb/store/gcworker/saved_safe_point", + }, + { + name: "unified GC", + meta: &keyspacepb.KeyspaceMeta{Id: 3, Config: map[string]string{"gc_management_type": "unified"}}, + want: unifiedTxnSafePointPath, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.want, compatibleTxnSafePointPath(tc.meta)) + }) + } +} From e8984187ef67ec212fbb0ad816a5e0ee80bd16ab Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Wed, 5 Aug 2026 10:25:48 +0800 Subject: [PATCH 2/2] tikv: fix CSE keyspace-level GC naming Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus Signed-off-by: Ping Yu --- tikv/compatible_txn_safe_point_loader.go | 8 ++++---- tikv/compatible_txn_safe_point_loader_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tikv/compatible_txn_safe_point_loader.go b/tikv/compatible_txn_safe_point_loader.go index ad4884c5ce..85e4510fb6 100644 --- a/tikv/compatible_txn_safe_point_loader.go +++ b/tikv/compatible_txn_safe_point_loader.go @@ -24,14 +24,14 @@ const ( keyspaceLevelTxnSafePointPath = "/keyspaces/tidb/%d/tidb/store/gcworker/saved_safe_point" ) -// IsCESKeyspaceLevelGC reports whether a keyspace uses the CES keyspace-level GC metadata format. -// TODO: Replace this implementation with pd.IsCESKeyspaceLevelGC after API v3 support is merged into client-go. -func IsCESKeyspaceLevelGC(keyspaceMeta *keyspacepb.KeyspaceMeta) bool { +// IsCSEKeyspaceLevelGC reports whether a keyspace uses the CSE keyspace-level GC metadata format. +// TODO: Replace this implementation with pd.IsCSEKeyspaceLevelGC after API v3 support is merged into client-go. +func IsCSEKeyspaceLevelGC(keyspaceMeta *keyspacepb.KeyspaceMeta) bool { return keyspaceMeta != nil && keyspaceMeta.Config != nil && keyspaceMeta.Config["safe_point_version"] == "v2" } func compatibleTxnSafePointPath(keyspaceMeta *keyspacepb.KeyspaceMeta) string { - if pd.IsKeyspaceUsingKeyspaceLevelGC(keyspaceMeta) || IsCESKeyspaceLevelGC(keyspaceMeta) { + if pd.IsKeyspaceUsingKeyspaceLevelGC(keyspaceMeta) || IsCSEKeyspaceLevelGC(keyspaceMeta) { return fmt.Sprintf(keyspaceLevelTxnSafePointPath, keyspaceMeta.Id) } return unifiedTxnSafePointPath diff --git a/tikv/compatible_txn_safe_point_loader_test.go b/tikv/compatible_txn_safe_point_loader_test.go index 36ae2a9f5e..fb927a103d 100644 --- a/tikv/compatible_txn_safe_point_loader_test.go +++ b/tikv/compatible_txn_safe_point_loader_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestIsCESKeyspaceLevelGC(t *testing.T) { +func TestIsCSEKeyspaceLevelGC(t *testing.T) { testCases := []struct { name string meta *keyspacepb.KeyspaceMeta @@ -15,13 +15,13 @@ func TestIsCESKeyspaceLevelGC(t *testing.T) { }{ {name: "nil keyspace"}, {name: "missing config", meta: &keyspacepb.KeyspaceMeta{}}, - {name: "CES keyspace-level GC", meta: &keyspacepb.KeyspaceMeta{Config: map[string]string{"safe_point_version": "v2"}}, want: true}, + {name: "CSE keyspace-level GC", meta: &keyspacepb.KeyspaceMeta{Config: map[string]string{"safe_point_version": "v2"}}, want: true}, {name: "case-sensitive version", meta: &keyspacepb.KeyspaceMeta{Config: map[string]string{"safe_point_version": "V2"}}}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.want, IsCESKeyspaceLevelGC(tc.meta)) + require.Equal(t, tc.want, IsCSEKeyspaceLevelGC(tc.meta)) }) } } @@ -42,7 +42,7 @@ func TestCompatibleTxnSafePointPath(t *testing.T) { want: "/keyspaces/tidb/1/tidb/store/gcworker/saved_safe_point", }, { - name: "CES keyspace-level GC", + name: "CSE keyspace-level GC", meta: &keyspacepb.KeyspaceMeta{Id: 2, Config: map[string]string{"safe_point_version": "v2"}}, want: "/keyspaces/tidb/2/tidb/store/gcworker/saved_safe_point", },