Skip to content

Commit c1df891

Browse files
author
Denis Evdokimov
committed
feat: add buffer encoder and refactor
1 parent 34ce284 commit c1df891

File tree

6 files changed

+77
-65
lines changed

6 files changed

+77
-65
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
* Added the use of sync.Pool in EncoderMap and renamed it to MultiEncoder
1+
* Supported pool of encoders, which implement ResetableWriter interface
22

33
## v3.97.0
44
* Added immutable range iterators from go1.23 into query stats to iterate over query phases and accessed tables without query stats object mutation

internal/topic/topicwriterinternal/encoders.go

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package topicwriterinternal
22

33
import (
4+
"bytes"
45
"compress/gzip"
56
"fmt"
67
"io"
@@ -45,30 +46,49 @@ func newEncoderPool() *encoderPool {
4546
type MultiEncoder struct {
4647
m map[rawtopiccommon.Codec]PublicCreateEncoderFunc
4748

48-
mx sync.RWMutex
49-
p map[rawtopiccommon.Codec]*encoderPool
49+
bp xsync.Pool[bytes.Buffer]
50+
ep map[rawtopiccommon.Codec]*encoderPool
5051
}
5152

52-
func NewEncoderMap() *MultiEncoder {
53-
return &MultiEncoder{
54-
m: map[rawtopiccommon.Codec]PublicCreateEncoderFunc{
55-
rawtopiccommon.CodecRaw: func(writer io.Writer) (io.WriteCloser, error) {
56-
return nopWriteCloser{writer}, nil
57-
},
58-
rawtopiccommon.CodecGzip: func(writer io.Writer) (io.WriteCloser, error) {
59-
return gzip.NewWriter(writer), nil
53+
func NewMultiEncoder() *MultiEncoder {
54+
me := &MultiEncoder{
55+
m: make(map[rawtopiccommon.Codec]PublicCreateEncoderFunc),
56+
bp: xsync.Pool[bytes.Buffer]{
57+
New: func() *bytes.Buffer {
58+
return &bytes.Buffer{}
6059
},
6160
},
62-
mx: sync.RWMutex{},
63-
p: make(map[rawtopiccommon.Codec]*encoderPool),
61+
ep: make(map[rawtopiccommon.Codec]*encoderPool),
6462
}
63+
64+
me.AddEncoder(rawtopiccommon.CodecRaw, func(writer io.Writer) (io.WriteCloser, error) {
65+
return nopWriteCloser{writer}, nil
66+
})
67+
me.AddEncoder(rawtopiccommon.CodecGzip, func(writer io.Writer) (io.WriteCloser, error) {
68+
return gzip.NewWriter(writer), nil
69+
})
70+
71+
return me
6572
}
6673

6774
func (e *MultiEncoder) AddEncoder(codec rawtopiccommon.Codec, creator PublicCreateEncoderFunc) {
6875
e.m[codec] = creator
76+
e.ep[codec] = newEncoderPool()
77+
}
78+
79+
func (e *MultiEncoder) Encode(codec rawtopiccommon.Codec, target io.Writer, source io.Reader) (int, error) {
80+
buf := e.bp.GetOrNew()
81+
defer e.bp.Put(buf)
82+
83+
buf.Reset()
84+
if _, err := buf.ReadFrom(source); err != nil {
85+
return 0, err
86+
}
87+
88+
return e.EncodeBytes(codec, target, buf.Bytes())
6989
}
7090

71-
func (e *MultiEncoder) Encode(codec rawtopiccommon.Codec, target io.Writer, data []byte) (int, error) {
91+
func (e *MultiEncoder) EncodeBytes(codec rawtopiccommon.Codec, target io.Writer, data []byte) (int, error) {
7292
enc, err := e.createEncodeWriter(codec, target)
7393
if err != nil {
7494
return 0, err
@@ -82,27 +102,15 @@ func (e *MultiEncoder) Encode(codec rawtopiccommon.Codec, target io.Writer, data
82102
return 0, err
83103
}
84104

85-
resetableEnc, ok := enc.(PublicResetableWriter)
86-
if ok {
87-
e.mx.Lock()
88-
if _, ok := e.p[codec]; !ok {
89-
e.p[codec] = newEncoderPool()
90-
}
91-
e.mx.Unlock()
92-
93-
e.mx.RLock()
94-
e.p[codec].Put(resetableEnc)
95-
e.mx.RUnlock()
105+
if resetableEnc, ok := enc.(PublicResetableWriter); ok {
106+
e.ep[codec].Put(resetableEnc)
96107
}
97108

98109
return n, nil
99110
}
100111

101112
func (e *MultiEncoder) createEncodeWriter(codec rawtopiccommon.Codec, target io.Writer) (io.WriteCloser, error) {
102-
e.mx.RLock()
103-
defer e.mx.RUnlock()
104-
105-
if ePool, ok := e.p[codec]; ok {
113+
if ePool, ok := e.ep[codec]; ok {
106114
wr := ePool.Get()
107115
if wr != nil {
108116
wr.Reset(target)

internal/topic/topicwriterinternal/encoders_test.go

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestEncoderSelector_CodecMeasure(t *testing.T) {
2323
})
2424
t.Run("One", func(t *testing.T) {
2525
s := NewEncoderSelector(
26-
NewEncoderMap(),
26+
NewMultiEncoder(),
2727
rawtopiccommon.SupportedCodecs{rawtopiccommon.CodecRaw},
2828
1,
2929
&trace.Topic{},
@@ -210,7 +210,7 @@ func TestCompressMessages(t *testing.T) {
210210
})
211211
}
212212

213-
func TestEncodersPool(t *testing.T) {
213+
func TestMultiEncoder(t *testing.T) {
214214
decompressGzip := func(rd io.Reader) string {
215215
gzreader, err := gzip.NewReader(rd)
216216
require.NoError(t, err)
@@ -221,67 +221,77 @@ func TestEncodersPool(t *testing.T) {
221221
return string(decompressedMsg)
222222
}
223223

224+
t.Run("BuffersPool", func(t *testing.T) {
225+
testMultiEncoder := NewMultiEncoder()
226+
227+
buf := &bytes.Buffer{}
228+
for i := 0; i < 50; i++ {
229+
testMsg := []byte(fmt.Sprintf("test_data_%d", i))
230+
231+
buf.Reset()
232+
_, err := testMultiEncoder.Encode(rawtopiccommon.CodecGzip, buf, bytes.NewReader(testMsg))
233+
require.NoError(t, err)
234+
235+
require.Equal(t, string(testMsg), decompressGzip(buf))
236+
}
237+
})
238+
224239
t.Run("NotResetableWriter", func(t *testing.T) {
225-
testEncoderMap := NewEncoderMap()
240+
testMultiEncoder := NewMultiEncoder()
241+
require.Len(t, testMultiEncoder.ep, 2)
226242

227243
buf := &bytes.Buffer{}
228-
_, err := testEncoderMap.Encode(rawtopiccommon.CodecRaw, buf, []byte("test_data"))
244+
_, err := testMultiEncoder.EncodeBytes(rawtopiccommon.CodecRaw, buf, []byte("test_data"))
229245
require.NoError(t, err)
230-
require.Len(t, testEncoderMap.p, 0)
231246
require.Equal(t, "test_data", buf.String())
232247
})
233248

234249
t.Run("ResetableWriterCustom", func(t *testing.T) {
235-
testEncoderMap := NewEncoderMap()
250+
testMultiEncoder := NewMultiEncoder()
236251

237-
customCodecCode := rawtopiccommon.Codec(5)
238-
testEncoderMap.AddEncoder(customCodecCode, func(writer io.Writer) (io.WriteCloser, error) {
252+
customCodecCode := rawtopiccommon.Codec(10001)
253+
testMultiEncoder.AddEncoder(customCodecCode, func(writer io.Writer) (io.WriteCloser, error) {
239254
return gzip.NewWriter(writer), nil
240255
})
256+
require.Len(t, testMultiEncoder.ep, 3)
241257

242258
buf := &bytes.Buffer{}
243-
_, err := testEncoderMap.Encode(customCodecCode, buf, []byte("test_data_1"))
259+
_, err := testMultiEncoder.EncodeBytes(customCodecCode, buf, []byte("test_data_1"))
244260
require.NoError(t, err)
245-
require.Len(t, testEncoderMap.p, 1)
246261
require.Equal(t, "test_data_1", decompressGzip(buf))
247262

248263
buf.Reset()
249-
_, err = testEncoderMap.Encode(rawtopiccommon.CodecGzip, buf, []byte("test_data_2"))
264+
_, err = testMultiEncoder.EncodeBytes(rawtopiccommon.CodecGzip, buf, []byte("test_data_2"))
250265
require.NoError(t, err)
251-
require.Len(t, testEncoderMap.p, 2)
252266
require.Equal(t, "test_data_2", decompressGzip(buf))
253267
})
254268

255269
t.Run("ResetableWriter", func(t *testing.T) {
256-
testEncoderMap := NewEncoderMap()
270+
testMultiEncoder := NewMultiEncoder()
257271

258272
buf := &bytes.Buffer{}
259-
_, err := testEncoderMap.Encode(rawtopiccommon.CodecGzip, buf, []byte("test_data_1"))
273+
_, err := testMultiEncoder.EncodeBytes(rawtopiccommon.CodecGzip, buf, []byte("test_data_1"))
260274
require.NoError(t, err)
261-
require.Len(t, testEncoderMap.p, 1)
262275
require.Equal(t, "test_data_1", decompressGzip(buf))
263276

264277
buf.Reset()
265-
_, err = testEncoderMap.Encode(rawtopiccommon.CodecGzip, buf, []byte("test_data_2"))
278+
_, err = testMultiEncoder.EncodeBytes(rawtopiccommon.CodecGzip, buf, []byte("test_data_2"))
266279
require.NoError(t, err)
267-
require.Len(t, testEncoderMap.p, 1)
268280
require.Equal(t, "test_data_2", decompressGzip(buf))
269281
})
270282

271283
t.Run("ResetableWriterManyMessages", func(t *testing.T) {
272-
testEncoderMap := NewEncoderMap()
284+
testMultiEncoder := NewMultiEncoder()
273285

274286
buf := &bytes.Buffer{}
275287
for i := 0; i < 50; i++ {
276-
testMsg := []byte(fmt.Sprintf("data_%d", i))
288+
testMsg := []byte(fmt.Sprintf("test_data_%d", i))
277289

278290
buf.Reset()
279-
_, err := testEncoderMap.Encode(rawtopiccommon.CodecGzip, buf, testMsg)
291+
_, err := testMultiEncoder.EncodeBytes(rawtopiccommon.CodecGzip, buf, testMsg)
280292
require.NoError(t, err)
281293

282294
require.Equal(t, string(testMsg), decompressGzip(buf))
283295
}
284-
285-
require.Len(t, testEncoderMap.p, 1)
286296
})
287297
}

internal/topic/topicwriterinternal/message.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (m *messageWithDataContent) encodeRawContent(codec rawtopiccommon.Codec) ([
111111

112112
m.bufEncoded.Reset()
113113

114-
_, err := m.encoders.Encode(codec, &m.bufEncoded, m.rawBuf.Bytes())
114+
_, err := m.encoders.EncodeBytes(codec, &m.bufEncoded, m.rawBuf.Bytes())
115115
if err != nil {
116116
return nil, xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf(
117117
"ydb: failed to compress message, codec '%v': %w",
@@ -151,13 +151,7 @@ func (m *messageWithDataContent) readDataToTargetCodec(codec rawtopiccommon.Code
151151
reader = &bytes.Reader{}
152152
}
153153

154-
buf := &bytes.Buffer{}
155-
_, err := buf.ReadFrom(reader)
156-
if err != nil {
157-
return err
158-
}
159-
160-
bytesCount, err := m.encoders.Encode(codec, &m.bufEncoded, buf.Bytes())
154+
bytesCount, err := m.encoders.Encode(codec, &m.bufEncoded, reader)
161155
if err != nil {
162156
return xerrors.WithStackTrace(xerrors.Wrap(fmt.Errorf(
163157
"ydb: failed compress message with codec '%v': %w",

internal/topic/topicwriterinternal/writer_reconnector.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func newWriterReconnectorStopped(
156156
queue: newMessageQueue(),
157157
lastSeqNo: -1,
158158
firstInitResponseProcessedChan: make(empty.Chan),
159-
encodersMap: NewEncoderMap(),
159+
encodersMap: NewMultiEncoder(),
160160
writerInstanceID: writerInstanceID.String(),
161161
retrySettings: cfg.RetrySettings,
162162
}
@@ -760,11 +760,11 @@ func createRawMessageData(
760760
return res, err
761761
}
762762

763-
func calculateAllowedCodecs(forceCodec rawtopiccommon.Codec, encoderMap *MultiEncoder,
763+
func calculateAllowedCodecs(forceCodec rawtopiccommon.Codec, multiEncoder *MultiEncoder,
764764
serverCodecs rawtopiccommon.SupportedCodecs,
765765
) rawtopiccommon.SupportedCodecs {
766766
if forceCodec != rawtopiccommon.CodecUNSPECIFIED {
767-
if serverCodecs.AllowedByCodecsList(forceCodec) && encoderMap.IsSupported(forceCodec) {
767+
if serverCodecs.AllowedByCodecsList(forceCodec) && multiEncoder.IsSupported(forceCodec) {
768768
return rawtopiccommon.SupportedCodecs{forceCodec}
769769
}
770770

@@ -779,7 +779,7 @@ func calculateAllowedCodecs(forceCodec rawtopiccommon.Codec, encoderMap *MultiEn
779779

780780
res := make(rawtopiccommon.SupportedCodecs, 0, len(serverCodecs))
781781
for _, codec := range serverCodecs {
782-
if encoderMap.IsSupported(codec) {
782+
if multiEncoder.IsSupported(codec) {
783783
res = append(res, codec)
784784
}
785785
}

internal/topic/topicwriterinternal/writer_reconnector_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
2727
)
2828

29-
var testCommonEncoders = NewEncoderMap()
29+
var testCommonEncoders = NewMultiEncoder()
3030

3131
func TestWriterImpl_AutoSeq(t *testing.T) {
3232
t.Run("OK", func(t *testing.T) {
@@ -829,7 +829,7 @@ func TestSplitMessagesByBufCodec(t *testing.T) {
829829
func TestCalculateAllowedCodecs(t *testing.T) {
830830
customCodecSupported := rawtopiccommon.Codec(rawtopiccommon.CodecCustomerFirst)
831831
customCodecUnsupported := rawtopiccommon.Codec(rawtopiccommon.CodecCustomerFirst + 1)
832-
encoders := NewEncoderMap()
832+
encoders := NewMultiEncoder()
833833
encoders.AddEncoder(customCodecSupported, func(writer io.Writer) (io.WriteCloser, error) {
834834
return nil, errors.New("test")
835835
})

0 commit comments

Comments
 (0)