github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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  		// file.Seek does not raise an error - simply seeks to the new position
    75  		return nil, err
    76  	}
    77  	if newPosition != startOffset {
    78  		panic(fmt.Sprintf("Could not seek file [%s] to given startOffset [%d]. New position = [%d]",
    79  			filePath, startOffset, newPosition))
    80  	}
    81  	s := &blockfileStream{fileNum, file, bufio.NewReader(file), startOffset}
    82  	return s, nil
    83  }
    84  
    85  func (s *blockfileStream) nextBlockBytes() ([]byte, error) {
    86  	blockBytes, _, err := s.nextBlockBytesAndPlacementInfo()
    87  	return blockBytes, err
    88  }
    89  
    90  // nextBlockBytesAndPlacementInfo returns bytes for the next block
    91  // along with the offset information in the block file.
    92  // An error `ErrUnexpectedEndOfBlockfile` is returned if a partial written data is detected
    93  // which is possible towards the tail of the file if a crash had taken place during appending of a block
    94  func (s *blockfileStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) {
    95  	var lenBytes []byte
    96  	var err error
    97  	var fileInfo os.FileInfo
    98  	moreContentAvailable := true
    99  
   100  	if fileInfo, err = s.file.Stat(); err != nil {
   101  		return nil, nil, err
   102  	}
   103  	if s.currentOffset == fileInfo.Size() {
   104  		logger.Debugf("Finished reading file number [%d]", s.fileNum)
   105  		return nil, nil, nil
   106  	}
   107  	remainingBytes := fileInfo.Size() - s.currentOffset
   108  	// Peek 8 or smaller number of bytes (if remaining bytes are less than 8)
   109  	// Assumption is that a block size would be small enough to be represented in 8 bytes varint
   110  	peekBytes := 8
   111  	if remainingBytes < int64(peekBytes) {
   112  		peekBytes = int(remainingBytes)
   113  		moreContentAvailable = false
   114  	}
   115  	logger.Debugf("Remaining bytes=[%d], Going to peek [%d] bytes", remainingBytes, peekBytes)
   116  	if lenBytes, err = s.reader.Peek(peekBytes); err != nil {
   117  		return nil, nil, err
   118  	}
   119  	length, n := proto.DecodeVarint(lenBytes)
   120  	if n == 0 {
   121  		// proto.DecodeVarint did not consume any byte at all which means that the bytes
   122  		// representing the size of the block are partial bytes
   123  		if !moreContentAvailable {
   124  			return nil, nil, ErrUnexpectedEndOfBlockfile
   125  		}
   126  		panic(fmt.Errorf("Error in decoding varint bytes [%#v]", lenBytes))
   127  	}
   128  	bytesExpected := int64(n) + int64(length)
   129  	if bytesExpected > remainingBytes {
   130  		logger.Debugf("At least [%d] bytes expected. Remaining bytes = [%d]. Returning with error [%s]",
   131  			bytesExpected, remainingBytes, ErrUnexpectedEndOfBlockfile)
   132  		return nil, nil, ErrUnexpectedEndOfBlockfile
   133  	}
   134  	// skip the bytes representing the block size
   135  	if _, err = s.reader.Discard(n); err != nil {
   136  		return nil, nil, err
   137  	}
   138  	blockBytes := make([]byte, length)
   139  	if _, err = io.ReadAtLeast(s.reader, blockBytes, int(length)); err != nil {
   140  		logger.Debugf("Error while trying to read [%d] bytes from fileNum [%d]: %s", length, s.fileNum, err)
   141  		return nil, nil, err
   142  	}
   143  	blockPlacementInfo := &blockPlacementInfo{
   144  		fileNum:          s.fileNum,
   145  		blockStartOffset: s.currentOffset,
   146  		blockBytesOffset: s.currentOffset + int64(n)}
   147  	s.currentOffset += int64(n) + int64(length)
   148  	logger.Debugf("Returning blockbytes - length=[%d], placementInfo={%s}", len(blockBytes), blockPlacementInfo)
   149  	return blockBytes, blockPlacementInfo, nil
   150  }
   151  
   152  func (s *blockfileStream) close() error {
   153  	return s.file.Close()
   154  }
   155  
   156  ///////////////////////////////////
   157  // blockStream functions
   158  ////////////////////////////////////
   159  func newBlockStream(rootDir string, startFileNum int, startOffset int64, endFileNum int) (*blockStream, error) {
   160  	startFileStream, err := newBlockfileStream(rootDir, startFileNum, startOffset)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	return &blockStream{rootDir, startFileNum, endFileNum, startFileStream}, nil
   165  }
   166  
   167  func (s *blockStream) moveToNextBlockfileStream() error {
   168  	var err error
   169  	if err = s.currentFileStream.close(); err != nil {
   170  		return err
   171  	}
   172  	s.currentFileNum++
   173  	if s.currentFileStream, err = newBlockfileStream(s.rootDir, s.currentFileNum, 0); err != nil {
   174  		return err
   175  	}
   176  	return nil
   177  }
   178  
   179  func (s *blockStream) nextBlockBytes() ([]byte, error) {
   180  	blockBytes, _, err := s.nextBlockBytesAndPlacementInfo()
   181  	return blockBytes, err
   182  }
   183  
   184  func (s *blockStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) {
   185  	var blockBytes []byte
   186  	var blockPlacementInfo *blockPlacementInfo
   187  	var err error
   188  	if blockBytes, blockPlacementInfo, err = s.currentFileStream.nextBlockBytesAndPlacementInfo(); err != nil {
   189  		logger.Debugf("current file [%d]", s.currentFileNum)
   190  		logger.Debugf("blockbytes [%d]. Err:%s", len(blockBytes), err)
   191  		return nil, nil, err
   192  	}
   193  	logger.Debugf("blockbytes [%d] read from file [%d]", len(blockBytes), s.currentFileNum)
   194  	if blockBytes == nil && (s.currentFileNum < s.endFileNum || s.endFileNum < 0) {
   195  		logger.Debugf("current file [%d] exhausted. Moving to next file", s.currentFileNum)
   196  		if err = s.moveToNextBlockfileStream(); err != nil {
   197  			return nil, nil, err
   198  		}
   199  		return s.nextBlockBytesAndPlacementInfo()
   200  	}
   201  	return blockBytes, blockPlacementInfo, nil
   202  }
   203  
   204  func (s *blockStream) close() error {
   205  	return s.currentFileStream.close()
   206  }
   207  
   208  func (i *blockPlacementInfo) String() string {
   209  	return fmt.Sprintf("fileNum=[%d], startOffset=[%d], bytesOffset=[%d]",
   210  		i.fileNum, i.blockStartOffset, i.blockBytesOffset)
   211  }