github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/common/ledger/blkstorage/fsblkstorage/block_stream.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fsblkstorage
    18  
    19  import (
    20  	"bufio"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  
    26  	"github.com/golang/protobuf/proto"
    27  )
    28  
    29  // ErrUnexpectedEndOfBlockfile error used to indicate an unexpected end of a file segment
    30  // this can happen mainly if a crash occurs during appening a block and partial block contents
    31  // get written towards the end of the file
    32  var ErrUnexpectedEndOfBlockfile = errors.New("unexpected end of blockfile")
    33  
    34  // blockfileStream reads blocks sequentially from a single file.
    35  // It starts from the given offset and can traverse till the end of the file
    36  type blockfileStream struct {
    37  	fileNum       int
    38  	file          *os.File
    39  	reader        *bufio.Reader
    40  	currentOffset int64
    41  }
    42  
    43  // blockStream reads blocks sequentially from multiple files.
    44  // it starts from a given file offset and continues with the next
    45  // file segment until the end of the last segment (`endFileNum`)
    46  type blockStream struct {
    47  	rootDir           string
    48  	currentFileNum    int
    49  	endFileNum        int
    50  	currentFileStream *blockfileStream
    51  }
    52  
    53  // blockPlacementInfo captures the information related
    54  // to block's placement in the file.
    55  type blockPlacementInfo struct {
    56  	fileNum          int
    57  	blockStartOffset int64
    58  	blockBytesOffset int64
    59  }
    60  
    61  ///////////////////////////////////
    62  // blockfileStream functions
    63  ////////////////////////////////////
    64  func newBlockfileStream(rootDir string, fileNum int, startOffset int64) (*blockfileStream, error) {
    65  	filePath := deriveBlockfilePath(rootDir, fileNum)
    66  	logger.Debugf("newBlockfileStream(): filePath=[%s], startOffset=[%d]", filePath, startOffset)
    67  	var file *os.File
    68  	var err error
    69  	if file, err = os.OpenFile(filePath, os.O_RDONLY, 0600); err != nil {
    70  		return nil, err
    71  	}
    72  	var newPosition int64
    73  	if newPosition, err = file.Seek(startOffset, 0); err != nil {
    74  		return nil, err
    75  	}
    76  	if newPosition != startOffset {
    77  		panic(fmt.Sprintf("Could not seek file [%s] to given startOffset [%d]. New position = [%d]",
    78  			filePath, startOffset, newPosition))
    79  	}
    80  	s := &blockfileStream{fileNum, file, bufio.NewReader(file), startOffset}
    81  	return s, nil
    82  }
    83  
    84  func (s *blockfileStream) nextBlockBytes() ([]byte, error) {
    85  	blockBytes, _, err := s.nextBlockBytesAndPlacementInfo()
    86  	return blockBytes, err
    87  }
    88  
    89  // nextBlockBytesAndPlacementInfo returns bytes for the next block
    90  // along with the offset information in the block file.
    91  // An error `ErrUnexpectedEndOfBlockfile` is returned if a partial written data is detected
    92  // which is possible towards the tail of the file if a crash had taken place during appending of a block
    93  func (s *blockfileStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) {
    94  	var lenBytes []byte
    95  	var err error
    96  	var fileInfo os.FileInfo
    97  	moreContentAvailable := true
    98  
    99  	if fileInfo, err = s.file.Stat(); err != nil {
   100  		return nil, nil, err
   101  	}
   102  	if s.currentOffset == fileInfo.Size() {
   103  		logger.Debugf("Finished reading file number [%d]", s.fileNum)
   104  		return nil, nil, nil
   105  	}
   106  	remainingBytes := fileInfo.Size() - s.currentOffset
   107  	// Peek 8 or smaller number of bytes (if remaining bytes are less than 8)
   108  	// Assumption is that a block size would be small enough to be represented in 8 bytes varint
   109  	peekBytes := 8
   110  	if remainingBytes < int64(peekBytes) {
   111  		peekBytes = int(remainingBytes)
   112  		moreContentAvailable = false
   113  	}
   114  	logger.Debugf("Remaining bytes=[%d], Going to peek [%d] bytes", remainingBytes, peekBytes)
   115  	if lenBytes, err = s.reader.Peek(peekBytes); err != nil {
   116  		return nil, nil, err
   117  	}
   118  	length, n := proto.DecodeVarint(lenBytes)
   119  	if n == 0 {
   120  		// proto.DecodeVarint did not consume any byte at all which means that the bytes
   121  		// representing the size of the block are partial bytes
   122  		if !moreContentAvailable {
   123  			return nil, nil, ErrUnexpectedEndOfBlockfile
   124  		}
   125  		panic(fmt.Errorf("Error in decoding varint bytes [%#v]", lenBytes))
   126  	}
   127  	bytesExpected := int64(n) + int64(length)
   128  	if bytesExpected > remainingBytes {
   129  		logger.Debugf("At least [%d] bytes expected. Remaining bytes = [%d]. Returning with error [%s]",
   130  			bytesExpected, remainingBytes, ErrUnexpectedEndOfBlockfile)
   131  		return nil, nil, ErrUnexpectedEndOfBlockfile
   132  	}
   133  	// skip the bytes representing the block size
   134  	if _, err = s.reader.Discard(n); err != nil {
   135  		return nil, nil, err
   136  	}
   137  	blockBytes := make([]byte, length)
   138  	if _, err = io.ReadAtLeast(s.reader, blockBytes, int(length)); err != nil {
   139  		logger.Debugf("Error while trying to read [%d] bytes from fileNum [%d]: %s", length, s.fileNum, err)
   140  		return nil, nil, err
   141  	}
   142  	blockPlacementInfo := &blockPlacementInfo{
   143  		fileNum:          s.fileNum,
   144  		blockStartOffset: s.currentOffset,
   145  		blockBytesOffset: s.currentOffset + int64(n)}
   146  	s.currentOffset += int64(n) + int64(length)
   147  	logger.Debugf("Returning blockbytes - length=[%d], placementInfo={%s}", len(blockBytes), blockPlacementInfo)
   148  	return blockBytes, blockPlacementInfo, nil
   149  }
   150  
   151  func (s *blockfileStream) close() error {
   152  	return s.file.Close()
   153  }
   154  
   155  ///////////////////////////////////
   156  // blockStream functions
   157  ////////////////////////////////////
   158  func newBlockStream(rootDir string, startFileNum int, startOffset int64, endFileNum int) (*blockStream, error) {
   159  	startFileStream, err := newBlockfileStream(rootDir, startFileNum, startOffset)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return &blockStream{rootDir, startFileNum, endFileNum, startFileStream}, nil
   164  }
   165  
   166  func (s *blockStream) moveToNextBlockfileStream() error {
   167  	var err error
   168  	if err = s.currentFileStream.close(); err != nil {
   169  		return err
   170  	}
   171  	s.currentFileNum++
   172  	if s.currentFileStream, err = newBlockfileStream(s.rootDir, s.currentFileNum, 0); err != nil {
   173  		return err
   174  	}
   175  	return nil
   176  }
   177  
   178  func (s *blockStream) nextBlockBytes() ([]byte, error) {
   179  	blockBytes, _, err := s.nextBlockBytesAndPlacementInfo()
   180  	return blockBytes, err
   181  }
   182  
   183  func (s *blockStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) {
   184  	var blockBytes []byte
   185  	var blockPlacementInfo *blockPlacementInfo
   186  	var err error
   187  	if blockBytes, blockPlacementInfo, err = s.currentFileStream.nextBlockBytesAndPlacementInfo(); err != nil {
   188  		logger.Debugf("current file [%d] length of blockbytes [%d]. Err:%s", s.currentFileNum, len(blockBytes), err)
   189  		return nil, nil, err
   190  	}
   191  	logger.Debugf("blockbytes [%d] read from file [%d]", len(blockBytes), s.currentFileNum)
   192  	if blockBytes == nil && (s.currentFileNum < s.endFileNum || s.endFileNum < 0) {
   193  		logger.Debugf("current file [%d] exhausted. Moving to next file", s.currentFileNum)
   194  		if err = s.moveToNextBlockfileStream(); err != nil {
   195  			return nil, nil, err
   196  		}
   197  		return s.nextBlockBytesAndPlacementInfo()
   198  	}
   199  	return blockBytes, blockPlacementInfo, nil
   200  }
   201  
   202  func (s *blockStream) close() error {
   203  	return s.currentFileStream.close()
   204  }
   205  
   206  func (i *blockPlacementInfo) String() string {
   207  	return fmt.Sprintf("fileNum=[%d], startOffset=[%d], bytesOffset=[%d]",
   208  		i.fileNum, i.blockStartOffset, i.blockBytesOffset)
   209  }