github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/metacache-stream.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"strings"
    26  	"sync"
    27  
    28  	jsoniter "github.com/json-iterator/go"
    29  	"github.com/klauspost/compress/s2"
    30  	xioutil "github.com/minio/minio/internal/ioutil"
    31  	"github.com/minio/minio/internal/logger"
    32  	"github.com/tinylib/msgp/msgp"
    33  	"github.com/valyala/bytebufferpool"
    34  )
    35  
    36  // metadata stream format:
    37  //
    38  // The stream is s2 compressed.
    39  // https://github.com/klauspost/compress/tree/master/s2#s2-compression
    40  // This ensures integrity and reduces the size typically by at least 50%.
    41  //
    42  // All stream elements are msgpack encoded.
    43  //
    44  // 1 Integer, metacacheStreamVersion of the writer.
    45  // This can be used for managing breaking changes.
    46  //
    47  // For each element:
    48  // 1. Bool. If false at end of stream.
    49  // 2. String. Name of object. Directories contains a trailing slash.
    50  // 3. Binary. Blob of metadata. Length 0 on directories.
    51  // ... Next element.
    52  //
    53  // Streams can be assumed to be sorted in ascending order.
    54  // If the stream ends before a false boolean it can be assumed it was truncated.
    55  
    56  const metacacheStreamVersion = 2
    57  
    58  // metacacheWriter provides a serializer of metacache objects.
    59  type metacacheWriter struct {
    60  	streamErr   error
    61  	mw          *msgp.Writer
    62  	creator     func() error
    63  	closer      func() error
    64  	blockSize   int
    65  	streamWg    sync.WaitGroup
    66  	reuseBlocks bool
    67  }
    68  
    69  // newMetacacheWriter will create a serializer that will write objects in given order to the output.
    70  // Provide a block size that affects latency. If 0 a default of 128KiB will be used.
    71  // Block size can be up to 4MiB.
    72  func newMetacacheWriter(out io.Writer, blockSize int) *metacacheWriter {
    73  	if blockSize < 8<<10 {
    74  		blockSize = 128 << 10
    75  	}
    76  	w := metacacheWriter{
    77  		mw:        nil,
    78  		blockSize: blockSize,
    79  	}
    80  	w.creator = func() error {
    81  		s2w := s2.NewWriter(out, s2.WriterBlockSize(blockSize), s2.WriterConcurrency(2))
    82  		w.mw = msgp.NewWriter(s2w)
    83  		w.creator = nil
    84  		if err := w.mw.WriteByte(metacacheStreamVersion); err != nil {
    85  			return err
    86  		}
    87  
    88  		w.closer = func() (err error) {
    89  			defer func() {
    90  				cerr := s2w.Close()
    91  				if err == nil && cerr != nil {
    92  					err = cerr
    93  				}
    94  			}()
    95  			if w.streamErr != nil {
    96  				return w.streamErr
    97  			}
    98  			if err = w.mw.WriteBool(false); err != nil {
    99  				return err
   100  			}
   101  			return w.mw.Flush()
   102  		}
   103  		return nil
   104  	}
   105  	return &w
   106  }
   107  
   108  // write one or more objects to the stream in order.
   109  // It is favorable to send as many objects as possible in a single write,
   110  // but no more than math.MaxUint32
   111  func (w *metacacheWriter) write(objs ...metaCacheEntry) error {
   112  	if w == nil {
   113  		return errors.New("metacacheWriter: nil writer")
   114  	}
   115  	if len(objs) == 0 {
   116  		return nil
   117  	}
   118  	if w.creator != nil {
   119  		err := w.creator()
   120  		w.creator = nil
   121  		if err != nil {
   122  			return fmt.Errorf("metacacheWriter: unable to create writer: %w", err)
   123  		}
   124  		if w.mw == nil {
   125  			return errors.New("metacacheWriter: writer not initialized")
   126  		}
   127  	}
   128  	for _, o := range objs {
   129  		if len(o.name) == 0 {
   130  			return errors.New("metacacheWriter: no name provided")
   131  		}
   132  		// Indicate EOS
   133  		err := w.mw.WriteBool(true)
   134  		if err != nil {
   135  			return err
   136  		}
   137  		err = w.mw.WriteString(o.name)
   138  		if err != nil {
   139  			return err
   140  		}
   141  		err = w.mw.WriteBytes(o.metadata)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		if w.reuseBlocks || o.reusable {
   146  			metaDataPoolPut(o.metadata)
   147  		}
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  // stream entries to the output.
   154  // The returned channel should be closed when done.
   155  // Any error is reported when closing the metacacheWriter.
   156  func (w *metacacheWriter) stream() (chan<- metaCacheEntry, error) {
   157  	if w.creator != nil {
   158  		err := w.creator()
   159  		w.creator = nil
   160  		if err != nil {
   161  			return nil, fmt.Errorf("metacacheWriter: unable to create writer: %w", err)
   162  		}
   163  		if w.mw == nil {
   164  			return nil, errors.New("metacacheWriter: writer not initialized")
   165  		}
   166  	}
   167  	objs := make(chan metaCacheEntry, 100)
   168  	w.streamErr = nil
   169  	w.streamWg.Add(1)
   170  	go func() {
   171  		defer w.streamWg.Done()
   172  		for o := range objs {
   173  			if len(o.name) == 0 || w.streamErr != nil {
   174  				continue
   175  			}
   176  			// Indicate EOS
   177  			err := w.mw.WriteBool(true)
   178  			if err != nil {
   179  				w.streamErr = err
   180  				continue
   181  			}
   182  			err = w.mw.WriteString(o.name)
   183  			if err != nil {
   184  				w.streamErr = err
   185  				continue
   186  			}
   187  			err = w.mw.WriteBytes(o.metadata)
   188  			if w.reuseBlocks || o.reusable {
   189  				metaDataPoolPut(o.metadata)
   190  			}
   191  			if err != nil {
   192  				w.streamErr = err
   193  				continue
   194  			}
   195  		}
   196  	}()
   197  
   198  	return objs, nil
   199  }
   200  
   201  // Close and release resources.
   202  func (w *metacacheWriter) Close() error {
   203  	if w == nil || w.closer == nil {
   204  		return nil
   205  	}
   206  	w.streamWg.Wait()
   207  	err := w.closer()
   208  	w.closer = nil
   209  	return err
   210  }
   211  
   212  // Reset and start writing to new writer.
   213  // Close must have been called before this.
   214  func (w *metacacheWriter) Reset(out io.Writer) {
   215  	w.streamErr = nil
   216  	w.creator = func() error {
   217  		s2w := s2.NewWriter(out, s2.WriterBlockSize(w.blockSize), s2.WriterConcurrency(2))
   218  		w.mw = msgp.NewWriter(s2w)
   219  		w.creator = nil
   220  		if err := w.mw.WriteByte(metacacheStreamVersion); err != nil {
   221  			return err
   222  		}
   223  
   224  		w.closer = func() error {
   225  			if w.streamErr != nil {
   226  				return w.streamErr
   227  			}
   228  			if err := w.mw.WriteBool(false); err != nil {
   229  				return err
   230  			}
   231  			if err := w.mw.Flush(); err != nil {
   232  				return err
   233  			}
   234  			return s2w.Close()
   235  		}
   236  		return nil
   237  	}
   238  }
   239  
   240  var s2DecPool = sync.Pool{New: func() interface{} {
   241  	// Default alloc block for network transfer.
   242  	return s2.NewReader(nil, s2.ReaderAllocBlock(16<<10))
   243  }}
   244  
   245  // metacacheReader allows reading a cache stream.
   246  type metacacheReader struct {
   247  	mr      *msgp.Reader
   248  	current metaCacheEntry
   249  	err     error // stateful error
   250  	closer  func()
   251  	creator func() error
   252  }
   253  
   254  // newMetacacheReader creates a new cache reader.
   255  // Nothing will be read from the stream yet.
   256  func newMetacacheReader(r io.Reader) *metacacheReader {
   257  	dec := s2DecPool.Get().(*s2.Reader)
   258  	dec.Reset(r)
   259  	mr := msgpNewReader(dec)
   260  	return &metacacheReader{
   261  		mr: mr,
   262  		closer: func() {
   263  			dec.Reset(nil)
   264  			s2DecPool.Put(dec)
   265  			readMsgpReaderPoolPut(mr)
   266  		},
   267  		creator: func() error {
   268  			v, err := mr.ReadByte()
   269  			if err != nil {
   270  				return err
   271  			}
   272  			switch v {
   273  			case 1, 2:
   274  			default:
   275  				return fmt.Errorf("metacacheReader: Unknown version: %d", v)
   276  			}
   277  			return nil
   278  		},
   279  	}
   280  }
   281  
   282  func (r *metacacheReader) checkInit() {
   283  	if r.creator == nil || r.err != nil {
   284  		return
   285  	}
   286  	r.err = r.creator()
   287  	r.creator = nil
   288  }
   289  
   290  // peek will return the name of the next object.
   291  // Will return io.EOF if there are no more objects.
   292  // Should be used sparingly.
   293  func (r *metacacheReader) peek() (metaCacheEntry, error) {
   294  	r.checkInit()
   295  	if r.err != nil {
   296  		return metaCacheEntry{}, r.err
   297  	}
   298  	if r.current.name != "" {
   299  		return r.current, nil
   300  	}
   301  	if more, err := r.mr.ReadBool(); !more {
   302  		switch err {
   303  		case nil:
   304  			r.err = io.EOF
   305  			return metaCacheEntry{}, io.EOF
   306  		case io.EOF:
   307  			r.err = io.ErrUnexpectedEOF
   308  			return metaCacheEntry{}, io.ErrUnexpectedEOF
   309  		}
   310  		r.err = err
   311  		return metaCacheEntry{}, err
   312  	}
   313  
   314  	var err error
   315  	if r.current.name, err = r.mr.ReadString(); err != nil {
   316  		if err == io.EOF {
   317  			err = io.ErrUnexpectedEOF
   318  		}
   319  		r.err = err
   320  		return metaCacheEntry{}, err
   321  	}
   322  	r.current.metadata, err = r.mr.ReadBytes(r.current.metadata[:0])
   323  	if err == io.EOF {
   324  		err = io.ErrUnexpectedEOF
   325  	}
   326  	r.err = err
   327  	return r.current, err
   328  }
   329  
   330  // next will read one entry from the stream.
   331  // Generally not recommended for fast operation.
   332  func (r *metacacheReader) next() (metaCacheEntry, error) {
   333  	r.checkInit()
   334  	if r.err != nil {
   335  		return metaCacheEntry{}, r.err
   336  	}
   337  	var m metaCacheEntry
   338  	var err error
   339  	if r.current.name != "" {
   340  		m.name = r.current.name
   341  		m.metadata = r.current.metadata
   342  		r.current.name = ""
   343  		r.current.metadata = nil
   344  		return m, nil
   345  	}
   346  	if more, err := r.mr.ReadBool(); !more {
   347  		switch err {
   348  		case nil:
   349  			r.err = io.EOF
   350  			return m, io.EOF
   351  		case io.EOF:
   352  			r.err = io.ErrUnexpectedEOF
   353  			return m, io.ErrUnexpectedEOF
   354  		}
   355  		r.err = err
   356  		return m, err
   357  	}
   358  	if m.name, err = r.mr.ReadString(); err != nil {
   359  		if err == io.EOF {
   360  			err = io.ErrUnexpectedEOF
   361  		}
   362  		r.err = err
   363  		return m, err
   364  	}
   365  	m.metadata, err = r.mr.ReadBytes(metaDataPoolGet())
   366  	if err == io.EOF {
   367  		err = io.ErrUnexpectedEOF
   368  	}
   369  	if len(m.metadata) == 0 && cap(m.metadata) >= metaDataReadDefault {
   370  		metaDataPoolPut(m.metadata)
   371  		m.metadata = nil
   372  	}
   373  	r.err = err
   374  	return m, err
   375  }
   376  
   377  // next will read one entry from the stream.
   378  // Generally not recommended for fast operation.
   379  func (r *metacacheReader) nextEOF() bool {
   380  	r.checkInit()
   381  	if r.err != nil {
   382  		return r.err == io.EOF
   383  	}
   384  	if r.current.name != "" {
   385  		return false
   386  	}
   387  	_, err := r.peek()
   388  	if err != nil {
   389  		r.err = err
   390  		return r.err == io.EOF
   391  	}
   392  	return false
   393  }
   394  
   395  // forwardTo will forward to the first entry that is >= s.
   396  // Will return io.EOF if end of stream is reached without finding any.
   397  func (r *metacacheReader) forwardTo(s string) error {
   398  	r.checkInit()
   399  	if r.err != nil {
   400  		return r.err
   401  	}
   402  
   403  	if s == "" {
   404  		return nil
   405  	}
   406  	if r.current.name != "" {
   407  		if r.current.name >= s {
   408  			return nil
   409  		}
   410  		r.current.name = ""
   411  		r.current.metadata = nil
   412  	}
   413  	// temporary name buffer.
   414  	tmp := make([]byte, 0, 256)
   415  	for {
   416  		if more, err := r.mr.ReadBool(); !more {
   417  			switch err {
   418  			case nil:
   419  				r.err = io.EOF
   420  				return io.EOF
   421  			case io.EOF:
   422  				r.err = io.ErrUnexpectedEOF
   423  				return io.ErrUnexpectedEOF
   424  			}
   425  			r.err = err
   426  			return err
   427  		}
   428  		// Read name without allocating more than 1 buffer.
   429  		sz, err := r.mr.ReadStringHeader()
   430  		if err != nil {
   431  			r.err = err
   432  			return err
   433  		}
   434  		if cap(tmp) < int(sz) {
   435  			tmp = make([]byte, 0, sz+256)
   436  		}
   437  		tmp = tmp[:sz]
   438  		_, err = r.mr.R.ReadFull(tmp)
   439  		if err != nil {
   440  			r.err = err
   441  			return err
   442  		}
   443  		if string(tmp) >= s {
   444  			r.current.name = string(tmp)
   445  			r.current.metadata, r.err = r.mr.ReadBytes(nil)
   446  			return r.err
   447  		}
   448  		// Skip metadata
   449  		err = r.mr.Skip()
   450  		if err != nil {
   451  			if err == io.EOF {
   452  				err = io.ErrUnexpectedEOF
   453  			}
   454  			r.err = err
   455  			return err
   456  		}
   457  	}
   458  }
   459  
   460  // readN will return all the requested number of entries in order
   461  // or all if n < 0.
   462  // Will return io.EOF if end of stream is reached.
   463  // If requesting 0 objects nil error will always be returned regardless of at end of stream.
   464  // Use peek to determine if at end of stream.
   465  func (r *metacacheReader) readN(n int, inclDeleted, inclDirs, inclVersions bool, prefix string) (metaCacheEntriesSorted, error) {
   466  	r.checkInit()
   467  	if n == 0 {
   468  		return metaCacheEntriesSorted{}, nil
   469  	}
   470  	if r.err != nil {
   471  		return metaCacheEntriesSorted{}, r.err
   472  	}
   473  
   474  	var res metaCacheEntries
   475  	if n > 0 {
   476  		res = make(metaCacheEntries, 0, n)
   477  	}
   478  	if prefix != "" {
   479  		if err := r.forwardTo(prefix); err != nil {
   480  			return metaCacheEntriesSorted{}, err
   481  		}
   482  	}
   483  	next, err := r.peek()
   484  	if err != nil {
   485  		return metaCacheEntriesSorted{}, err
   486  	}
   487  	if !next.hasPrefix(prefix) {
   488  		return metaCacheEntriesSorted{}, io.EOF
   489  	}
   490  
   491  	if r.current.name != "" {
   492  		if (inclDeleted || !r.current.isLatestDeletemarker()) && r.current.hasPrefix(prefix) && (inclDirs || r.current.isObject()) {
   493  			res = append(res, r.current)
   494  		}
   495  		r.current.name = ""
   496  		r.current.metadata = nil
   497  	}
   498  
   499  	for n < 0 || len(res) < n {
   500  		if more, err := r.mr.ReadBool(); !more {
   501  			switch err {
   502  			case nil:
   503  				r.err = io.EOF
   504  				return metaCacheEntriesSorted{o: res}, io.EOF
   505  			case io.EOF:
   506  				r.err = io.ErrUnexpectedEOF
   507  				return metaCacheEntriesSorted{o: res}, io.ErrUnexpectedEOF
   508  			}
   509  			r.err = err
   510  			return metaCacheEntriesSorted{o: res}, err
   511  		}
   512  		var err error
   513  		var meta metaCacheEntry
   514  		if meta.name, err = r.mr.ReadString(); err != nil {
   515  			if err == io.EOF {
   516  				err = io.ErrUnexpectedEOF
   517  			}
   518  			r.err = err
   519  			return metaCacheEntriesSorted{o: res}, err
   520  		}
   521  		if !meta.hasPrefix(prefix) {
   522  			r.mr.R.Skip(1)
   523  			return metaCacheEntriesSorted{o: res}, io.EOF
   524  		}
   525  		if meta.metadata, err = r.mr.ReadBytes(metaDataPoolGet()); err != nil {
   526  			if err == io.EOF {
   527  				err = io.ErrUnexpectedEOF
   528  			}
   529  			r.err = err
   530  			return metaCacheEntriesSorted{o: res}, err
   531  		}
   532  		if len(meta.metadata) == 0 {
   533  			metaDataPoolPut(meta.metadata)
   534  			meta.metadata = nil
   535  		}
   536  		if !inclDirs && (meta.isDir() || (!inclVersions && meta.isObjectDir() && meta.isLatestDeletemarker())) {
   537  			continue
   538  		}
   539  		if !inclDeleted && meta.isLatestDeletemarker() && meta.isObject() && !meta.isObjectDir() {
   540  			continue
   541  		}
   542  		res = append(res, meta)
   543  	}
   544  	return metaCacheEntriesSorted{o: res}, nil
   545  }
   546  
   547  // readAll will return all remaining objects on the dst channel and close it when done.
   548  // The context allows the operation to be canceled.
   549  func (r *metacacheReader) readAll(ctx context.Context, dst chan<- metaCacheEntry) error {
   550  	r.checkInit()
   551  	if r.err != nil {
   552  		return r.err
   553  	}
   554  	defer xioutil.SafeClose(dst)
   555  	if r.current.name != "" {
   556  		select {
   557  		case <-ctx.Done():
   558  			r.err = ctx.Err()
   559  			return ctx.Err()
   560  		case dst <- r.current:
   561  		}
   562  		r.current.name = ""
   563  		r.current.metadata = nil
   564  	}
   565  	for {
   566  		if more, err := r.mr.ReadBool(); !more {
   567  			if err == io.EOF {
   568  				err = io.ErrUnexpectedEOF
   569  			}
   570  			r.err = err
   571  			return err
   572  		}
   573  
   574  		var err error
   575  		var meta metaCacheEntry
   576  		if meta.name, err = r.mr.ReadString(); err != nil {
   577  			if err == io.EOF {
   578  				err = io.ErrUnexpectedEOF
   579  			}
   580  			r.err = err
   581  			return err
   582  		}
   583  		if meta.metadata, err = r.mr.ReadBytes(metaDataPoolGet()); err != nil {
   584  			if err == io.EOF {
   585  				err = io.ErrUnexpectedEOF
   586  			}
   587  			r.err = err
   588  			return err
   589  		}
   590  		if len(meta.metadata) == 0 {
   591  			metaDataPoolPut(meta.metadata)
   592  			meta.metadata = nil
   593  		}
   594  		select {
   595  		case <-ctx.Done():
   596  			r.err = ctx.Err()
   597  			return ctx.Err()
   598  		case dst <- meta:
   599  		}
   600  	}
   601  }
   602  
   603  // readFn will return all remaining objects
   604  // and provide a callback for each entry read in order
   605  // as long as true is returned on the callback.
   606  func (r *metacacheReader) readFn(fn func(entry metaCacheEntry) bool) error {
   607  	r.checkInit()
   608  	if r.err != nil {
   609  		return r.err
   610  	}
   611  	if r.current.name != "" {
   612  		fn(r.current)
   613  		r.current.name = ""
   614  		r.current.metadata = nil
   615  	}
   616  	for {
   617  		if more, err := r.mr.ReadBool(); !more {
   618  			switch err {
   619  			case io.EOF:
   620  				r.err = io.ErrUnexpectedEOF
   621  				return io.ErrUnexpectedEOF
   622  			case nil:
   623  				r.err = io.EOF
   624  				return io.EOF
   625  			}
   626  			return err
   627  		}
   628  
   629  		var err error
   630  		var meta metaCacheEntry
   631  		if meta.name, err = r.mr.ReadString(); err != nil {
   632  			if err == io.EOF {
   633  				err = io.ErrUnexpectedEOF
   634  			}
   635  			r.err = err
   636  			return err
   637  		}
   638  		if meta.metadata, err = r.mr.ReadBytes(nil); err != nil {
   639  			if err == io.EOF {
   640  				err = io.ErrUnexpectedEOF
   641  			}
   642  			r.err = err
   643  			return err
   644  		}
   645  		// Send it!
   646  		if !fn(meta) {
   647  			return nil
   648  		}
   649  	}
   650  }
   651  
   652  // readNames will return all the requested number of names in order
   653  // or all if n < 0.
   654  // Will return io.EOF if end of stream is reached.
   655  func (r *metacacheReader) readNames(n int) ([]string, error) {
   656  	r.checkInit()
   657  	if r.err != nil {
   658  		return nil, r.err
   659  	}
   660  	if n == 0 {
   661  		return nil, nil
   662  	}
   663  	var res []string
   664  	if n > 0 {
   665  		res = make([]string, 0, n)
   666  	}
   667  	if r.current.name != "" {
   668  		res = append(res, r.current.name)
   669  		r.current.name = ""
   670  		r.current.metadata = nil
   671  	}
   672  	for n < 0 || len(res) < n {
   673  		if more, err := r.mr.ReadBool(); !more {
   674  			switch err {
   675  			case nil:
   676  				r.err = io.EOF
   677  				return res, io.EOF
   678  			case io.EOF:
   679  				r.err = io.ErrUnexpectedEOF
   680  				return res, io.ErrUnexpectedEOF
   681  			}
   682  			return res, err
   683  		}
   684  
   685  		var err error
   686  		var name string
   687  		if name, err = r.mr.ReadString(); err != nil {
   688  			r.err = err
   689  			return res, err
   690  		}
   691  		if err = r.mr.Skip(); err != nil {
   692  			if err == io.EOF {
   693  				err = io.ErrUnexpectedEOF
   694  			}
   695  			r.err = err
   696  			return res, err
   697  		}
   698  		res = append(res, name)
   699  	}
   700  	return res, nil
   701  }
   702  
   703  // skip n entries on the input stream.
   704  // If there are less entries left io.EOF is returned.
   705  func (r *metacacheReader) skip(n int) error {
   706  	r.checkInit()
   707  	if r.err != nil {
   708  		return r.err
   709  	}
   710  	if n <= 0 {
   711  		return nil
   712  	}
   713  	if r.current.name != "" {
   714  		n--
   715  		r.current.name = ""
   716  		r.current.metadata = nil
   717  	}
   718  	for n > 0 {
   719  		if more, err := r.mr.ReadBool(); !more {
   720  			switch err {
   721  			case nil:
   722  				r.err = io.EOF
   723  				return io.EOF
   724  			case io.EOF:
   725  				r.err = io.ErrUnexpectedEOF
   726  				return io.ErrUnexpectedEOF
   727  			}
   728  			return err
   729  		}
   730  
   731  		if err := r.mr.Skip(); err != nil {
   732  			if err == io.EOF {
   733  				err = io.ErrUnexpectedEOF
   734  			}
   735  			r.err = err
   736  			return err
   737  		}
   738  		if err := r.mr.Skip(); err != nil {
   739  			if err == io.EOF {
   740  				err = io.ErrUnexpectedEOF
   741  			}
   742  			r.err = err
   743  			return err
   744  		}
   745  		n--
   746  	}
   747  	return nil
   748  }
   749  
   750  // Close and release resources.
   751  func (r *metacacheReader) Close() error {
   752  	if r == nil || r.closer == nil {
   753  		return nil
   754  	}
   755  	r.closer()
   756  	r.closer = nil
   757  	r.creator = nil
   758  	return nil
   759  }
   760  
   761  // metacacheBlockWriter collects blocks and provides a callaback to store them.
   762  type metacacheBlockWriter struct {
   763  	wg           sync.WaitGroup
   764  	streamErr    error
   765  	blockEntries int
   766  }
   767  
   768  // newMetacacheBlockWriter provides a streaming block writer.
   769  // Each block is the size of the capacity of the input channel.
   770  // The caller should close to indicate the stream has ended.
   771  func newMetacacheBlockWriter(in <-chan metaCacheEntry, nextBlock func(b *metacacheBlock) error) *metacacheBlockWriter {
   772  	w := metacacheBlockWriter{blockEntries: cap(in)}
   773  	w.wg.Add(1)
   774  	go func() {
   775  		defer w.wg.Done()
   776  		var current metacacheBlock
   777  		var n int
   778  
   779  		buf := bytebufferpool.Get()
   780  		defer func() {
   781  			buf.Reset()
   782  			bytebufferpool.Put(buf)
   783  		}()
   784  
   785  		block := newMetacacheWriter(buf, 1<<20)
   786  		defer block.Close()
   787  		finishBlock := func() {
   788  			if err := block.Close(); err != nil {
   789  				w.streamErr = err
   790  				return
   791  			}
   792  			current.data = buf.Bytes()
   793  			w.streamErr = nextBlock(&current)
   794  			// Prepare for next
   795  			current.n++
   796  			buf.Reset()
   797  			block.Reset(buf)
   798  			current.First = ""
   799  		}
   800  		for o := range in {
   801  			if len(o.name) == 0 || w.streamErr != nil {
   802  				continue
   803  			}
   804  			if current.First == "" {
   805  				current.First = o.name
   806  			}
   807  
   808  			if n >= w.blockEntries-1 {
   809  				finishBlock()
   810  				n = 0
   811  			}
   812  			n++
   813  
   814  			w.streamErr = block.write(o)
   815  			if w.streamErr != nil {
   816  				continue
   817  			}
   818  			current.Last = o.name
   819  		}
   820  		if n > 0 || current.n == 0 {
   821  			current.EOS = true
   822  			finishBlock()
   823  		}
   824  	}()
   825  	return &w
   826  }
   827  
   828  // Close the stream.
   829  // The incoming channel must be closed before calling this.
   830  // Returns the first error the occurred during the writing if any.
   831  func (w *metacacheBlockWriter) Close() error {
   832  	w.wg.Wait()
   833  	return w.streamErr
   834  }
   835  
   836  type metacacheBlock struct {
   837  	data  []byte
   838  	n     int
   839  	First string `json:"f"`
   840  	Last  string `json:"l"`
   841  	EOS   bool   `json:"eos,omitempty"`
   842  }
   843  
   844  func (b metacacheBlock) headerKV() map[string]string {
   845  	json := jsoniter.ConfigCompatibleWithStandardLibrary
   846  	v, err := json.Marshal(b)
   847  	if err != nil {
   848  		logger.LogIf(context.Background(), err) // Unlikely
   849  		return nil
   850  	}
   851  	return map[string]string{fmt.Sprintf("%s-metacache-part-%d", ReservedMetadataPrefixLower, b.n): string(v)}
   852  }
   853  
   854  // pastPrefix returns true if the given prefix is before start of the block.
   855  func (b metacacheBlock) pastPrefix(prefix string) bool {
   856  	if prefix == "" || strings.HasPrefix(b.First, prefix) {
   857  		return false
   858  	}
   859  	// We have checked if prefix matches, so we can do direct compare.
   860  	return b.First > prefix
   861  }
   862  
   863  // endedPrefix returns true if the given prefix ends within the block.
   864  func (b metacacheBlock) endedPrefix(prefix string) bool {
   865  	if prefix == "" || strings.HasPrefix(b.Last, prefix) {
   866  		return false
   867  	}
   868  
   869  	// We have checked if prefix matches, so we can do direct compare.
   870  	return b.Last > prefix
   871  }