From 05672cfea295d0b1f588572683e113f7f838d34d Mon Sep 17 00:00:00 2001 From: Daniil Porokhnin Date: Sun, 18 Jan 2026 13:33:12 +0300 Subject: [PATCH 1/5] perf: store decoded rids --- frac/fraction_concurrency_test.go | 2 +- frac/fraction_test.go | 6 ++-- frac/index_cache.go | 2 +- frac/sealed/seqids/loader.go | 52 ++++++++++++++++++++++--------- frac/sealed/seqids/provider.go | 2 +- fracmanager/cache_maintainer.go | 2 +- 6 files changed, 44 insertions(+), 22 deletions(-) diff --git a/frac/fraction_concurrency_test.go b/frac/fraction_concurrency_test.go index 0155a682..cc2b840a 100644 --- a/frac/fraction_concurrency_test.go +++ b/frac/fraction_concurrency_test.go @@ -334,7 +334,7 @@ func seal(active *Active) (*Sealed, error) { } indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]byte](nil, nil), + RIDs: cache.NewCache[[]uint64](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), diff --git a/frac/fraction_test.go b/frac/fraction_test.go index d43b30ae..ddf68c9c 100644 --- a/frac/fraction_test.go +++ b/frac/fraction_test.go @@ -1617,7 +1617,7 @@ func (s *FractionTestSuite) newSealed(bulks ...[]string) *Sealed { indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]byte](nil, nil), + RIDs: cache.NewCache[[]uint64](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), @@ -1805,7 +1805,7 @@ func (s *SealedLoadedFractionTestSuite) newSealedLoaded(bulks ...[]string) *Seal indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]byte](nil, nil), + RIDs: cache.NewCache[[]uint64](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), @@ -1872,7 +1872,7 @@ func (s *RemoteFractionTestSuite) SetupTest() { indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]byte](nil, nil), + RIDs: cache.NewCache[[]uint64](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), diff --git a/frac/index_cache.go b/frac/index_cache.go index 3dc5b3a4..7e65ca6c 100644 --- a/frac/index_cache.go +++ b/frac/index_cache.go @@ -10,7 +10,7 @@ import ( type IndexCache struct { Registry *cache.Cache[[]byte] MIDs *cache.Cache[[]byte] - RIDs *cache.Cache[[]byte] + RIDs *cache.Cache[[]uint64] Params *cache.Cache[seqids.BlockParams] Tokens *cache.Cache[*token.Block] TokenTable *cache.Cache[token.Table] diff --git a/frac/sealed/seqids/loader.go b/frac/sealed/seqids/loader.go index 1928e182..633d75db 100644 --- a/frac/sealed/seqids/loader.go +++ b/frac/sealed/seqids/loader.go @@ -2,6 +2,7 @@ package seqids import ( "errors" + "unsafe" "github.com/ozontech/seq-db/cache" "github.com/ozontech/seq-db/config" @@ -29,7 +30,7 @@ type Loader struct { reader *storage.IndexReader table *Table cacheMIDs *cache.Cache[[]byte] - cacheRIDs *cache.Cache[[]byte] + cacheRIDs *cache.Cache[[]uint64] cacheParams *cache.Cache[BlockParams] fracVersion config.BinaryDataVersion } @@ -56,27 +57,48 @@ func (l *Loader) GetMIDsBlock(index uint32, buf []uint64) (BlockMIDs, error) { } func (l *Loader) GetRIDsBlock(index uint32, buf []uint64) (BlockRIDs, error) { - // load binary from index - data, err := l.cacheRIDs.GetWithError(index, func() ([]byte, int, error) { + data, err := l.cacheRIDs.GetWithError(index, func() ([]uint64, int, error) { data, _, err := l.reader.ReadIndexBlock(l.ridBlockIndex(index), nil) - return data, cap(data), err + if err != nil { + return nil, 0, err + } + + length := len(data) / int(unsafe.Sizeof(uint64(0))) + cached := make([]uint64, length) + + block := BlockRIDs{ + fracVersion: l.fracVersion, + Values: cached, + } + + err = block.Unpack(data) + // fmt.Printf("len(block.Values): %v\n", len(block.Values)) + return block.Values, cap(block.Values), err }) - // check errors - if err == nil && len(data) == 0 { - err = errors.New("empty block") - } + if err != nil { return BlockRIDs{}, err } - // unpack - block := BlockRIDs{ - fracVersion: l.fracVersion, - Values: buf, + + if len(data) == 0 { + err = errors.New("empty block") } - if err := block.Unpack(data); err != nil { - return BlockRIDs{}, err + + // fmt.Printf("[before] len(buf): %v\n", len(buf)) + // fmt.Printf("[before] cap(buf): %v\n", cap(buf)) + + buf = buf[:0] // Why? + for i := range len(data) { + buf = append(buf, data[i]) } - return block, nil + + // fmt.Printf("[after] len(buf): %v\n", len(buf)) + // fmt.Printf("[after] cap(buf): %v\n", cap(buf)) + + return BlockRIDs{ + fracVersion: l.fracVersion, + Values: buf, + }, nil } func (l *Loader) GetParamsBlock(index uint32) (BlockParams, error) { diff --git a/frac/sealed/seqids/provider.go b/frac/sealed/seqids/provider.go index dff698c9..eea48512 100644 --- a/frac/sealed/seqids/provider.go +++ b/frac/sealed/seqids/provider.go @@ -18,7 +18,7 @@ type Provider struct { func NewProvider( indexReader *storage.IndexReader, cacheMIDs *cache.Cache[[]byte], - cacheRIDs *cache.Cache[[]byte], + cacheRIDs *cache.Cache[[]uint64], cacheParams *cache.Cache[BlockParams], table *Table, fracVersion config.BinaryDataVersion, diff --git a/fracmanager/cache_maintainer.go b/fracmanager/cache_maintainer.go index 5139b4ca..8991d409 100644 --- a/fracmanager/cache_maintainer.go +++ b/fracmanager/cache_maintainer.go @@ -144,7 +144,7 @@ func (cm *CacheMaintainer) CreateSortDocsCache() *cache.Cache[[]byte] { func (cm *CacheMaintainer) CreateIndexCache() *frac.IndexCache { return &frac.IndexCache{ MIDs: newCache[[]byte](cm, midsName), - RIDs: newCache[[]byte](cm, ridsName), + RIDs: newCache[[]uint64](cm, ridsName), Params: newCache[seqids.BlockParams](cm, paramsName), LIDs: newCache[*lids.Block](cm, lidsName), Tokens: newCache[*token.Block](cm, tokensName), From 23f21ead53f84241101f58e1cc2a79892ea00f13 Mon Sep 17 00:00:00 2001 From: Daniil Porokhnin Date: Sun, 18 Jan 2026 18:25:49 +0300 Subject: [PATCH 2/5] refactor: cleanup code --- frac/fraction_concurrency_test.go | 2 +- frac/fraction_test.go | 6 +++--- frac/index_cache.go | 2 +- frac/sealed/seqids/loader.go | 26 ++++++++++---------------- frac/sealed/seqids/provider.go | 2 +- fracmanager/cache_maintainer.go | 2 +- 6 files changed, 17 insertions(+), 23 deletions(-) diff --git a/frac/fraction_concurrency_test.go b/frac/fraction_concurrency_test.go index cc2b840a..a1d36957 100644 --- a/frac/fraction_concurrency_test.go +++ b/frac/fraction_concurrency_test.go @@ -334,7 +334,7 @@ func seal(active *Active) (*Sealed, error) { } indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]uint64](nil, nil), + RIDs: cache.NewCache[seqids.BlockRIDs](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), diff --git a/frac/fraction_test.go b/frac/fraction_test.go index ddf68c9c..ff4ac602 100644 --- a/frac/fraction_test.go +++ b/frac/fraction_test.go @@ -1617,7 +1617,7 @@ func (s *FractionTestSuite) newSealed(bulks ...[]string) *Sealed { indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]uint64](nil, nil), + RIDs: cache.NewCache[seqids.BlockRIDs](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), @@ -1805,7 +1805,7 @@ func (s *SealedLoadedFractionTestSuite) newSealedLoaded(bulks ...[]string) *Seal indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]uint64](nil, nil), + RIDs: cache.NewCache[seqids.BlockRIDs](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), @@ -1872,7 +1872,7 @@ func (s *RemoteFractionTestSuite) SetupTest() { indexCache := &IndexCache{ MIDs: cache.NewCache[[]byte](nil, nil), - RIDs: cache.NewCache[[]uint64](nil, nil), + RIDs: cache.NewCache[seqids.BlockRIDs](nil, nil), Params: cache.NewCache[seqids.BlockParams](nil, nil), LIDs: cache.NewCache[*lids.Block](nil, nil), Tokens: cache.NewCache[*token.Block](nil, nil), diff --git a/frac/index_cache.go b/frac/index_cache.go index 7e65ca6c..4536fa22 100644 --- a/frac/index_cache.go +++ b/frac/index_cache.go @@ -10,7 +10,7 @@ import ( type IndexCache struct { Registry *cache.Cache[[]byte] MIDs *cache.Cache[[]byte] - RIDs *cache.Cache[[]uint64] + RIDs *cache.Cache[seqids.BlockRIDs] Params *cache.Cache[seqids.BlockParams] Tokens *cache.Cache[*token.Block] TokenTable *cache.Cache[token.Table] diff --git a/frac/sealed/seqids/loader.go b/frac/sealed/seqids/loader.go index 633d75db..aa5a74e1 100644 --- a/frac/sealed/seqids/loader.go +++ b/frac/sealed/seqids/loader.go @@ -30,7 +30,7 @@ type Loader struct { reader *storage.IndexReader table *Table cacheMIDs *cache.Cache[[]byte] - cacheRIDs *cache.Cache[[]uint64] + cacheRIDs *cache.Cache[BlockRIDs] cacheParams *cache.Cache[BlockParams] fracVersion config.BinaryDataVersion } @@ -57,13 +57,15 @@ func (l *Loader) GetMIDsBlock(index uint32, buf []uint64) (BlockMIDs, error) { } func (l *Loader) GetRIDsBlock(index uint32, buf []uint64) (BlockRIDs, error) { - data, err := l.cacheRIDs.GetWithError(index, func() ([]uint64, int, error) { + block, err := l.cacheRIDs.GetWithError(index, func() (BlockRIDs, int, error) { data, _, err := l.reader.ReadIndexBlock(l.ridBlockIndex(index), nil) if err != nil { - return nil, 0, err + return BlockRIDs{}, 0, err } - length := len(data) / int(unsafe.Sizeof(uint64(0))) + ui64 := int(unsafe.Sizeof(uint64(0))) + + length := len(data) / ui64 cached := make([]uint64, length) block := BlockRIDs{ @@ -72,29 +74,21 @@ func (l *Loader) GetRIDsBlock(index uint32, buf []uint64) (BlockRIDs, error) { } err = block.Unpack(data) - // fmt.Printf("len(block.Values): %v\n", len(block.Values)) - return block.Values, cap(block.Values), err + return block, cap(block.Values) * ui64, err }) if err != nil { return BlockRIDs{}, err } - if len(data) == 0 { + if len(block.Values) == 0 { err = errors.New("empty block") } - // fmt.Printf("[before] len(buf): %v\n", len(buf)) - // fmt.Printf("[before] cap(buf): %v\n", cap(buf)) - - buf = buf[:0] // Why? - for i := range len(data) { - buf = append(buf, data[i]) + for i := range len(block.Values) { + buf = append(buf, block.Values[i]) } - // fmt.Printf("[after] len(buf): %v\n", len(buf)) - // fmt.Printf("[after] cap(buf): %v\n", cap(buf)) - return BlockRIDs{ fracVersion: l.fracVersion, Values: buf, diff --git a/frac/sealed/seqids/provider.go b/frac/sealed/seqids/provider.go index eea48512..e9f0ca51 100644 --- a/frac/sealed/seqids/provider.go +++ b/frac/sealed/seqids/provider.go @@ -18,7 +18,7 @@ type Provider struct { func NewProvider( indexReader *storage.IndexReader, cacheMIDs *cache.Cache[[]byte], - cacheRIDs *cache.Cache[[]uint64], + cacheRIDs *cache.Cache[BlockRIDs], cacheParams *cache.Cache[BlockParams], table *Table, fracVersion config.BinaryDataVersion, diff --git a/fracmanager/cache_maintainer.go b/fracmanager/cache_maintainer.go index 8991d409..70e5f956 100644 --- a/fracmanager/cache_maintainer.go +++ b/fracmanager/cache_maintainer.go @@ -144,7 +144,7 @@ func (cm *CacheMaintainer) CreateSortDocsCache() *cache.Cache[[]byte] { func (cm *CacheMaintainer) CreateIndexCache() *frac.IndexCache { return &frac.IndexCache{ MIDs: newCache[[]byte](cm, midsName), - RIDs: newCache[[]uint64](cm, ridsName), + RIDs: newCache[seqids.BlockRIDs](cm, ridsName), Params: newCache[seqids.BlockParams](cm, paramsName), LIDs: newCache[*lids.Block](cm, lidsName), Tokens: newCache[*token.Block](cm, tokensName), From f89bbb766d3b445b9cb92e1147209dfb2bb049d7 Mon Sep 17 00:00:00 2001 From: Daniil Porokhnin Date: Mon, 19 Jan 2026 16:47:45 +0300 Subject: [PATCH 3/5] fix: do not omit empty block error --- frac/sealed/seqids/loader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frac/sealed/seqids/loader.go b/frac/sealed/seqids/loader.go index aa5a74e1..f9c44cfd 100644 --- a/frac/sealed/seqids/loader.go +++ b/frac/sealed/seqids/loader.go @@ -82,7 +82,7 @@ func (l *Loader) GetRIDsBlock(index uint32, buf []uint64) (BlockRIDs, error) { } if len(block.Values) == 0 { - err = errors.New("empty block") + return BlockRIDs{}, errors.New("empty block") } for i := range len(block.Values) { From d85189cfcbe3c3f1da3ac9af1225557cf161e2f2 Mon Sep 17 00:00:00 2001 From: Daniil Porokhnin Date: Tue, 20 Jan 2026 20:09:37 +0300 Subject: [PATCH 4/5] refactor: perform copy in a caller --- frac/sealed/seqids/loader.go | 52 ++++++++++++---------------------- frac/sealed/seqids/provider.go | 7 +++-- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/frac/sealed/seqids/loader.go b/frac/sealed/seqids/loader.go index f9c44cfd..30f43096 100644 --- a/frac/sealed/seqids/loader.go +++ b/frac/sealed/seqids/loader.go @@ -56,67 +56,51 @@ func (l *Loader) GetMIDsBlock(index uint32, buf []uint64) (BlockMIDs, error) { return block, nil } -func (l *Loader) GetRIDsBlock(index uint32, buf []uint64) (BlockRIDs, error) { - block, err := l.cacheRIDs.GetWithError(index, func() (BlockRIDs, int, error) { +func (l *Loader) GetRIDsBlock(index uint32) (BlockRIDs, error) { + return l.cacheRIDs.GetWithError(index, func() (BlockRIDs, int, error) { data, _, err := l.reader.ReadIndexBlock(l.ridBlockIndex(index), nil) if err != nil { return BlockRIDs{}, 0, err } - ui64 := int(unsafe.Sizeof(uint64(0))) - - length := len(data) / ui64 - cached := make([]uint64, length) - block := BlockRIDs{ fracVersion: l.fracVersion, - Values: cached, + Values: make([]uint64, 0, consts.IDsPerBlock), } err = block.Unpack(data) - return block, cap(block.Values) * ui64, err - }) - - if err != nil { - return BlockRIDs{}, err - } - - if len(block.Values) == 0 { - return BlockRIDs{}, errors.New("empty block") - } + if err != nil { + return BlockRIDs{}, 0, err + } - for i := range len(block.Values) { - buf = append(buf, block.Values[i]) - } + if len(block.Values) == 0 { + return BlockRIDs{}, 0, errors.New("empty block") + } - return BlockRIDs{ - fracVersion: l.fracVersion, - Values: buf, - }, nil + ui64 := int(unsafe.Sizeof(uint64(0))) + return block, cap(block.Values) * ui64, err + }) } func (l *Loader) GetParamsBlock(index uint32) (BlockParams, error) { - // load binary from index - block, err := l.cacheParams.GetWithError(index, func() (BlockParams, int, error) { + return l.cacheParams.GetWithError(index, func() (BlockParams, int, error) { data, _, err := l.reader.ReadIndexBlock(l.paramsBlockIndex(index), nil) if err != nil { return BlockParams{}, 0, err } - // unpack + block := BlockParams{Values: make([]uint64, 0, consts.IDsPerBlock)} if err := block.Unpack(data); err != nil { return BlockParams{}, 0, err } + if len(block.Values) == 0 { return BlockParams{}, 0, errors.New("empty block") } - return block, cap(block.Values) * 8, nil + + ui64 := int(unsafe.Sizeof(uint64(0))) + return block, cap(block.Values) * ui64, nil }) - // check errors - if err != nil { - return BlockParams{}, err - } - return block, nil } // blocks are stored as triplets on disk, (MID + RID + Pos), check docs/format-index-file.go diff --git a/frac/sealed/seqids/provider.go b/frac/sealed/seqids/provider.go index e9f0ca51..18a1c57c 100644 --- a/frac/sealed/seqids/provider.go +++ b/frac/sealed/seqids/provider.go @@ -76,13 +76,16 @@ func (p *Provider) RID(lid seq.LID) (seq.RID, error) { func (p *Provider) fillRIDs(blockIndex uint32, dst *unpackCache) error { if dst.blockIndex != int(blockIndex) { - block, err := p.loader.GetRIDsBlock(blockIndex, dst.values[:0]) + block, err := p.loader.GetRIDsBlock(blockIndex) if err != nil { return err } dst.blockIndex = int(blockIndex) dst.startLID = p.loader.table.BlockStartLID(blockIndex) - dst.values = block.Values + // we have to copy `block.Values` because we store them in `cache.Cache[BlockRIDs]`, + // but `dst *unpackCache` might put its `values` in sync.Pool on `release()`, and they + // will be reused and corrupted + dst.values = append(dst.values[:0], block.Values...) } return nil } From ad5f0a3601ff43723fc9d8f1c1bbff1c9dd25385 Mon Sep 17 00:00:00 2001 From: Daniil Porokhnin Date: Wed, 21 Jan 2026 11:08:07 +0300 Subject: [PATCH 5/5] refactor: add `const` modifier to `ui64` --- frac/sealed/seqids/loader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frac/sealed/seqids/loader.go b/frac/sealed/seqids/loader.go index 30f43096..a4c9ecdb 100644 --- a/frac/sealed/seqids/loader.go +++ b/frac/sealed/seqids/loader.go @@ -77,7 +77,7 @@ func (l *Loader) GetRIDsBlock(index uint32) (BlockRIDs, error) { return BlockRIDs{}, 0, errors.New("empty block") } - ui64 := int(unsafe.Sizeof(uint64(0))) + const ui64 = int(unsafe.Sizeof(uint64(0))) return block, cap(block.Values) * ui64, err }) } @@ -98,7 +98,7 @@ func (l *Loader) GetParamsBlock(index uint32) (BlockParams, error) { return BlockParams{}, 0, errors.New("empty block") } - ui64 := int(unsafe.Sizeof(uint64(0))) + const ui64 = int(unsafe.Sizeof(uint64(0))) return block, cap(block.Values) * ui64, nil }) }