Skip to content

Commit 9f20d1c

Browse files
committed
cli: add lock table key support to debug decode-key
The debug decode-key command previously only supported MVCC keys and unversioned user keys. This change adds support for decoding lock table keys by using DecodeEngineKey to determine key type and handling lock table keys with ToLockTableKey(). Test cases added for both Intent and Shared lock strengths. Fixes: #114425 Release Notes: None
1 parent 34a67e1 commit 9f20d1c

File tree

3 files changed

+140
-4
lines changed

3 files changed

+140
-4
lines changed

pkg/cli/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ go_test(
420420
"//pkg/kv/kvclient/kvtenant",
421421
"//pkg/kv/kvpb",
422422
"//pkg/kv/kvserver",
423+
"//pkg/kv/kvserver/concurrency/lock",
423424
"//pkg/kv/kvserver/liveness",
424425
"//pkg/kv/kvserver/liveness/livenesspb",
425426
"//pkg/kv/kvserver/loqrecovery",
@@ -456,6 +457,7 @@ go_test(
456457
"//pkg/ts/tspb",
457458
"//pkg/util",
458459
"//pkg/util/envutil",
460+
"//pkg/util/hlc",
459461
"//pkg/util/ioctx",
460462
"//pkg/util/leaktest",
461463
"//pkg/util/log",

pkg/cli/debug.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -660,11 +660,35 @@ For example:
660660
if decodeKeyOptions.userKey {
661661
fmt.Println(roachpb.Key(b))
662662
} else {
663-
k, err := storage.DecodeMVCCKey(b)
664-
if err != nil {
665-
return err
663+
// Try to decode as an EngineKey first to determine the key type.
664+
engineKey, ok := storage.DecodeEngineKey(b)
665+
if ok {
666+
if engineKey.IsLockTableKey() {
667+
// Decode as a lock table key.
668+
lockKey, err := engineKey.ToLockTableKey()
669+
if err != nil {
670+
return err
671+
}
672+
fmt.Println(lockKey)
673+
} else if engineKey.IsMVCCKey() {
674+
// Decode as an MVCC key.
675+
mvccKey, err := engineKey.ToMVCCKey()
676+
if err != nil {
677+
return err
678+
}
679+
fmt.Println(mvccKey)
680+
} else {
681+
// Print the engine key as-is.
682+
fmt.Println(engineKey)
683+
}
684+
} else {
685+
// Fall back to trying MVCC key decoding for backwards compatibility.
686+
k, err := storage.DecodeMVCCKey(b)
687+
if err != nil {
688+
return err
689+
}
690+
fmt.Println(k)
666691
}
667-
fmt.Println(k)
668692
}
669693
}
670694
return nil

pkg/cli/debug_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/cockroachdb/cockroach/pkg/base"
1818
"github.com/cockroachdb/cockroach/pkg/clusterversion"
1919
"github.com/cockroachdb/cockroach/pkg/gossip"
20+
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
2021
"github.com/cockroachdb/cockroach/pkg/roachpb"
2122
"github.com/cockroachdb/cockroach/pkg/server"
2223
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
@@ -25,9 +26,11 @@ import (
2526
"github.com/cockroachdb/cockroach/pkg/testutils"
2627
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
2728
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
29+
"github.com/cockroachdb/cockroach/pkg/util/hlc"
2830
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
2931
"github.com/cockroachdb/cockroach/pkg/util/log"
3032
"github.com/cockroachdb/cockroach/pkg/util/stop"
33+
"github.com/cockroachdb/cockroach/pkg/util/uuid"
3134
)
3235

3336
func createStore(t *testing.T, path string) {
@@ -136,3 +139,110 @@ func TestParsePositiveDuration(t *testing.T) {
136139
t.Errorf("Expected to fail parsing negative duration -5m")
137140
}
138141
}
142+
143+
// TestDebugDecodeKeyEngineKeyPaths tests the new engine key decoding logic in debug.go
144+
func TestDebugDecodeKeyEngineKeyPaths(t *testing.T) {
145+
defer leaktest.AfterTest(t)()
146+
defer log.Scope(t).Close(t)
147+
148+
t.Run("lock_table_keys", func(t *testing.T) {
149+
// Create lock table keys and test they decode properly
150+
uuid1 := uuid.Must(uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
151+
152+
lockKeys := []storage.LockTableKey{
153+
{Key: roachpb.Key("test"), Strength: lock.Shared, TxnUUID: uuid1},
154+
{Key: roachpb.Key("foo"), Strength: lock.Exclusive, TxnUUID: uuid1},
155+
{Key: roachpb.Key("bar"), Strength: lock.Intent, TxnUUID: uuid1},
156+
}
157+
158+
for i, lockKey := range lockKeys {
159+
t.Run(fmt.Sprintf("lock_%d_%s", i, lockKey.Strength), func(t *testing.T) {
160+
engineKey, _ := lockKey.ToEngineKey(nil)
161+
encoded := engineKey.Encode()
162+
hexKey := fmt.Sprintf("%x", encoded)
163+
164+
out, err := TestCLI{}.RunWithCaptureArgs([]string{
165+
"debug", "decode-key", "--encoding", "hex", hexKey,
166+
})
167+
168+
if err != nil {
169+
t.Fatalf("Unexpected error: %v", err)
170+
}
171+
172+
// Verify the output contains lock strength and key
173+
if !strings.Contains(out, lockKey.Strength.String()) {
174+
t.Errorf("Expected output to contain lock strength %s, got: %s", lockKey.Strength, out)
175+
}
176+
if !strings.Contains(out, string(lockKey.Key)) {
177+
t.Errorf("Expected output to contain key %s, got: %s", lockKey.Key, out)
178+
}
179+
})
180+
}
181+
})
182+
183+
t.Run("mvcc_keys_via_engine_key", func(t *testing.T) {
184+
// Test MVCC keys that are decoded via the EngineKey.IsMVCCKey() path
185+
mvccKey := storage.MVCCKey{
186+
Key: roachpb.Key("mvcc_test"),
187+
Timestamp: hlc.Timestamp{WallTime: 123456},
188+
}
189+
190+
encoded := storage.EncodeMVCCKey(mvccKey)
191+
hexKey := fmt.Sprintf("%x", encoded)
192+
193+
out, err := TestCLI{}.RunWithCaptureArgs([]string{
194+
"debug", "decode-key", "--encoding", "hex", hexKey,
195+
})
196+
197+
if err != nil {
198+
t.Fatalf("Unexpected error: %v", err)
199+
}
200+
201+
if !strings.Contains(out, "mvcc_test") {
202+
t.Errorf("Expected output to contain key content, got: %s", out)
203+
}
204+
})
205+
206+
t.Run("engine_key_other_types", func(t *testing.T) {
207+
// Test engine keys that are neither lock table nor MVCC keys
208+
// These should be printed as-is via the "else" branch
209+
// For now, we'll test with a simple case that should trigger this path
210+
211+
// This will test the fallback path when DecodeEngineKey returns false
212+
invalidEngineKey := "ff"
213+
214+
out, err := TestCLI{}.RunWithCaptureArgs([]string{
215+
"debug", "decode-key", "--encoding", "hex", invalidEngineKey,
216+
})
217+
218+
if err != nil {
219+
t.Fatalf("Unexpected error: %v", err)
220+
}
221+
222+
// Should trigger the MVCC fallback path and show an error
223+
if !strings.Contains(out, "ERROR") {
224+
t.Errorf("Expected output to contain error from MVCC fallback, got: %s", out)
225+
}
226+
})
227+
228+
t.Run("backwards_compatibility_fallback", func(t *testing.T) {
229+
// Test the fallback path: when DecodeEngineKey fails,
230+
// it should fall back to DecodeMVCCKey for backwards compatibility
231+
232+
// Use the same test case from existing tests that should trigger fallback
233+
invalidKey := "jg==" // base64 encoded "8e"
234+
235+
out, err := TestCLI{}.RunWithCaptureArgs([]string{
236+
"debug", "decode-key", "--encoding", "base64", invalidKey,
237+
})
238+
239+
if err != nil {
240+
t.Fatalf("Unexpected error: %v", err)
241+
}
242+
243+
// Should contain error message from MVCC key decoding fallback
244+
if !strings.Contains(out, "ERROR: invalid encoded mvcc key") {
245+
t.Errorf("Expected fallback to MVCC decoding with error message, got: %s", out)
246+
}
247+
})
248+
}

0 commit comments

Comments
 (0)