Skip to content

Object storage API and sample implementation#1

Open
jssmith wants to merge 12 commits intomasterfrom
objstore
Open

Object storage API and sample implementation#1
jssmith wants to merge 12 commits intomasterfrom
objstore

Conversation

@jssmith
Copy link
Copy Markdown
Collaborator

@jssmith jssmith commented Aug 20, 2019

We are moving forward with a prototype object storage API implementation using gRPC as the transport. What's important about this contribution is the interface, not the implementation. What's most important to review is the gRPC file: pkg/objstore/objstore.proto.

Some notes:

  • This implementation is all Go and uses a local file system to store the objects. We are using this to get the interface right, then will do a C++ implementation wrapping Anna.
  • gRPC has greater latency than Thrift, however it is better suited to streaming large objects. See experience at Alluxio

Some key questions:

  • How should object versions work?
  • Do we want to be support multi-part object uploads?
  • What are we doing for permissions / ACLs?
  • What do we want to do for location, e.g., regions?
  • What concurrency models / consistency guarantees do we support? Are such guarantees part of the interface / spec or not?
    • How are we encoding error codes? Can we use standard gRPC status or should we encode our own errors on top of gRPC?
  • Should we be checking in the Go code generated by protoc? That would make builds simpler by eliminating a dependency?

}

message GetResponse {
bytes data = 1;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include objectName for async request/responses?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand how gRPC asyc requests work but it seems that there should be some id associated with the request, rather than in the data. There appears to be a provision for tagging the responses, e.g., in this example.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could create separate, async versions of each of the requests, where the request returns a handle that one could wait upon.

Comment thread pkg/objstore/objstore.proto Outdated
bytes data = 3;
}

message Empty {} No newline at end of file
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This shouldn't need to be redefined. You can use import "google/protobuf/empty.proto"; and then return google.protobuf.Empty. There are a bunch of commonly used protobuf definitions in that package, which you can find here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's try that. I was going to go that way but then figured this one is actually simpler to define than to import. But maybe some of the other definitions are useful.

Comment thread pkg/objstore/objstore.proto Outdated
- What do we want to do for location, e.g., regions?
- How do object versions work?
- What concurrency models / consistency guarantees do we support
- How are we encoding error codes? Can we use standard rRPC status (https://github.com/grpc/grpc/blob/master/doc/statuscodes.md)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there standard ways to translate those codes into client languages? If not, you can just use protobuf enums, and those are compiled into enums in the corresponding language, which helps with readability quite a bit (e.g., here).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main advantage that I see in using the standard error codes, at least in Go, is that you check the error once.

Here's what it could end up looking like with two levels of errors:

func doPut(objectName string, objectData []byte) {
    // ... get client and context
    r, err := client.Put(ctx, &pb.PutRequest{ObjectName:objectName,ObjectData:data})
    if err != nil {
        return err
    }
    if r.ErrCode != pb.OK {
        return NewObjStoreErr(r.ErrCode)
    }
    return nil
}

vs

func doPut(objectName string, objectData []byte) {
    // ... get client and context
    r, err := client.Put(ctx, &pb.PutRequest{ObjectName:objectName,ObjectData:data})
    if err != nil {
        return err
    }
    return nil
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for standard error codes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are planning to go down the path of standard error codes. It definitely makes the code nicer. One disadvantage we should be mindful of is that the errors become a more explicit part of the interface when we define them as part of the protobuf messages. Fully specifying the behavior requires tests either way, though.

Comment thread pkg/objstore/objstore.proto Outdated

message CreateBucketRequest {
string bucketName = 1;
// TODO add permissions
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permissions would make sense in a multi-tenant setting. In the worst case, if a store does not support it, it can ignore this value.

}

message GetResponse {
bytes data = 1;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could create separate, async versions of each of the requests, where the request returns a handle that one could wait upon.

*/


service ObjectStore {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think deleteBucket(bucketName), delete(objectName) are missing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if we do decide to add permissions, some way of looking up permissions..

Comment thread pkg/objstore/objstore.proto Outdated
message CreateBucketRequest {
string bucketName = 1;
// TODO add permissions
// TODO add location
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would location correspond to? Geographic region?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I had in mind. Going off of the S3 API it's a region. In principle it could mean something else though, e.g., some locality group.

Comment thread pkg/objstore/objstore.proto Outdated
- What do we want to do for location, e.g., regions?
- How do object versions work?
- What concurrency models / consistency guarantees do we support
- How are we encoding error codes? Can we use standard rRPC status (https://github.com/grpc/grpc/blob/master/doc/statuscodes.md)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for standard error codes.

@vsreekanti
Copy link
Copy Markdown
Collaborator

Would be good to have comments on Protobuf definitions (especially to document decisions to explicitly include/exclude certain things in our existing API) and on the reference implementation to explain what the moving parts in a gRPC object store service are.

@jssmith
Copy link
Copy Markdown
Collaborator Author

jssmith commented Sep 5, 2019

Status update:

  • Libo has added some documentation and I've contributed as well. We want the documentation to render properly using the godoc tool, but right now some of it doesn't show up. @Chenlibo, can you please have a look at this?
  • The benchmark client currently is a standalone program rather than one that is integrated with the SRK tool and that can run in a cloud function environment. We need to change this.

…d gRPC server. Prelim bench shows that the throughput of Anna is 1/4 of that of local file system (1, 10m files, 1 thread). DeleteBucket remains buggy since I'm not sure how to remove a setLattice
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants