diff --git a/changelog/13286.txt b/changelog/13286.txt new file mode 100644 index 000000000..9d39be65f --- /dev/null +++ b/changelog/13286.txt @@ -0,0 +1,3 @@ +```release-note:bug +storage/raft: Fix a panic when trying to store a key > 32KB in a transaction. +``` diff --git a/physical/raft/raft.go b/physical/raft/raft.go index 8d3621862..f88d66c5b 100644 --- a/physical/raft/raft.go +++ b/physical/raft/raft.go @@ -1352,6 +1352,9 @@ func (b *RaftBackend) Transaction(ctx context.Context, txns []*physical.TxnEntry op := &LogOperation{} switch txn.Operation { case physical.PutOperation: + if len(txn.Entry.Key) > bolt.MaxKeySize { + return fmt.Errorf("%s, max key size for integrated storage is %d", physical.ErrKeyTooLarge, bolt.MaxKeySize) + } op.OpType = putOp op.Key = txn.Entry.Key op.Value = txn.Entry.Value diff --git a/physical/raft/raft_test.go b/physical/raft/raft_test.go index f6211d0b7..258d23bc6 100644 --- a/physical/raft/raft_test.go +++ b/physical/raft/raft_test.go @@ -280,6 +280,45 @@ func TestRaft_Backend_LargeValue(t *testing.T) { } } +func TestRaft_TransactionalBackend_LargeKey(t *testing.T) { + b, dir := getRaft(t, true, true) + defer os.RemoveAll(dir) + + value := make([]byte, defaultMaxEntrySize+1) + rand.Read(value) + + key, err := base62.Random(bolt.MaxKeySize + 1) + if err != nil { + t.Fatal(err) + } + txns := []*physical.TxnEntry{ + { + Operation: physical.PutOperation, + Entry: &physical.Entry{ + Key: key, + Value: []byte(key), + }, + }, + } + + err = b.Transaction(context.Background(), txns) + if err == nil { + t.Fatal("expected error for transactions") + } + + if !strings.Contains(err.Error(), physical.ErrKeyTooLarge) { + t.Fatalf("expected %q, got %v", physical.ErrValueTooLarge, err) + } + + out, err := b.Get(context.Background(), txns[0].Entry.Key) + if err != nil { + t.Fatalf("unexpected error after failed put: %v", err) + } + if out != nil { + t.Fatal("expected response entry to be nil after a failed put") + } +} + func TestRaft_TransactionalBackend_LargeValue(t *testing.T) { b, dir := getRaft(t, true, true) defer os.RemoveAll(dir)