diff --git a/cmd/distribution/main.go b/cmd/distribution/main.go index 527c484ff..371e7bba7 100644 --- a/cmd/distribution/main.go +++ b/cmd/distribution/main.go @@ -48,7 +48,12 @@ func getReader(path string) (storage.IndexReader, *os.File) { if err != nil { panic(err) } - return storage.NewIndexReader(readLimiter, f.Name(), f, c), f + readerProvider := storage.NewReaderProvider(readLimiter, f.Name(), f, c) + reader, err := readerProvider.GetReader() + if err != nil { + panic(err) + } + return reader, f } func readBlock(reader storage.IndexReader, blockIndex uint32) ([]byte, error) { diff --git a/cmd/index_analyzer/main.go b/cmd/index_analyzer/main.go index b7422da41..dec9bf09a 100644 --- a/cmd/index_analyzer/main.go +++ b/cmd/index_analyzer/main.go @@ -118,8 +118,17 @@ func analyzeIndex( defer tokenFile.Close() defer lidFile.Close() - tokenReader := storage.NewIndexReader(rl, tokenFile.Name(), tokenFile, indexCache.TokenRegistry) - lidReader := storage.NewIndexReader(rl, lidFile.Name(), lidFile, indexCache.LIDRegistry) + tokenReaderProvider := storage.NewReaderProvider(rl, tokenFile.Name(), tokenFile, indexCache.TokenRegistry) + tokenReader, err := tokenReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating tokenReader", zap.String("file", tokenFile.Name()), zap.Error(err)) + } + + lidReaderPorvider := storage.NewReaderProvider(rl, lidFile.Name(), lidFile, indexCache.LIDRegistry) + lidReader, err := lidReaderPorvider.GetReader() + if err != nil { + logger.Fatal("error creating lidReader", zap.String("file", lidFile.Name()), zap.Error(err)) + } // --- Info --- var blockIndex uint32 diff --git a/frac/remote.go b/frac/remote.go index 9630b8ca6..4c531fab1 100644 --- a/frac/remote.go +++ b/frac/remote.go @@ -44,9 +44,9 @@ type Remote struct { docsReader storage.DocsReader // IsLegacy is true for fractions that use the old single .index file format. - IsLegacy bool - legacyFile storage.ImmutableFile - legacyReader storage.IndexReader + IsLegacy bool + legacyFile storage.ImmutableFile + legacyReaderProvider *storage.ReaderProvider // Per-section index files and their readers (new split format only). infoFile storage.ImmutableFile @@ -55,10 +55,10 @@ type Remote struct { idFile storage.ImmutableFile lidFile storage.ImmutableFile - tokenReader storage.IndexReader - offsetsReader storage.IndexReader - idReader storage.IndexReader - lidReader storage.IndexReader + tokenReaderProvider *storage.ReaderProvider + offsetsReaderProvider *storage.ReaderProvider + idReaderProvider *storage.ReaderProvider + lidReaderProvider *storage.ReaderProvider indexCache *IndexCache @@ -171,14 +171,34 @@ func (f *Remote) createDataProvider(ctx context.Context) (*sealedDataProvider, e return nil, err } - tokenReader := &f.tokenReader - lidReader := &f.lidReader - idReader := &f.idReader + var ( + tokenReader storage.IndexReader + lidReader storage.IndexReader + idReader storage.IndexReader + ) if f.IsLegacy { - tokenReader = &f.legacyReader - lidReader = &f.legacyReader - idReader = &f.legacyReader + legacyReader, err := f.legacyReaderProvider.GetReader() + if err != nil { + return nil, err + } + tokenReader = legacyReader + lidReader = legacyReader + idReader = legacyReader + } else { + var err error + tokenReader, err = f.tokenReaderProvider.GetReader() + if err != nil { + return nil, err + } + lidReader, err = f.lidReaderProvider.GetReader() + if err != nil { + return nil, err + } + idReader, err = f.idReaderProvider.GetReader() + if err != nil { + return nil, err + } } return &sealedDataProvider{ @@ -190,13 +210,13 @@ func (f *Remote) createDataProvider(ctx context.Context) (*sealedDataProvider, e docsReader: &f.docsReader, blocksOffsets: f.blocksData.BlocksOffsets, lidsTable: f.blocksData.LIDsTable, - lidsLoader: lids.NewLoader(f.info.BinaryDataVer, lidReader, f.indexCache.LIDs), - tokenBlockLoader: token.NewBlockLoader(f.BaseFileName, tokenReader, f.indexCache.Tokens), - tokenTableLoader: token.NewTableLoader(f.BaseFileName, f.IsLegacy, tokenReader, f.indexCache.TokenTable), + lidsLoader: lids.NewLoader(f.info.BinaryDataVer, &lidReader, f.indexCache.LIDs), + tokenBlockLoader: token.NewBlockLoader(f.BaseFileName, &tokenReader, f.indexCache.Tokens), + tokenTableLoader: token.NewTableLoader(f.BaseFileName, f.IsLegacy, &tokenReader, f.indexCache.TokenTable), idsTable: &f.blocksData.IDsTable, idsProvider: seqids.NewProvider( - idReader, + &idReader, f.indexCache.MIDs, f.indexCache.RIDs, f.indexCache.Params, @@ -259,7 +279,11 @@ func (f *Remote) loadInfo() error { if err := f.openInfoLegacy(); err != nil { return err } - if f.info, err = loadInfoLegacy(f.legacyReader); err != nil { + legacyReader, err := f.legacyReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating legacyReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + if f.info, err = loadInfoLegacy(legacyReader); err != nil { logger.Fatal("error loading Info", zap.String("fraction", f.BaseFileName), zap.Error(err)) } return nil @@ -292,16 +316,37 @@ func (f *Remote) init() error { } if f.IsLegacy { - (&LegacyLoader{}).Load(&f.blocksData, f.info, f.legacyReader) + legacyReader, err := f.legacyReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating legacyReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + (&LegacyLoader{}).Load(&f.blocksData, f.info, legacyReader) f.isInited = true return nil } + tokenReader, err := f.tokenReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating tokenReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + offsetsReader, err := f.offsetsReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating offsetsReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + idReader, err := f.idReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating idReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + lidReader, err := f.lidReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating lidReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + (&Loader{}).Load(&f.blocksData, f.info, IndexReaders{ - Token: f.tokenReader, - Offsets: f.offsetsReader, - ID: f.idReader, - LID: f.lidReader, + Token: tokenReader, + Offsets: offsetsReader, + ID: idReader, + LID: lidReader, }) f.isInited = true @@ -315,7 +360,7 @@ func (f *Remote) openInfoLegacy() error { return f.openRemoteFile(consts.IndexFileSuffix, func(file storage.ImmutableFile) { f.legacyFile = file - f.legacyReader = storage.NewIndexReader( + f.legacyReaderProvider = storage.NewReaderProvider( f.readLimiter, file.Name(), file, f.indexCache.LegacyRegistry, ) @@ -349,7 +394,7 @@ func (f *Remote) openIndex() error { consts.TokenFileSuffix, func(file storage.ImmutableFile) { f.tokenFile = file - f.tokenReader = storage.NewIndexReader( + f.tokenReaderProvider = storage.NewReaderProvider( f.readLimiter, file.Name(), file, f.indexCache.TokenRegistry, ) @@ -364,7 +409,7 @@ func (f *Remote) openIndex() error { consts.OffsetsFileSuffix, func(file storage.ImmutableFile) { f.offsetsFile = file - f.offsetsReader = storage.NewIndexReader( + f.offsetsReaderProvider = storage.NewReaderProvider( f.readLimiter, file.Name(), file, f.indexCache.OffsetsRegistry, ) @@ -379,7 +424,7 @@ func (f *Remote) openIndex() error { consts.IDFileSuffix, func(file storage.ImmutableFile) { f.idFile = file - f.idReader = storage.NewIndexReader( + f.idReaderProvider = storage.NewReaderProvider( f.readLimiter, file.Name(), file, f.indexCache.IDRegistry, ) @@ -394,7 +439,7 @@ func (f *Remote) openIndex() error { consts.LIDFileSuffix, func(file storage.ImmutableFile) { f.lidFile = file - f.lidReader = storage.NewIndexReader( + f.lidReaderProvider = storage.NewReaderProvider( f.readLimiter, file.Name(), file, f.indexCache.LIDRegistry, ) diff --git a/frac/sealed.go b/frac/sealed.go index 4bde6d5b9..576d397aa 100644 --- a/frac/sealed.go +++ b/frac/sealed.go @@ -40,9 +40,9 @@ type Sealed struct { docsReader storage.DocsReader // IsLegacy is true for fractions that use the old single .index file format. - IsLegacy bool - legacyFile *os.File - legacyReader storage.IndexReader + IsLegacy bool + legacyFile *os.File + legacyReaderProvider *storage.ReaderProvider // Per-section index files and their readers (new split format only). infoFile *os.File @@ -51,10 +51,10 @@ type Sealed struct { idFile *os.File lidFile *os.File - tokenReader storage.IndexReader - offsetsReader storage.IndexReader - idReader storage.IndexReader - lidReader storage.IndexReader + tokenReaderProvider *storage.ReaderProvider + offsetsReaderProvider *storage.ReaderProvider + idReaderProvider *storage.ReaderProvider + lidReaderProvider *storage.ReaderProvider blocksData sealed.BlocksData indexCache *IndexCache @@ -176,7 +176,7 @@ func (f *Sealed) openInfoLegacy() { } f.legacyFile = file - f.legacyReader = storage.NewIndexReader( + f.legacyReaderProvider = storage.NewReaderProvider( f.readLimiter, file.Name(), file, f.indexCache.LegacyRegistry, ) @@ -216,7 +216,7 @@ func (f *Sealed) openIndex() { logger.Fatal("can't open token file", zap.String("file", name), zap.Error(err)) } f.tokenFile = file - f.tokenReader = storage.NewIndexReader(f.readLimiter, file.Name(), file, f.indexCache.TokenRegistry) + f.tokenReaderProvider = storage.NewReaderProvider(f.readLimiter, file.Name(), file, f.indexCache.TokenRegistry) } if f.offsetsFile == nil { @@ -226,7 +226,7 @@ func (f *Sealed) openIndex() { logger.Fatal("can't open offsets file", zap.String("file", name), zap.Error(err)) } f.offsetsFile = file - f.offsetsReader = storage.NewIndexReader(f.readLimiter, file.Name(), file, f.indexCache.OffsetsRegistry) + f.offsetsReaderProvider = storage.NewReaderProvider(f.readLimiter, file.Name(), file, f.indexCache.OffsetsRegistry) } if f.idFile == nil { @@ -236,7 +236,7 @@ func (f *Sealed) openIndex() { logger.Fatal("can't open id file", zap.String("file", name), zap.Error(err)) } f.idFile = file - f.idReader = storage.NewIndexReader(f.readLimiter, file.Name(), file, f.indexCache.IDRegistry) + f.idReaderProvider = storage.NewReaderProvider(f.readLimiter, file.Name(), file, f.indexCache.IDRegistry) } if f.lidFile == nil { @@ -246,7 +246,7 @@ func (f *Sealed) openIndex() { logger.Fatal("can't open lid file", zap.String("file", name), zap.Error(err)) } f.lidFile = file - f.lidReader = storage.NewIndexReader(f.readLimiter, file.Name(), file, f.indexCache.LIDRegistry) + f.lidReaderProvider = storage.NewReaderProvider(f.readLimiter, file.Name(), file, f.indexCache.LIDRegistry) } } @@ -284,7 +284,11 @@ func (f *Sealed) loadInfo() { if f.IsLegacy { f.openInfoLegacy() - if f.info, err = loadInfoLegacy(f.legacyReader); err != nil { + legacyReader, err := f.legacyReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating legacyReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + if f.info, err = loadInfoLegacy(legacyReader); err != nil { logger.Fatal("error loading Info", zap.String("fraction", f.BaseFileName), zap.Error(err)) } return @@ -308,16 +312,37 @@ func (f *Sealed) init(full bool) { } if f.IsLegacy { - (&LegacyLoader{}).Load(&f.blocksData, f.info, f.legacyReader) + legacyReader, err := f.legacyReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating legacyReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + (&LegacyLoader{}).Load(&f.blocksData, f.info, legacyReader) f.isInited = true return } + tokenReader, err := f.tokenReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating tokenReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + offsetsReader, err := f.offsetsReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating offsetsReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + idReader, err := f.idReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating idReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + lidReader, err := f.lidReaderProvider.GetReader() + if err != nil { + logger.Fatal("error creating lidReader", zap.String("fraction", f.BaseFileName), zap.Error(err)) + } + (&Loader{}).Load(&f.blocksData, f.info, IndexReaders{ - Token: f.tokenReader, - Offsets: f.offsetsReader, - ID: f.idReader, - LID: f.lidReader, + Token: tokenReader, + Offsets: offsetsReader, + ID: idReader, + LID: lidReader, }) f.isInited = true @@ -472,35 +497,64 @@ func (f *Sealed) String() string { } func (f *Sealed) Fetch(ctx context.Context, ids []seq.ID, noSkipMasks bool) ([][]byte, error) { - dp := f.createDataProvider(ctx) + dp, err := f.createDataProvider(ctx) + if err != nil { + return nil, err + } defer dp.release() return dp.Fetch(ids, noSkipMasks) } func (f *Sealed) Search(ctx context.Context, params processor.SearchParams) (*seq.QPR, error) { - dp := f.createDataProvider(ctx) + dp, err := f.createDataProvider(ctx) + if err != nil { + return nil, err + } defer dp.release() return dp.Search(params) } func (f *Sealed) FindLIDs(ctx context.Context, ids []seq.ID) ([]seq.LID, error) { - dp := f.createDataProvider(ctx) + dp, err := f.createDataProvider(ctx) + if err != nil { + return nil, err + } defer dp.release() return dp.FindLIDs(ids) } -func (f *Sealed) createDataProvider(ctx context.Context) *sealedDataProvider { +func (f *Sealed) createDataProvider(ctx context.Context) (*sealedDataProvider, error) { f.init(true) - tokenReader := &f.tokenReader - lidReader := &f.lidReader - idReader := &f.idReader + var ( + tokenReader storage.IndexReader + lidReader storage.IndexReader + idReader storage.IndexReader + ) if f.IsLegacy { - tokenReader = &f.legacyReader - lidReader = &f.legacyReader - idReader = &f.legacyReader + legacyReader, err := f.legacyReaderProvider.GetReader() + if err != nil { + return nil, err + } + tokenReader = legacyReader + lidReader = legacyReader + idReader = legacyReader + } else { + var err error + tokenReader, err = f.tokenReaderProvider.GetReader() + if err != nil { + return nil, err + } + lidReader, err = f.lidReaderProvider.GetReader() + if err != nil { + return nil, err + } + idReader, err = f.idReaderProvider.GetReader() + if err != nil { + return nil, err + } } return &sealedDataProvider{ @@ -512,13 +566,13 @@ func (f *Sealed) createDataProvider(ctx context.Context) *sealedDataProvider { docsReader: &f.docsReader, blocksOffsets: f.blocksData.BlocksOffsets, lidsTable: f.blocksData.LIDsTable, - lidsLoader: lids.NewLoader(f.info.BinaryDataVer, lidReader, f.indexCache.LIDs), - tokenBlockLoader: token.NewBlockLoader(f.BaseFileName, tokenReader, f.indexCache.Tokens), - tokenTableLoader: token.NewTableLoader(f.BaseFileName, f.IsLegacy, tokenReader, f.indexCache.TokenTable), + lidsLoader: lids.NewLoader(f.info.BinaryDataVer, &lidReader, f.indexCache.LIDs), + tokenBlockLoader: token.NewBlockLoader(f.BaseFileName, &tokenReader, f.indexCache.Tokens), + tokenTableLoader: token.NewTableLoader(f.BaseFileName, f.IsLegacy, &tokenReader, f.indexCache.TokenTable), idsTable: &f.blocksData.IDsTable, idsProvider: seqids.NewProvider( - idReader, + &idReader, f.indexCache.MIDs, f.indexCache.RIDs, f.indexCache.Params, @@ -527,7 +581,7 @@ func (f *Sealed) createDataProvider(ctx context.Context) *sealedDataProvider { ), skipMaskProvider: f.skipMaskProvider, - } + }, nil } func (f *Sealed) Info() *common.Info { diff --git a/frac/sealed/token/table_loader.go b/frac/sealed/token/table_loader.go index a3fd61c01..638be6d0a 100644 --- a/frac/sealed/token/table_loader.go +++ b/frac/sealed/token/table_loader.go @@ -136,10 +136,7 @@ func (l *TableLoader) loadBlocksLegacy() ([]TableBlock, error) { } func (l *TableLoader) loadBlocks() ([]TableBlock, error) { - blocksCount, err := l.reader.BlocksCount() - if err != nil { - return nil, err - } + blocksCount := l.reader.BlocksCount() var blocks []TableBlock for blockIndex := l.tableIndex; blockIndex < uint32(blocksCount); blockIndex++ { diff --git a/frac/sealed_loader.go b/frac/sealed_loader.go index 893b75a42..1fb8fc614 100644 --- a/frac/sealed_loader.go +++ b/frac/sealed_loader.go @@ -233,13 +233,7 @@ func (l *Loader) loadIDsTable(r storage.IndexReader, idsTotal uint32, fracVersio IDsTotal: idsTotal, } - blocksCount, err := r.BlocksCount() - if err != nil { - logger.Fatal( - "cannot get block count", - zap.Error(err), - ) - } + blocksCount := r.BlocksCount() for blockIdx := 0; blockIdx < blocksCount; blockIdx += 3 { header, err := r.GetBlockHeader(uint32(blockIdx)) @@ -273,13 +267,7 @@ func (l *Loader) loadLIDsTable(r storage.IndexReader) (*lids.Table, error) { isContinued []bool ) - blocksCount, err := r.BlocksCount() - if err != nil { - logger.Fatal( - "cannot get block count", - zap.Error(err), - ) - } + blocksCount := r.BlocksCount() for blockIdx := 0; blockIdx < blocksCount; blockIdx++ { header, err := r.GetBlockHeader(uint32(blockIdx)) diff --git a/storage/index_reader.go b/storage/index_reader.go index c7dee18c9..7daa6a7a0 100644 --- a/storage/index_reader.go +++ b/storage/index_reader.go @@ -1,12 +1,10 @@ package storage import ( - "encoding/binary" "fmt" "io" "github.com/ozontech/seq-db/bytespool" - "github.com/ozontech/seq-db/cache" "github.com/ozontech/seq-db/util" ) @@ -16,70 +14,33 @@ type IndexReader struct { reader io.ReaderAt readerName string - cache *cache.Cache[[]byte] + registry []byte } func NewIndexReader( - limiter *ReadLimiter, readerName string, - reader io.ReaderAt, registryCache *cache.Cache[[]byte], + limiter *ReadLimiter, + readerName string, + reader io.ReaderAt, + registry []byte, ) IndexReader { return IndexReader{ limiter: limiter, reader: reader, readerName: readerName, - cache: registryCache, + registry: registry, } } func (r *IndexReader) GetBlockHeader(index uint32) (IndexBlockHeader, error) { - registry, err := r.cache.GetWithError(1, func() ([]byte, int, error) { - data, err := r.readRegistry() - return data, cap(data), err - }) - if err != nil { - return nil, err - } - - if (uint64(index)+1)*IndexBlockHeaderSize > uint64(len(registry)) { + if (uint64(index)+1)*IndexBlockHeaderSize > uint64(len(r.registry)) { return nil, fmt.Errorf( "too large index block in file %s, with index %d, registry size %d", - r.readerName, index, len(registry), + r.readerName, index, len(r.registry), ) } pos := index * IndexBlockHeaderSize - return registry[pos : pos+IndexBlockHeaderSize], nil -} - -func (r *IndexReader) readRegistry() ([]byte, error) { - numBuf := make([]byte, 16) - - n, err := r.limiter.ReadAt(r.reader, numBuf, 0) - if err != nil { - return nil, fmt.Errorf("can't read disk registry, %s", err.Error()) - } - if n == 0 { - return nil, fmt.Errorf("can't read disk registry, n=0") - } - - pos := binary.LittleEndian.Uint64(numBuf) - l := binary.LittleEndian.Uint64(numBuf[8:]) - buf := make([]byte, l) - - n, err = r.limiter.ReadAt(r.reader, buf, int64(pos)) - if err != nil && err != io.EOF { - return nil, fmt.Errorf("can't read disk registry, %s", err.Error()) - } - - if uint64(n) != l { - return nil, fmt.Errorf("can't read disk registry, read=%d, requested=%d", n, l) - } - - if len(buf)%IndexBlockHeaderSize != 0 { - return nil, fmt.Errorf("wrong registry format") - } - - return buf, nil + return r.registry[pos : pos+IndexBlockHeaderSize], nil } func (r *IndexReader) ReadIndexBlock(blockIndex uint32, dst []byte) ([]byte, uint64, error) { @@ -108,14 +69,6 @@ func (r *IndexReader) ReadIndexBlock(blockIndex uint32, dst []byte) ([]byte, uin return dst, uint64(n), err } -func (r *IndexReader) BlocksCount() (int, error) { - registry, err := r.cache.GetWithError(1, func() ([]byte, int, error) { - data, err := r.readRegistry() - return data, cap(data), err - }) - if err != nil { - return 0, err - } - - return len(registry) / IndexBlockHeaderSize, nil +func (r *IndexReader) BlocksCount() int { + return len(r.registry) / IndexBlockHeaderSize } diff --git a/storage/reader_provider.go b/storage/reader_provider.go new file mode 100644 index 000000000..5e728fa09 --- /dev/null +++ b/storage/reader_provider.go @@ -0,0 +1,75 @@ +package storage + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/ozontech/seq-db/cache" +) + +type ReaderProvider struct { + limiter *ReadLimiter + + reader io.ReaderAt + readerName string + + cache *cache.Cache[[]byte] +} + +func NewReaderProvider( + limiter *ReadLimiter, + readerName string, + reader io.ReaderAt, + registryCache *cache.Cache[[]byte], +) *ReaderProvider { + return &ReaderProvider{ + limiter: limiter, + readerName: readerName, + reader: reader, + cache: registryCache, + } +} + +func (r *ReaderProvider) GetReader() (IndexReader, error) { + registry, err := r.cache.GetWithError(1, func() ([]byte, int, error) { + data, err := r.readRegistry() + return data, cap(data), err + }) + if err != nil { + return IndexReader{}, err + } + + return NewIndexReader(r.limiter, r.readerName, r.reader, registry), nil +} + +func (r *ReaderProvider) readRegistry() ([]byte, error) { + numBuf := make([]byte, 16) + + n, err := r.limiter.ReadAt(r.reader, numBuf, 0) + if err != nil { + return nil, fmt.Errorf("can't read disk registry, %s", err.Error()) + } + if n == 0 { + return nil, fmt.Errorf("can't read disk registry, n=0") + } + + pos := binary.LittleEndian.Uint64(numBuf) + l := binary.LittleEndian.Uint64(numBuf[8:]) + buf := make([]byte, l) + + n, err = r.limiter.ReadAt(r.reader, buf, int64(pos)) + if err != nil && err != io.EOF { + return nil, fmt.Errorf("can't read disk registry, %s", err.Error()) + } + + if uint64(n) != l { + return nil, fmt.Errorf("can't read disk registry, read=%d, requested=%d", n, l) + } + + if len(buf)%IndexBlockHeaderSize != 0 { + return nil, fmt.Errorf("wrong registry format") + } + + return buf, nil +}