Avoid frivolous GetObjectMetadata() calls at driver level, return back all the information in donut ListObjects()

master
Harshavardhana 10 years ago
parent f05ad062ee
commit 6921328b93
  1. 76
      pkg/storage/donut/bucket.go
  2. 2
      pkg/storage/donut/definitions.go
  3. 10
      pkg/storage/donut/donut_test.go
  4. 31
      pkg/storage/drivers/donut/donut.go

@ -84,28 +84,6 @@ func newBucket(bucketName, aclType, donutName string, nodes map[string]node) (bu
func (b bucket) getBucketName() string { func (b bucket) getBucketName() string {
return b.name return b.name
} }
func (b bucket) GetObjectMetadata(objectName string) (ObjectMetadata, error) {
b.lock.RLock()
defer b.lock.RUnlock()
metadataReaders, err := b.getDiskReaders(normalizeObjectName(objectName), objectMetadataConfig)
if err != nil {
return ObjectMetadata{}, iodine.New(err, nil)
}
for _, metadataReader := range metadataReaders {
defer metadataReader.Close()
}
objMetadata := ObjectMetadata{}
for _, metadataReader := range metadataReaders {
jdec := json.NewDecoder(metadataReader)
if err := jdec.Decode(&objMetadata); err != nil {
return ObjectMetadata{}, iodine.New(err, nil)
}
return objMetadata, nil
}
return ObjectMetadata{}, iodine.New(InvalidArgument{}, nil)
}
func (b bucket) getBucketMetadataReaders() ([]io.ReadCloser, error) { func (b bucket) getBucketMetadataReaders() ([]io.ReadCloser, error) {
var readers []io.ReadCloser var readers []io.ReadCloser
for _, node := range b.nodes { for _, node := range b.nodes {
@ -144,6 +122,13 @@ func (b bucket) getBucketMetadata() (*AllBuckets, error) {
return nil, iodine.New(InvalidArgument{}, nil) return nil, iodine.New(InvalidArgument{}, nil)
} }
// GetObjectMetadata - get metadata for an object
func (b bucket) GetObjectMetadata(objectName string) (ObjectMetadata, error) {
b.lock.RLock()
defer b.lock.RUnlock()
return b.readObjectMetadata(objectName)
}
// ListObjects - list all objects // ListObjects - list all objects
func (b bucket) ListObjects(prefix, marker, delimiter string, maxkeys int) (ListObjects, error) { func (b bucket) ListObjects(prefix, marker, delimiter string, maxkeys int) (ListObjects, error) {
b.lock.RLock() b.lock.RLock()
@ -191,13 +176,20 @@ func (b bucket) ListObjects(prefix, marker, delimiter string, maxkeys int) (List
} }
results = AppendU(results, prefix+objectName) results = AppendU(results, prefix+objectName)
} }
sort.Strings(results)
sort.Strings(commonPrefixes) sort.Strings(commonPrefixes)
listObjects := ListObjects{} listObjects := ListObjects{}
listObjects.Objects = results listObjects.Objects = make(map[string]ObjectMetadata)
listObjects.CommonPrefixes = commonPrefixes listObjects.CommonPrefixes = commonPrefixes
listObjects.IsTruncated = isTruncated listObjects.IsTruncated = isTruncated
for _, objectName := range results {
objMetadata, err := b.readObjectMetadata(normalizeObjectName(objectName))
if err != nil {
return ListObjects{}, iodine.New(err, nil)
}
listObjects.Objects[objectName] = objMetadata
}
return listObjects, nil return listObjects, nil
} }
@ -215,21 +207,10 @@ func (b bucket) ReadObject(objectName string) (reader io.ReadCloser, size int64,
if _, ok := bucketMetadata.Buckets[b.getBucketName()].BucketObjects[objectName]; !ok { if _, ok := bucketMetadata.Buckets[b.getBucketName()].BucketObjects[objectName]; !ok {
return nil, 0, iodine.New(ObjectNotFound{Object: objectName}, nil) return nil, 0, iodine.New(ObjectNotFound{Object: objectName}, nil)
} }
objMetadata := ObjectMetadata{} objMetadata, err := b.readObjectMetadata(normalizeObjectName(objectName))
metadataReaders, err := b.getDiskReaders(normalizeObjectName(objectName), objectMetadataConfig)
if err != nil { if err != nil {
return nil, 0, iodine.New(err, nil) return nil, 0, iodine.New(err, nil)
} }
for _, metadataReader := range metadataReaders {
defer metadataReader.Close()
}
for _, metadataReader := range metadataReaders {
jdec := json.NewDecoder(metadataReader)
if err := jdec.Decode(&objMetadata); err != nil {
return nil, 0, iodine.New(err, nil)
}
break
}
// read and reply back to GetObject() request in a go-routine // read and reply back to GetObject() request in a go-routine
go b.readEncodedData(normalizeObjectName(objectName), writer, objMetadata) go b.readEncodedData(normalizeObjectName(objectName), writer, objMetadata)
return reader, objMetadata.Size, nil return reader, objMetadata.Size, nil
@ -346,6 +327,29 @@ func (b bucket) writeObjectMetadata(objectName string, objMetadata *ObjectMetada
return nil return nil
} }
// readObjectMetadata - read object metadata
func (b bucket) readObjectMetadata(objectName string) (ObjectMetadata, error) {
objMetadata := ObjectMetadata{}
if objectName == "" {
return ObjectMetadata{}, iodine.New(InvalidArgument{}, nil)
}
objMetadataReaders, err := b.getDiskReaders(objectName, objectMetadataConfig)
if err != nil {
return ObjectMetadata{}, iodine.New(err, nil)
}
for _, objMetadataReader := range objMetadataReaders {
defer objMetadataReader.Close()
}
for _, objMetadataReader := range objMetadataReaders {
jdec := json.NewDecoder(objMetadataReader)
if err := jdec.Decode(&objMetadata); err != nil {
return ObjectMetadata{}, iodine.New(err, nil)
}
break
}
return objMetadata, nil
}
// TODO - This a temporary normalization of objectNames, need to find a better way // TODO - This a temporary normalization of objectNames, need to find a better way
// //
// normalizedObjectName - all objectNames with "/" get normalized to a simple objectName // normalizedObjectName - all objectNames with "/" get normalized to a simple objectName

@ -67,7 +67,7 @@ type BucketMetadata struct {
// ListObjects container for list objects response // ListObjects container for list objects response
type ListObjects struct { type ListObjects struct {
Objects []string `json:"objects"` Objects map[string]ObjectMetadata `json:"objects"`
CommonPrefixes []string `json:"commonPrefixes"` CommonPrefixes []string `json:"commonPrefixes"`
IsTruncated bool `json:"isTruncated"` IsTruncated bool `json:"isTruncated"`
} }

@ -93,7 +93,7 @@ func (s *MySuite) TestEmptyBucket(c *C) {
// check if bucket is empty // check if bucket is empty
listObjects, err := donut.ListObjects("foo", "", "", "", 1) listObjects, err := donut.ListObjects("foo", "", "", "", 1)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(listObjects.Objects, IsNil) c.Assert(len(listObjects.Objects), Equals, 0)
c.Assert(listObjects.CommonPrefixes, IsNil) c.Assert(listObjects.CommonPrefixes, IsNil)
c.Assert(listObjects.IsTruncated, Equals, false) c.Assert(listObjects.IsTruncated, Equals, false)
} }
@ -312,7 +312,8 @@ func (s *MySuite) TestMultipleNewObjects(c *C) {
// test list objects with only delimiter // test list objects with only delimiter
listObjects, err = donut.ListObjects("foo", "", "", "1", 10) listObjects, err = donut.ListObjects("foo", "", "", "1", 10)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(listObjects.Objects[0], Equals, "obj2") _, ok := listObjects.Objects["obj2"]
c.Assert(ok, Equals, true)
c.Assert(listObjects.IsTruncated, Equals, false) c.Assert(listObjects.IsTruncated, Equals, false)
c.Assert(listObjects.CommonPrefixes[0], Equals, "obj1") c.Assert(listObjects.CommonPrefixes[0], Equals, "obj1")
@ -320,7 +321,10 @@ func (s *MySuite) TestMultipleNewObjects(c *C) {
listObjects, err = donut.ListObjects("foo", "o", "", "", 10) listObjects, err = donut.ListObjects("foo", "o", "", "", 10)
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(listObjects.IsTruncated, Equals, false) c.Assert(listObjects.IsTruncated, Equals, false)
c.Assert(listObjects.Objects, DeepEquals, []string{"obj1", "obj2"}) _, ok1 := listObjects.Objects["obj1"]
_, ok2 := listObjects.Objects["obj2"]
c.Assert(ok1, Equals, true)
c.Assert(ok2, Equals, true)
three := ioutil.NopCloser(bytes.NewReader([]byte("three"))) three := ioutil.NopCloser(bytes.NewReader([]byte("three")))
metadata["contentLength"] = strconv.Itoa(len("three")) metadata["contentLength"] = strconv.Itoa(len("three"))

@ -448,6 +448,12 @@ func (d donutDriver) GetObjectMetadata(bucketName, objectName string) (drivers.O
return objectMetadata, nil return objectMetadata, nil
} }
type byObjectName []drivers.ObjectMetadata
func (b byObjectName) Len() int { return len(b) }
func (b byObjectName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byObjectName) Less(i, j int) bool { return b[i].Key < b[j].Key }
// ListObjects - returns list of objects // ListObjects - returns list of objects
func (d donutDriver) ListObjects(bucketName string, resources drivers.BucketResourcesMetadata) ([]drivers.ObjectMetadata, drivers.BucketResourcesMetadata, error) { func (d donutDriver) ListObjects(bucketName string, resources drivers.BucketResourcesMetadata) ([]drivers.ObjectMetadata, drivers.BucketResourcesMetadata, error) {
d.lock.RLock() d.lock.RLock()
@ -470,23 +476,18 @@ func (d donutDriver) ListObjects(bucketName string, resources drivers.BucketReso
} }
resources.CommonPrefixes = listObjects.CommonPrefixes resources.CommonPrefixes = listObjects.CommonPrefixes
resources.IsTruncated = listObjects.IsTruncated resources.IsTruncated = listObjects.IsTruncated
if resources.IsTruncated && resources.IsDelimiterSet() { var results []drivers.ObjectMetadata
resources.NextMarker = listObjects.Objects[len(listObjects.Objects)-1] for _, objMetadata := range listObjects.Objects {
}
// make sure to keep the lexical order same as returned by donut
// we do not have to sort here again
results := make([]drivers.ObjectMetadata, len(listObjects.Objects))
for i, objectName := range listObjects.Objects {
objectMetadata, err := d.donut.GetObjectMetadata(bucketName, objectName)
if err != nil {
return nil, drivers.BucketResourcesMetadata{}, iodine.New(err, errParams)
}
metadata := drivers.ObjectMetadata{ metadata := drivers.ObjectMetadata{
Key: objectMetadata.Object, Key: objMetadata.Object,
Created: objectMetadata.Created, Created: objMetadata.Created,
Size: objectMetadata.Size, Size: objMetadata.Size,
}
results = append(results, metadata)
} }
results[i] = metadata sort.Sort(byObjectName(results))
if resources.IsTruncated && resources.IsDelimiterSet() {
resources.NextMarker = results[len(results)-1].Key
} }
return results, resources, nil return results, resources, nil
} }

Loading…
Cancel
Save