github.com/matrixorigin/matrixone@v0.7.0/pkg/objectio/reader.go (about)

     1  // Copyright 2021 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package objectio
    16  
    17  import (
    18  	"context"
    19  	"github.com/matrixorigin/matrixone/pkg/logutil"
    20  	"io"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    23  	"github.com/matrixorigin/matrixone/pkg/compress"
    24  
    25  	"github.com/matrixorigin/matrixone/pkg/fileservice"
    26  )
    27  
    28  type ObjectReader struct {
    29  	object *Object
    30  	name   string
    31  }
    32  
    33  const ExtentTypeSize = 4 * 3
    34  const ExtentsLength = 20
    35  const FooterSize = 8 + 4
    36  
    37  func NewObjectReader(name string, fs fileservice.FileService) (Reader, error) {
    38  	reader := &ObjectReader{
    39  		name:   name,
    40  		object: NewObject(name, fs),
    41  	}
    42  	return reader, nil
    43  }
    44  
    45  func (r *ObjectReader) ReadMeta(ctx context.Context, extents []Extent, m *mpool.MPool) ([]BlockObject, error) {
    46  	l := len(extents)
    47  	if l == 0 {
    48  		return nil, nil
    49  	}
    50  
    51  	metas := &fileservice.IOVector{
    52  		FilePath: r.name,
    53  		Entries:  make([]fileservice.IOEntry, 1, l),
    54  	}
    55  
    56  	metas.Entries[0] = fileservice.IOEntry{
    57  		Offset: int64(extents[0].offset),
    58  		Size:   int64(extents[0].originSize),
    59  
    60  		ToObject: func(reader io.Reader, data []byte) (any, int64, error) {
    61  			if len(data) == 0 {
    62  				var err error
    63  				data, err = io.ReadAll(reader)
    64  				if err != nil {
    65  					return nil, 0, err
    66  				}
    67  			}
    68  			dataLen := len(data)
    69  			blocks := make([]*Block, 0)
    70  			size := uint32(0)
    71  			i := uint32(0)
    72  			for {
    73  				if size == uint32(dataLen) {
    74  					break
    75  				}
    76  				extent := Extent{
    77  					id:         i,
    78  					offset:     extents[0].offset,
    79  					length:     extents[0].length,
    80  					originSize: extents[0].originSize,
    81  				}
    82  				block := &Block{
    83  					object: r.object,
    84  					extent: extent,
    85  					name:   r.name,
    86  				}
    87  				cache := data[size:dataLen]
    88  				unSize, err := block.UnMarshalMeta(cache)
    89  				if err != nil {
    90  					logutil.Infof("UnMarshalMeta failed: %v, extent %v", err.Error(), extents[0])
    91  					return nil, 0, err
    92  				}
    93  				i++
    94  				size += unSize
    95  				blocks = append(blocks, block)
    96  			}
    97  			return blocks, int64(len(data)), nil
    98  		},
    99  	}
   100  
   101  	err := r.object.fs.Read(ctx, metas)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	blocks := make([]BlockObject, 0, l)
   107  
   108  	for _, extent := range extents {
   109  		block := metas.Entries[0].Object.([]*Block)[extent.id]
   110  		blocks = append(blocks, block)
   111  	}
   112  	return blocks, err
   113  }
   114  
   115  func (r *ObjectReader) Read(ctx context.Context, extent Extent, idxs []uint16, m *mpool.MPool) (*fileservice.IOVector, error) {
   116  	blocks, err := r.ReadMeta(ctx, []Extent{extent}, m)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	block := blocks[0]
   121  
   122  	data := &fileservice.IOVector{
   123  		FilePath: r.name,
   124  		Entries:  make([]fileservice.IOEntry, 0, len(idxs)),
   125  	}
   126  	for _, idx := range idxs {
   127  		col := block.(*Block).columns[idx]
   128  		data.Entries = append(data.Entries, fileservice.IOEntry{
   129  			Offset: int64(col.GetMeta().location.Offset()),
   130  			Size:   int64(col.GetMeta().location.Length()),
   131  
   132  			ToObject: newDecompressToObject(int64(col.GetMeta().location.OriginSize())),
   133  		})
   134  	}
   135  
   136  	err = r.object.fs.Read(ctx, data)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	return data, nil
   141  }
   142  
   143  func (r *ObjectReader) ReadIndex(ctx context.Context, extent Extent, idxs []uint16, typ IndexDataType, m *mpool.MPool) ([]IndexData, error) {
   144  	blocks, err := r.ReadMeta(ctx, []Extent{extent}, m)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	block := blocks[0]
   149  	indexes := make([]IndexData, 0)
   150  	for _, idx := range idxs {
   151  		col := block.(*Block).columns[idx]
   152  		index, err := col.GetIndex(ctx, typ, m)
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  		indexes = append(indexes, index)
   157  	}
   158  	return indexes, nil
   159  }
   160  
   161  func (r *ObjectReader) ReadAllMeta(ctx context.Context, fileSize int64, m *mpool.MPool) ([]BlockObject, error) {
   162  	footer, err := r.readFooter(ctx, fileSize, m)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return r.ReadMeta(ctx, footer.extents, m)
   167  }
   168  
   169  func (r *ObjectReader) readFooter(ctx context.Context, fileSize int64, m *mpool.MPool) (*Footer, error) {
   170  	var err error
   171  	var footer *Footer
   172  
   173  	// I don't know how many blocks there are in the object,
   174  	// read "ExtentsLength" blocks by default
   175  	size := int64(FooterSize + ExtentsLength*ExtentTypeSize)
   176  	if size > fileSize {
   177  		size = fileSize
   178  	}
   179  	footer, err = r.readFooterAndUnMarshal(ctx, fileSize, size, m)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	if len(footer.extents) == 0 {
   184  		size = int64(FooterSize + footer.blockCount*ExtentTypeSize)
   185  		footer, err = r.readFooterAndUnMarshal(ctx, fileSize, size, m)
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  	}
   190  
   191  	return footer, nil
   192  }
   193  
   194  func (r *ObjectReader) readFooterAndUnMarshal(ctx context.Context, fileSize, size int64, m *mpool.MPool) (*Footer, error) {
   195  	data := &fileservice.IOVector{
   196  		FilePath: r.name,
   197  		Entries: []fileservice.IOEntry{
   198  			{
   199  				Offset: fileSize - size,
   200  				Size:   size,
   201  
   202  				ToObject: func(reader io.Reader, data []byte) (any, int64, error) {
   203  					// unmarshal
   204  					if len(data) == 0 {
   205  						var err error
   206  						data, err = io.ReadAll(reader)
   207  						if err != nil {
   208  							return nil, 0, err
   209  						}
   210  					}
   211  					footer := &Footer{}
   212  					if err := footer.UnMarshalFooter(data); err != nil {
   213  						return nil, 0, err
   214  					}
   215  					return footer, int64(len(data)), nil
   216  				},
   217  			},
   218  		},
   219  	}
   220  	err := r.object.fs.Read(ctx, data)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	return data.Entries[0].Object.(*Footer), nil
   226  }
   227  
   228  type ToObjectFunc = func(r io.Reader, buf []byte) (any, int64, error)
   229  
   230  // newDecompressToObject the decompression function passed to fileservice
   231  func newDecompressToObject(size int64) ToObjectFunc {
   232  	return func(reader io.Reader, data []byte) (any, int64, error) {
   233  		// decompress
   234  		var err error
   235  		if len(data) == 0 {
   236  			data, err = io.ReadAll(reader)
   237  			if err != nil {
   238  				return nil, 0, err
   239  			}
   240  		}
   241  		decompressed := make([]byte, size)
   242  		decompressed, err = compress.Decompress(data, decompressed, compress.Lz4)
   243  		if err != nil {
   244  			return nil, 0, err
   245  		}
   246  		return decompressed, int64(len(decompressed)), nil
   247  	}
   248  }