Merge pull request #339 from harshavardhana/pr_out_de_couple_donut_into_smaller_files_useful_for_ease_in_external_integration
commit
b2ee7d989d
@ -0,0 +1,88 @@ |
|||||||
|
package donut |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"io/ioutil" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
) |
||||||
|
|
||||||
|
func newDonutFileWriter(objectDir string) (Writer, error) { |
||||||
|
dataFile, err := os.OpenFile(path.Join(objectDir, "data"), os.O_WRONLY|os.O_CREATE, 0600) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return donutFileWriter{ |
||||||
|
root: objectDir, |
||||||
|
file: dataFile, |
||||||
|
metadata: make(map[string]string), |
||||||
|
donutMetadata: make(map[string]string), |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
type donutFileWriter struct { |
||||||
|
root string |
||||||
|
file *os.File |
||||||
|
metadata map[string]string |
||||||
|
donutMetadata map[string]string |
||||||
|
err error |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) Write(data []byte) (int, error) { |
||||||
|
return d.file.Write(data) |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) Close() error { |
||||||
|
if d.err != nil { |
||||||
|
return d.err |
||||||
|
} |
||||||
|
metadata, _ := json.Marshal(d.metadata) |
||||||
|
ioutil.WriteFile(path.Join(d.root, "metadata.json"), metadata, 0600) |
||||||
|
donutMetadata, _ := json.Marshal(d.donutMetadata) |
||||||
|
ioutil.WriteFile(path.Join(d.root, "donutMetadata.json"), donutMetadata, 0600) |
||||||
|
|
||||||
|
return d.file.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) CloseWithError(err error) error { |
||||||
|
if d.err != nil { |
||||||
|
d.err = err |
||||||
|
} |
||||||
|
return d.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) SetMetadata(metadata map[string]string) error { |
||||||
|
for k := range d.metadata { |
||||||
|
delete(d.metadata, k) |
||||||
|
} |
||||||
|
for k, v := range metadata { |
||||||
|
d.metadata[k] = v |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) GetMetadata() (map[string]string, error) { |
||||||
|
metadata := make(map[string]string) |
||||||
|
for k, v := range d.metadata { |
||||||
|
metadata[k] = v |
||||||
|
} |
||||||
|
return metadata, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) SetDonutMetadata(metadata map[string]string) error { |
||||||
|
for k := range d.donutMetadata { |
||||||
|
delete(d.donutMetadata, k) |
||||||
|
} |
||||||
|
for k, v := range metadata { |
||||||
|
d.donutMetadata[k] = v |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d donutFileWriter) GetDonutMetadata() (map[string]string, error) { |
||||||
|
donutMetadata := make(map[string]string) |
||||||
|
for k, v := range d.donutMetadata { |
||||||
|
donutMetadata[k] = v |
||||||
|
} |
||||||
|
return donutMetadata, nil |
||||||
|
} |
@ -0,0 +1,137 @@ |
|||||||
|
package donut |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"io" |
||||||
|
"strconv" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/minio-io/minio/pkg/encoding/erasure" |
||||||
|
"github.com/minio-io/minio/pkg/utils/split" |
||||||
|
) |
||||||
|
|
||||||
|
func erasureReader(readers []io.ReadCloser, donutMetadata map[string]string, writer *io.PipeWriter) { |
||||||
|
totalChunks, _ := strconv.Atoi(donutMetadata["chunkCount"]) |
||||||
|
totalLeft, _ := strconv.Atoi(donutMetadata["totalLength"]) |
||||||
|
blockSize, _ := strconv.Atoi(donutMetadata["blockSize"]) |
||||||
|
params, _ := erasure.ParseEncoderParams(8, 8, erasure.Cauchy) |
||||||
|
encoder := erasure.NewEncoder(params) |
||||||
|
for _, reader := range readers { |
||||||
|
defer reader.Close() |
||||||
|
} |
||||||
|
for i := 0; i < totalChunks; i++ { |
||||||
|
encodedBytes := make([][]byte, 16) |
||||||
|
for i, reader := range readers { |
||||||
|
var bytesBuffer bytes.Buffer |
||||||
|
io.Copy(&bytesBuffer, reader) |
||||||
|
encodedBytes[i] = bytesBuffer.Bytes() |
||||||
|
} |
||||||
|
curBlockSize := totalLeft |
||||||
|
if blockSize < totalLeft { |
||||||
|
curBlockSize = blockSize |
||||||
|
} |
||||||
|
decodedData, err := encoder.Decode(encodedBytes, curBlockSize) |
||||||
|
if err != nil { |
||||||
|
writer.CloseWithError(err) |
||||||
|
return |
||||||
|
} |
||||||
|
io.Copy(writer, bytes.NewBuffer(decodedData)) |
||||||
|
totalLeft = totalLeft - blockSize |
||||||
|
} |
||||||
|
writer.Close() |
||||||
|
} |
||||||
|
|
||||||
|
// erasure writer
|
||||||
|
|
||||||
|
type erasureWriter struct { |
||||||
|
writers []Writer |
||||||
|
metadata map[string]string |
||||||
|
donutMetadata map[string]string // not exposed
|
||||||
|
erasureWriter *io.PipeWriter |
||||||
|
isClosed <-chan bool |
||||||
|
} |
||||||
|
|
||||||
|
func newErasureWriter(writers []Writer) ObjectWriter { |
||||||
|
r, w := io.Pipe() |
||||||
|
isClosed := make(chan bool) |
||||||
|
writer := erasureWriter{ |
||||||
|
writers: writers, |
||||||
|
metadata: make(map[string]string), |
||||||
|
erasureWriter: w, |
||||||
|
isClosed: isClosed, |
||||||
|
} |
||||||
|
go erasureGoroutine(r, writer, isClosed) |
||||||
|
return writer |
||||||
|
} |
||||||
|
|
||||||
|
func erasureGoroutine(r *io.PipeReader, eWriter erasureWriter, isClosed chan<- bool) { |
||||||
|
chunks := split.Stream(r, 10*1024*1024) |
||||||
|
params, _ := erasure.ParseEncoderParams(8, 8, erasure.Cauchy) |
||||||
|
encoder := erasure.NewEncoder(params) |
||||||
|
chunkCount := 0 |
||||||
|
totalLength := 0 |
||||||
|
for chunk := range chunks { |
||||||
|
if chunk.Err == nil { |
||||||
|
totalLength = totalLength + len(chunk.Data) |
||||||
|
encodedBlocks, _ := encoder.Encode(chunk.Data) |
||||||
|
for blockIndex, block := range encodedBlocks { |
||||||
|
io.Copy(eWriter.writers[blockIndex], bytes.NewBuffer(block)) |
||||||
|
} |
||||||
|
} |
||||||
|
chunkCount = chunkCount + 1 |
||||||
|
} |
||||||
|
metadata := make(map[string]string) |
||||||
|
metadata["blockSize"] = strconv.Itoa(10 * 1024 * 1024) |
||||||
|
metadata["chunkCount"] = strconv.Itoa(chunkCount) |
||||||
|
metadata["created"] = time.Now().Format(time.RFC3339Nano) |
||||||
|
metadata["erasureK"] = "8" |
||||||
|
metadata["erasureM"] = "8" |
||||||
|
metadata["erasureTechnique"] = "Cauchy" |
||||||
|
metadata["totalLength"] = strconv.Itoa(totalLength) |
||||||
|
for _, nodeWriter := range eWriter.writers { |
||||||
|
if nodeWriter != nil { |
||||||
|
nodeWriter.SetMetadata(eWriter.metadata) |
||||||
|
nodeWriter.SetDonutMetadata(metadata) |
||||||
|
nodeWriter.Close() |
||||||
|
} |
||||||
|
} |
||||||
|
isClosed <- true |
||||||
|
} |
||||||
|
|
||||||
|
func (d erasureWriter) Write(data []byte) (int, error) { |
||||||
|
io.Copy(d.erasureWriter, bytes.NewBuffer(data)) |
||||||
|
return len(data), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d erasureWriter) Close() error { |
||||||
|
d.erasureWriter.Close() |
||||||
|
<-d.isClosed |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d erasureWriter) CloseWithError(err error) error { |
||||||
|
for _, writer := range d.writers { |
||||||
|
if writer != nil { |
||||||
|
writer.CloseWithError(err) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d erasureWriter) SetMetadata(metadata map[string]string) error { |
||||||
|
for k := range d.metadata { |
||||||
|
delete(d.metadata, k) |
||||||
|
} |
||||||
|
for k, v := range metadata { |
||||||
|
d.metadata[k] = v |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (d erasureWriter) GetMetadata() (map[string]string, error) { |
||||||
|
metadata := make(map[string]string) |
||||||
|
for k, v := range d.metadata { |
||||||
|
metadata[k] = v |
||||||
|
} |
||||||
|
return metadata, nil |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
package donut |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
"path" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"encoding/json" |
||||||
|
"path/filepath" |
||||||
|
) |
||||||
|
|
||||||
|
type localDirectoryNode struct { |
||||||
|
root string |
||||||
|
} |
||||||
|
|
||||||
|
func (d localDirectoryNode) GetBuckets() ([]string, error) { |
||||||
|
return nil, errors.New("Not Implemented") |
||||||
|
} |
||||||
|
|
||||||
|
func (d localDirectoryNode) GetWriter(bucket, object string) (Writer, error) { |
||||||
|
objectPath := path.Join(d.root, bucket, object) |
||||||
|
err := os.MkdirAll(objectPath, 0700) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return newDonutFileWriter(objectPath) |
||||||
|
} |
||||||
|
|
||||||
|
func (d localDirectoryNode) GetReader(bucket, object string) (io.ReadCloser, error) { |
||||||
|
return os.Open(path.Join(d.root, bucket, object, "data")) |
||||||
|
} |
||||||
|
|
||||||
|
func (d localDirectoryNode) GetMetadata(bucket, object string) (map[string]string, error) { |
||||||
|
return d.getMetadata(bucket, object, "metadata.json") |
||||||
|
} |
||||||
|
func (d localDirectoryNode) GetDonutMetadata(bucket, object string) (map[string]string, error) { |
||||||
|
return d.getMetadata(bucket, object, "donutMetadata.json") |
||||||
|
} |
||||||
|
|
||||||
|
func (d localDirectoryNode) getMetadata(bucket, object, fileName string) (map[string]string, error) { |
||||||
|
file, err := os.Open(path.Join(d.root, bucket, object, fileName)) |
||||||
|
defer file.Close() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
metadata := make(map[string]string) |
||||||
|
decoder := json.NewDecoder(file) |
||||||
|
if err := decoder.Decode(&metadata); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return metadata, nil |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func (d localDirectoryNode) ListObjects(bucketName string) ([]string, error) { |
||||||
|
prefix := path.Join(d.root, bucketName) |
||||||
|
var objects []string |
||||||
|
if err := filepath.Walk(prefix, func(path string, info os.FileInfo, err error) error { |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if !info.IsDir() && strings.HasSuffix(path, "data") { |
||||||
|
object := strings.TrimPrefix(path, prefix+"/") |
||||||
|
object = strings.TrimSuffix(object, "/data") |
||||||
|
objects = append(objects, object) |
||||||
|
} |
||||||
|
return nil |
||||||
|
}); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
sort.Strings(objects) |
||||||
|
return objects, nil |
||||||
|
} |
Loading…
Reference in new issue