From b05d75ab0b2a1cefed04f0590e97b52360439ee8 Mon Sep 17 00:00:00 2001 From: new-dream <111836360+new-dream@users.noreply.github.com> Date: Wed, 23 Nov 2022 22:43:15 +0800 Subject: [PATCH] server: optimizing memory overhead of copy operation in ConcurrentReadTxn Signed-off-by: new-dream <111836360+new-dream@users.noreply.github.com> --- server/storage/backend/tx_buffer.go | 12 ++-- server/storage/backend/tx_buffer_test.go | 92 ++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 server/storage/backend/tx_buffer_test.go diff --git a/server/storage/backend/tx_buffer.go b/server/storage/backend/tx_buffer.go index 779255b7320..7c2f9d63ac4 100644 --- a/server/storage/backend/tx_buffer.go +++ b/server/storage/backend/tx_buffer.go @@ -17,6 +17,8 @@ package backend import ( "bytes" "sort" + + "go.etcd.io/etcd/client/pkg/v3/verify" ) const bucketBufferInitialSize = 512 @@ -124,7 +126,7 @@ func (txr *txReadBuffer) unsafeCopy() txReadBuffer { bufVersion: 0, } for bucketName, bucket := range txr.txBuffer.buckets { - txrCopy.txBuffer.buckets[bucketName] = bucket.Copy() + txrCopy.txBuffer.buckets[bucketName] = bucket.CopyUsed() } return txrCopy } @@ -221,11 +223,13 @@ func (bb *bucketBuffer) Less(i, j int) bool { } func (bb *bucketBuffer) Swap(i, j int) { bb.buf[i], bb.buf[j] = bb.buf[j], bb.buf[i] } -func (bb *bucketBuffer) Copy() *bucketBuffer { +func (bb *bucketBuffer) CopyUsed() *bucketBuffer { + verify.Assert(bb.used <= len(bb.buf), + "used (%d) should never be bigger than the length of buf (%d)", bb.used, len(bb.buf)) bbCopy := bucketBuffer{ - buf: make([]kv, len(bb.buf)), + buf: make([]kv, bb.used), used: bb.used, } - copy(bbCopy.buf, bb.buf) + copy(bbCopy.buf, bb.buf[:bb.used]) return &bbCopy } diff --git a/server/storage/backend/tx_buffer_test.go b/server/storage/backend/tx_buffer_test.go new file mode 100644 index 00000000000..745b2a7ec9d --- /dev/null +++ b/server/storage/backend/tx_buffer_test.go @@ -0,0 +1,92 @@ +// Copyright 2023 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package backend + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_bucketBuffer_CopyUsed_After_Add(t *testing.T) { + bb := &bucketBuffer{buf: make([]kv, 10), used: 0} + for i := 0; i < 20; i++ { + k := fmt.Sprintf("key%d", i) + v := fmt.Sprintf("val%d", i) + bb.add([]byte(k), []byte(v)) + bbCopy := bb.CopyUsed() + assert.Equal(t, bb.used, bbCopy.used) + assert.Equal(t, bbCopy.used, len(bbCopy.buf)) + assert.GreaterOrEqual(t, len(bb.buf), len(bbCopy.buf)) + } +} + +func Test_bucketBuffer_CopyUsed(t *testing.T) { + tests := []struct { + name string + bufLen int + used int + wantPanic bool + wantUsed int + wantBufLen int + }{ + { + name: "used is 0", + bufLen: 10, + used: 0, + wantPanic: false, + wantUsed: 0, + wantBufLen: 0, + }, + { + name: "used is greater than 0 and less than len(buf)", + bufLen: 10, + used: 5, + wantPanic: false, + wantUsed: 5, + wantBufLen: 5, + }, + { + name: "used is equal to len(buf)", + bufLen: 10, + used: 10, + wantPanic: false, + wantUsed: 10, + wantBufLen: 10, + }, + { + name: "used is greater than len(buf)", + bufLen: 10, + used: 11, + wantPanic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bb := &bucketBuffer{buf: make([]kv, tt.bufLen), used: tt.used} + if tt.wantPanic { + assert.Panicsf(t, func() { + bb.CopyUsed() + }, "expected panic when used (%d) and the length of buf (%d)", tt.used, tt.bufLen) + } else { + bbCopy := bb.CopyUsed() + assert.Equal(t, tt.wantUsed, bbCopy.used) + assert.Equal(t, tt.wantBufLen, len(bbCopy.buf)) + } + }) + } +}