XL/erasure-read: Avoid memory copy, write to writer directly all the dataBlocks.

master
Harshavardhana 9 years ago committed by Krishna Srinivas
parent d4bea5fbf8
commit 9b82e64a11
  1. 19
      erasure-readfile.go
  2. 79
      erasure-utils.go
  3. 8
      server_xl_test.go

@ -189,6 +189,7 @@ func erasureReadFile(writer io.Writer, disks []StorageAPI, volume string, path s
} }
} }
var outSize, outOffset int64
// enBlocks data can have 0-padding hence we need to figure the exact number // enBlocks data can have 0-padding hence we need to figure the exact number
// of bytes we want to read from enBlocks. // of bytes we want to read from enBlocks.
blockSize := eInfo.BlockSize blockSize := eInfo.BlockSize
@ -196,26 +197,24 @@ func erasureReadFile(writer io.Writer, disks []StorageAPI, volume string, path s
// For the last block, the block size can be less than BlockSize. // For the last block, the block size can be less than BlockSize.
blockSize = totalLength % eInfo.BlockSize blockSize = totalLength % eInfo.BlockSize
} }
data, err := getDataBlocks(enBlocks, eInfo.DataBlocks, int(blockSize))
if err != nil {
return bytesWritten, err
}
// If this is start block, skip unwanted bytes. // If this is start block, skip unwanted bytes.
if block == startBlock { if block == startBlock {
data = data[bytesToSkip:] outOffset = bytesToSkip
} }
if len(data) > int(length-bytesWritten) { // Total data to be read.
outSize = blockSize
if length-bytesWritten < blockSize {
// We should not send more data than what was requested. // We should not send more data than what was requested.
data = data[:length-bytesWritten] outSize = length - bytesWritten
} }
// Write data blocks.
_, err = writer.Write(data) n, err := writeDataBlocks(writer, enBlocks, eInfo.DataBlocks, outOffset, outSize)
if err != nil { if err != nil {
return bytesWritten, err return bytesWritten, err
} }
bytesWritten += int64(len(data)) bytesWritten += n
} }
return bytesWritten, nil return bytesWritten, nil

@ -17,6 +17,7 @@
package main package main
import ( import (
"bytes"
"crypto/sha512" "crypto/sha512"
"hash" "hash"
"io" "io"
@ -62,30 +63,74 @@ func hashSum(disk StorageAPI, volume, path string, writer hash.Hash) ([]byte, er
return writer.Sum(nil), nil return writer.Sum(nil), nil
} }
// getDataBlocks - fetches the data block only part of the input encoded blocks. // getDataBlockLen - get length of data blocks from encoded blocks.
func getDataBlocks(enBlocks [][]byte, dataBlocks int, curBlockSize int) (data []byte, err error) { func getDataBlockLen(enBlocks [][]byte, dataBlocks int) int {
if len(enBlocks) < dataBlocks {
return nil, reedsolomon.ErrTooFewShards
}
size := 0 size := 0
blocks := enBlocks[:dataBlocks] // Figure out the data block length.
for _, block := range blocks { for _, block := range enBlocks[:dataBlocks] {
size += len(block) size += len(block)
} }
if size < curBlockSize { return size
return nil, reedsolomon.ErrShortData }
// Writes all the data blocks from encoded blocks until requested
// outSize length. Provides a way to skip bytes until the offset.
func writeDataBlocks(dst io.Writer, enBlocks [][]byte, dataBlocks int, outOffset int64, outSize int64) (int64, error) {
// Do we have enough blocks?
if len(enBlocks) < dataBlocks {
return 0, reedsolomon.ErrTooFewShards
} }
write := curBlockSize // Do we have enough data?
for _, block := range blocks { if int64(getDataBlockLen(enBlocks, dataBlocks)) < outSize {
if write < len(block) { return 0, reedsolomon.ErrShortData
data = append(data, block[:write]...) }
return data, nil
// Counter to decrement total left to write.
write := outSize
// Counter to increment total written.
totalWritten := int64(0)
// Write all data blocks to dst.
for _, block := range enBlocks[:dataBlocks] {
// Skip blocks until we have reached our offset.
if outOffset >= int64(len(block)) {
// Decrement offset.
outOffset -= int64(len(block))
continue
} else {
// Skip until offset.
block = block[outOffset:]
// Reset the offset for next iteration to read everything
// from subsequent blocks.
outOffset = 0
}
// We have written all the blocks, write the last remaining block.
if write < int64(len(block)) {
n, err := io.Copy(dst, bytes.NewReader(block[:write]))
if err != nil {
return 0, err
}
totalWritten += n
break
}
// Copy the block.
n, err := io.Copy(dst, bytes.NewReader(block))
if err != nil {
return 0, err
} }
data = append(data, block...)
write -= len(block) // Decrement output size.
write -= n
// Increment written.
totalWritten += n
} }
return data, nil
// Success.
return totalWritten, nil
} }
// getBlockInfo - find start/end block and bytes to skip for given offset, length and block size. // getBlockInfo - find start/end block and bytes to skip for given offset, length and block size.

@ -840,15 +840,17 @@ func (s *MyAPIXLSuite) TestPartialContent(c *C) {
c.Assert(response.StatusCode, Equals, http.StatusOK) c.Assert(response.StatusCode, Equals, http.StatusOK)
// Prepare request // Prepare request
var table = []struct { var testCases = []struct {
byteRange string byteRange string
expectedString string expectedString string
}{ }{
{"6-7", "Wo"}, {"4-7", "o Wo"},
{"1-", "ello World"},
{"6-", "World"}, {"6-", "World"},
{"-2", "ld"},
{"-7", "o World"}, {"-7", "o World"},
} }
for _, t := range table { for _, t := range testCases {
request, err = newTestRequest("GET", s.testServer.Server.URL+"/partial-content/bar", request, err = newTestRequest("GET", s.testServer.Server.URL+"/partial-content/bar",
0, nil, s.testServer.AccessKey, s.testServer.SecretKey) 0, nil, s.testServer.AccessKey, s.testServer.SecretKey)
c.Assert(err, IsNil) c.Assert(err, IsNil)

Loading…
Cancel
Save