Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate blobs from metadata in the ocis driver #1452

Merged
merged 28 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a713203
Extract generic part of s3ng into the "decomposed" package
aduffeck Feb 3, 2021
5684477
Switch the ocis driver over to the decomposed base driver
aduffeck Feb 10, 2021
18fd934
Move the shared code in fs/decomposed to utils
aduffeck Feb 10, 2021
ee37d45
Apply trash purge fix from the ocis driver
aduffeck Feb 4, 2021
326f34b
Add changelog
aduffeck Feb 4, 2021
b1b4318
Make hound happy
aduffeck Feb 4, 2021
b552bd9
Make Download return an io.ReadSeeker for range downloads
aduffeck Feb 5, 2021
7c49301
Switch the publiclink service to the ocis driver as well
aduffeck Feb 5, 2021
2c49a2e
Fix filename of the users service in the README
aduffeck Feb 5, 2021
2156f46
Fix revision file sizes by reading the blobsize xattr
aduffeck Feb 5, 2021
ac09e71
Provide a prefilled test environment for unit testing
aduffeck Feb 8, 2021
ff20832
Store a blobID with each node to uniquely identify it in the blobstore
aduffeck Feb 8, 2021
cc4f08a
Refine changelog
aduffeck Feb 10, 2021
cb42586
Make golint happy
aduffeck Feb 10, 2021
fb16e3d
Incorporate review suggestions
aduffeck Feb 15, 2021
a419edf
Use the upload id as the blob id
aduffeck Feb 12, 2021
f82ae2e
Do not try to delete blobs when deleting a directory node
aduffeck Feb 16, 2021
6765f54
Fix rebase artifacts
aduffeck Feb 26, 2021
c5899a5
Extract reading the blobSize attr in a separate method
aduffeck Mar 2, 2021
cb460f4
Fix TreeSizeAccounting by making it use the blobSize xattr
aduffeck Mar 2, 2021
a47ced0
Move tree size calculation to the tree package
aduffeck Mar 2, 2021
3651604
Improve changelog entry
aduffeck Mar 2, 2021
ec6d597
Improve publiclink storage test service configuration
aduffeck Mar 2, 2021
e7e05fa
Refine comment
aduffeck Mar 2, 2021
fa14553
Reuse bs.path for calculating the blob path
aduffeck Mar 3, 2021
7ba87a6
Make sure the calculated path lies inside the blobstore root
aduffeck Mar 3, 2021
7bb67e1
Readd windows compatibility fix from master
aduffeck Mar 3, 2021
d27102e
Fix file names
aduffeck Mar 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ You can also read the [build from sources guide](https://reva.link/docs/getting-
../../../cmd/revad/revad -c gateway.toml &
../../../cmd/revad/revad -c shares.toml &
../../../cmd/revad/revad -c storage-home.toml &
../../../cmd/revad/revad -c storage-oc.toml &
../../../cmd/revad/revad -c storage-users.toml &
../../../cmd/revad/revad -c storage-publiclink.toml &
../../../cmd/revad/revad -c ldap-users.toml
```
Expand Down
12 changes: 12 additions & 0 deletions changelog/unreleased/separate-blobs-from-metadata-in-ocis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Change: Separate blobs from metadata in the ocis storage driver

We changed the ocis storage driver to keep the file content separate from the
metadata by storing the blobs in a separate directory. This allows for using
a different (potentially faster) storage for the metadata.

**Note** This change makes existing ocis storages incompatible with the new code.

We also streamlined the ocis and the s3ng drivers so that most of the code is
shared between them.

https://github.com/cs3org/reva/pull/1452
83 changes: 83 additions & 0 deletions pkg/storage/fs/ocis/blobstore/blobstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2018-2021 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package blobstore

import (
"bufio"
"io"
"os"
"path/filepath"

"github.com/pkg/errors"
)

// Blobstore provides an interface to an filesystem based blobstore
type Blobstore struct {
root string
}

// New returns a new Blobstore
func New(root string) (*Blobstore, error) {
err := os.MkdirAll(root, 0700)
if err != nil {
return nil, err
}

return &Blobstore{
root: root,
}, nil
}

// Upload stores some data in the blobstore under the given key
func (bs *Blobstore) Upload(key string, data io.Reader) error {
f, err := os.OpenFile(bs.path(key), os.O_CREATE|os.O_WRONLY, 0700)
if err != nil {
return errors.Wrapf(err, "could not open blob '%s' for writing", key)
}

w := bufio.NewWriter(f)
_, err = w.ReadFrom(data)
if err != nil {
return errors.Wrapf(err, "could not write blob '%s'", key)
}

return w.Flush()
}

// Download retrieves a blob from the blobstore for reading
func (bs *Blobstore) Download(key string) (io.ReadCloser, error) {
file, err := os.Open(bs.path(key))
if err != nil {
return nil, errors.Wrapf(err, "could not read blob '%s'", key)
}
return file, nil
}

// Delete deletes a blob from the blobstore
func (bs *Blobstore) Delete(key string) error {
err := os.Remove(bs.path(key))
if err != nil {
return errors.Wrapf(err, "could not delete blob '%s'", key)
}
return nil
}

func (bs *Blobstore) path(key string) string {
return filepath.Join(bs.root, filepath.Clean(filepath.Join("/", key)))
}
31 changes: 31 additions & 0 deletions pkg/storage/fs/ocis/blobstore/blobstore_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2018-2021 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package blobstore_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestBlobstore(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Blobstore Suite")
}
118 changes: 118 additions & 0 deletions pkg/storage/fs/ocis/blobstore/blobstore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2018-2021 CERN
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package blobstore_test

import (
"bytes"
"io/ioutil"
"os"
"path"
"strings"

"github.com/cs3org/reva/pkg/storage/fs/ocis/blobstore"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Blobstore", func() {
var (
tmpRoot string
key string
blobPath string
data []byte

bs *blobstore.Blobstore
)

BeforeEach(func() {
var err error
tmpRoot, err = ioutil.TempDir("", "reva-unit-tests-*-root")
Expect(err).ToNot(HaveOccurred())

data = []byte("1234567890")
key = "foo"
blobPath = path.Join(tmpRoot, "blobs", key)

bs, err = blobstore.New(path.Join(tmpRoot, "blobs"))
Expect(err).ToNot(HaveOccurred())
})

AfterEach(func() {
if strings.HasPrefix(tmpRoot, os.TempDir()) {
os.RemoveAll(tmpRoot)
}
})

It("creates the root directory if it doesn't exist", func() {
_, err := os.Stat(path.Join(tmpRoot, "blobs"))
Expect(err).ToNot(HaveOccurred())
})

Describe("Upload", func() {
It("writes the blob", func() {
err := bs.Upload(key, bytes.NewReader(data))
Expect(err).ToNot(HaveOccurred())

writtenBytes, err := ioutil.ReadFile(blobPath)
Expect(err).ToNot(HaveOccurred())
Expect(writtenBytes).To(Equal(data))
})
})

Context("with an existing blob", func() {
BeforeEach(func() {
Expect(ioutil.WriteFile(blobPath, data, 0700)).To(Succeed())
})

Describe("Download", func() {
It("cleans the key", func() {
reader, err := bs.Download("../" + key)
Expect(err).ToNot(HaveOccurred())

readData, err := ioutil.ReadAll(reader)
Expect(err).ToNot(HaveOccurred())
Expect(readData).To(Equal(data))
})

It("returns a reader to the blob", func() {
reader, err := bs.Download(key)
Expect(err).ToNot(HaveOccurred())

readData, err := ioutil.ReadAll(reader)
Expect(err).ToNot(HaveOccurred())
Expect(readData).To(Equal(data))
})
})

Describe("Delete", func() {
It("deletes the blob", func() {
_, err := os.Stat(blobPath)
Expect(err).ToNot(HaveOccurred())

err = bs.Delete(key)
Expect(err).ToNot(HaveOccurred())

_, err = os.Stat(blobPath)
Expect(err).To(HaveOccurred())
})
})
})

})
Loading