[#196] Add tagging caching

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-08-19 15:33:02 +03:00 committed by Alex Vanin
parent 987185b9e1
commit 5502fb97c3
5 changed files with 96 additions and 108 deletions

View file

@ -71,7 +71,7 @@ func (b *BucketInfo) SettingsObjectName() string {
return bktVersionSettingsObject return bktVersionSettingsObject
} }
// SettingsObjectKey is key to use in SystemCache. // SystemObjectKey is key to use in SystemCache.
func (b *BucketInfo) SettingsObjectKey() string { func (b *BucketInfo) SystemObjectKey(obj string) string {
return b.Name + bktVersionSettingsObject return b.Name + obj
} }

View file

@ -30,6 +30,11 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request
return return
} }
if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
p := &layer.HeadObjectParams{ p := &layer.HeadObjectParams{
Bucket: reqInfo.BucketName, Bucket: reqInfo.BucketName,
Object: reqInfo.ObjectName, Object: reqInfo.ObjectName,
@ -56,6 +61,11 @@ func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request
func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := api.GetReqInfo(r.Context()) reqInfo := api.GetReqInfo(r.Context())
if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
p := &layer.HeadObjectParams{ p := &layer.HeadObjectParams{
Bucket: reqInfo.BucketName, Bucket: reqInfo.BucketName,
Object: reqInfo.ObjectName, Object: reqInfo.ObjectName,
@ -89,6 +99,11 @@ func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Requ
VersionID: reqInfo.URL.Query().Get("versionId"), VersionID: reqInfo.URL.Query().Get("versionId"),
} }
if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
objInfo, err := h.obj.GetObjectInfo(r.Context(), p) objInfo, err := h.obj.GetObjectInfo(r.Context(), p)
if err != nil { if err != nil {
h.logAndSendError(w, "could not get object info", reqInfo, err) h.logAndSendError(w, "could not get object info", reqInfo, err)
@ -111,6 +126,11 @@ func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request
return return
} }
if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
if err := h.obj.PutBucketTagging(r.Context(), reqInfo.BucketName, tagSet); err != nil { if err := h.obj.PutBucketTagging(r.Context(), reqInfo.BucketName, tagSet); err != nil {
h.logAndSendError(w, "could not put object tagging", reqInfo, err) h.logAndSendError(w, "could not put object tagging", reqInfo, err)
} }
@ -119,6 +139,11 @@ func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request
func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := api.GetReqInfo(r.Context()) reqInfo := api.GetReqInfo(r.Context())
if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
tagSet, err := h.obj.GetBucketTagging(r.Context(), reqInfo.BucketName) tagSet, err := h.obj.GetBucketTagging(r.Context(), reqInfo.BucketName)
if err != nil { if err != nil {
h.logAndSendError(w, "could not get object tagging", reqInfo, err) h.logAndSendError(w, "could not get object tagging", reqInfo, err)
@ -132,6 +157,10 @@ func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request
func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := api.GetReqInfo(r.Context()) reqInfo := api.GetReqInfo(r.Context())
if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil {
h.logAndSendError(w, "expected owner doesn't match", reqInfo, err)
return
}
if err := h.obj.DeleteBucketTagging(r.Context(), reqInfo.BucketName); err != nil { if err := h.obj.DeleteBucketTagging(r.Context(), reqInfo.BucketName); err != nil {
h.logAndSendError(w, "could not delete object tagging", reqInfo, err) h.logAndSendError(w, "could not delete object tagging", reqInfo, err)
} }

View file

@ -348,28 +348,6 @@ func (n *layer) GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*Object
return n.headVersion(ctx, bkt, p.VersionID) return n.headVersion(ctx, bkt, p.VersionID)
} }
func (n *layer) getSettingsObjectInfo(ctx context.Context, bkt *cache.BucketInfo) (*ObjectInfo, error) {
if meta := n.systemCache.Get(bkt.SettingsObjectKey()); meta != nil {
return objInfoFromMeta(bkt, meta), nil
}
oid, err := n.objectFindID(ctx, &findParams{cid: bkt.CID, attr: objectSystemAttributeName, val: bkt.SettingsObjectName()})
if err != nil {
return nil, err
}
meta, err := n.objectHead(ctx, bkt.CID, oid)
if err != nil {
n.log.Error("could not fetch object head", zap.Error(err))
return nil, err
}
if err = n.systemCache.Put(bkt.SettingsObjectKey(), meta); err != nil {
n.log.Error("couldn't cache system object", zap.Error(err))
}
return objInfoFromMeta(bkt, meta), nil
}
// PutObject into storage. // PutObject into storage.
func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*ObjectInfo, error) { func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*ObjectInfo, error) {
bkt, err := n.GetBucketInfo(ctx, p.Bucket) bkt, err := n.GetBucketInfo(ctx, p.Bucket)
@ -458,19 +436,30 @@ func (n *layer) PutBucketTagging(ctx context.Context, bucketName string, tagSet
// DeleteObjectTagging from storage. // DeleteObjectTagging from storage.
func (n *layer) DeleteObjectTagging(ctx context.Context, p *ObjectInfo) error { func (n *layer) DeleteObjectTagging(ctx context.Context, p *ObjectInfo) error {
return n.deleteSystemObject(ctx, p.CID(), p.TagsObject()) bktInfo, err := n.GetBucketInfo(ctx, p.Bucket)
}
func (n *layer) deleteSystemObject(ctx context.Context, bktCID *cid.ID, name string) error {
oid, err := n.objectFindID(ctx, &findParams{cid: bktCID, attr: objectSystemAttributeName, val: name})
if err != nil { if err != nil {
if errors.IsS3Error(err, errors.ErrNoSuchKey) {
return nil
}
return err return err
} }
return n.deleteSystemObject(ctx, bktInfo, p.TagsObject())
}
return n.objectDelete(ctx, bktCID, oid) func (n *layer) deleteSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, name string) error {
var oid *object.ID
if meta := n.systemCache.Get(bktInfo.SystemObjectKey(name)); meta != nil {
oid = meta.ID()
} else {
var err error
oid, err = n.objectFindID(ctx, &findParams{cid: bktInfo.CID, attr: objectSystemAttributeName, val: name})
if err != nil {
if errors.IsS3Error(err, errors.ErrNoSuchKey) {
return nil
}
return err
}
}
n.systemCache.Delete(bktInfo.SystemObjectKey(name))
return n.objectDelete(ctx, bktInfo.CID, oid)
} }
// DeleteBucketTagging from storage. // DeleteBucketTagging from storage.
@ -480,13 +469,21 @@ func (n *layer) DeleteBucketTagging(ctx context.Context, bucketName string) erro
return err return err
} }
return n.deleteSystemObject(ctx, bktInfo.CID, formBucketTagObjectName(bucketName)) return n.deleteSystemObject(ctx, bktInfo, formBucketTagObjectName(bucketName))
} }
func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, objName string, metadata map[string]string, prefix string) (*object.ID, error) { func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo, objName string, metadata map[string]string, prefix string) (*object.Object, error) {
oldOID, err := n.objectFindID(ctx, &findParams{cid: bktInfo.CID, attr: objectSystemAttributeName, val: objName}) var (
if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) { err error
return nil, err oldOID *object.ID
)
if meta := n.systemCache.Get(bktInfo.SystemObjectKey(objName)); meta != nil {
oldOID = meta.ID()
} else {
oldOID, err = n.objectFindID(ctx, &findParams{cid: bktInfo.CID, attr: objectSystemAttributeName, val: objName})
if err != nil && !errors.IsS3Error(err, errors.ErrNoSuchKey) {
return nil, err
}
} }
attributes := make([]*object.Attribute, 0, 3) attributes := make([]*object.Attribute, 0, 3)
@ -526,19 +523,27 @@ func (n *layer) putSystemObject(ctx context.Context, bktInfo *cache.BucketInfo,
return nil, err return nil, err
} }
if _, err = n.objectHead(ctx, bktInfo.CID, oid); err != nil { meta, err := n.objectHead(ctx, bktInfo.CID, oid)
if err != nil {
return nil, err return nil, err
} }
if err = n.systemCache.Put(bktInfo.SystemObjectKey(objName), meta); err != nil {
n.log.Error("couldn't cache system object", zap.Error(err))
}
if oldOID != nil { if oldOID != nil {
if err = n.objectDelete(ctx, bktInfo.CID, oldOID); err != nil { if err = n.objectDelete(ctx, bktInfo.CID, oldOID); err != nil {
return nil, err return nil, err
} }
} }
return oid, nil return meta, nil
} }
func (n *layer) getSystemObject(ctx context.Context, bkt *cache.BucketInfo, objName string) (*ObjectInfo, error) { func (n *layer) getSystemObject(ctx context.Context, bkt *cache.BucketInfo, objName string) (*ObjectInfo, error) {
if meta := n.systemCache.Get(bkt.SystemObjectKey(objName)); meta != nil {
return objInfoFromMeta(bkt, meta), nil
}
oid, err := n.objectFindID(ctx, &findParams{cid: bkt.CID, attr: objectSystemAttributeName, val: objName}) oid, err := n.objectFindID(ctx, &findParams{cid: bkt.CID, attr: objectSystemAttributeName, val: objName})
if err != nil { if err != nil {
return nil, err return nil, err
@ -548,8 +553,11 @@ func (n *layer) getSystemObject(ctx context.Context, bkt *cache.BucketInfo, objN
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err = n.systemCache.Put(bkt.SystemObjectKey(objName), meta); err != nil {
n.log.Error("couldn't cache system object", zap.Error(err))
}
return objectInfoFromMeta(bkt, meta, "", ""), nil return objInfoFromMeta(bkt, meta), nil
} }
// CopyObject from one bucket into another bucket. // CopyObject from one bucket into another bucket.
@ -619,7 +627,7 @@ func (n *layer) deleteObject(ctx context.Context, bkt *cache.BucketInfo, obj *Ve
if err = n.objectDelete(ctx, bkt.CID, id); err != nil { if err = n.objectDelete(ctx, bkt.CID, id); err != nil {
return err return err
} }
if err = n.DeleteObjectTagging(ctx, &ObjectInfo{id: id, bucketID: bkt.CID, Name: obj.Name}); err != nil { if err = n.DeleteObjectTagging(ctx, &ObjectInfo{id: id, Bucket: bkt.Name, Name: obj.Name}); err != nil {
return err return err
} }
} }

View file

@ -5,13 +5,10 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/nspcc-dev/neofs-api-go/pkg/client"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-s3-gw/api/cache" "github.com/nspcc-dev/neofs-s3-gw/api/cache"
"github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/api/errors"
"go.uber.org/zap"
) )
type objectVersions struct { type objectVersions struct {
@ -120,68 +117,22 @@ func (v *objectVersions) getVersion(oid *object.ID) *ObjectInfo {
} }
return nil return nil
} }
func (n *layer) PutBucketVersioning(ctx context.Context, p *PutVersioningParams) (*ObjectInfo, error) { func (n *layer) PutBucketVersioning(ctx context.Context, p *PutVersioningParams) (*ObjectInfo, error) {
bucketInfo, err := n.GetBucketInfo(ctx, p.Bucket) bktInfo, err := n.GetBucketInfo(ctx, p.Bucket)
if err != nil { if err != nil {
return nil, err return nil, err
} }
objectInfo, err := n.getSettingsObjectInfo(ctx, bucketInfo) metadata := map[string]string{
if err != nil { attrSettingsVersioningEnabled: strconv.FormatBool(p.Settings.VersioningEnabled),
n.log.Warn("couldn't get bucket version settings object, new one will be created",
zap.String("bucket_name", bucketInfo.Name),
zap.Stringer("cid", bucketInfo.CID),
zap.Error(err))
} }
attributes := make([]*object.Attribute, 0, 3) meta, err := n.putSystemObject(ctx, bktInfo, bktInfo.SettingsObjectName(), metadata, "")
filename := object.NewAttribute()
filename.SetKey(objectSystemAttributeName)
filename.SetValue(bucketInfo.SettingsObjectName())
createdAt := object.NewAttribute()
createdAt.SetKey(object.AttributeTimestamp)
createdAt.SetValue(strconv.FormatInt(time.Now().UTC().Unix(), 10))
versioningIgnore := object.NewAttribute()
versioningIgnore.SetKey(attrVersionsIgnore)
versioningIgnore.SetValue(strconv.FormatBool(true))
settingsVersioningEnabled := object.NewAttribute()
settingsVersioningEnabled.SetKey(attrSettingsVersioningEnabled)
settingsVersioningEnabled.SetValue(strconv.FormatBool(p.Settings.VersioningEnabled))
attributes = append(attributes, filename, createdAt, versioningIgnore, settingsVersioningEnabled)
raw := object.NewRaw()
raw.SetOwnerID(bucketInfo.Owner)
raw.SetContainerID(bucketInfo.CID)
raw.SetAttributes(attributes...)
ops := new(client.PutObjectParams).WithObject(raw.Object())
oid, err := n.pool.PutObject(ctx, ops, n.BearerOpt(ctx))
if err != nil { if err != nil {
return nil, err return nil, err
} }
meta, err := n.objectHead(ctx, bucketInfo.CID, oid) return objInfoFromMeta(bktInfo, meta), nil
if err != nil {
return nil, err
}
if err = n.systemCache.Put(bucketInfo.SettingsObjectKey(), meta); err != nil {
n.log.Error("couldn't cache system object", zap.Error(err))
}
if objectInfo != nil {
if err = n.objectDelete(ctx, bucketInfo.CID, objectInfo.ID()); err != nil {
return nil, err
}
}
return objectInfoFromMeta(bucketInfo, meta, "", ""), nil
} }
func (n *layer) GetBucketVersioning(ctx context.Context, bucketName string) (*BucketSettings, error) { func (n *layer) GetBucketVersioning(ctx context.Context, bucketName string) (*BucketSettings, error) {
@ -296,7 +247,7 @@ func contains(list []string, elem string) bool {
} }
func (n *layer) getBucketSettings(ctx context.Context, bktInfo *cache.BucketInfo) (*BucketSettings, error) { func (n *layer) getBucketSettings(ctx context.Context, bktInfo *cache.BucketInfo) (*BucketSettings, error) {
objInfo, err := n.getSettingsObjectInfo(ctx, bktInfo) objInfo, err := n.getSystemObject(ctx, bktInfo, bktInfo.SettingsObjectName())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -359,21 +359,21 @@ Compatibility: 0/19/22
## Tagging ## Tagging
Compatibility: 0/8/11 Compatibility: 9/8/11
| | Test | s3-gw | aws s3 | | | Test | s3-gw | aws s3 |
|----|------------------------------------------------------------|-------|--------| |----|------------------------------------------------------------|-------|--------|
| 1 | s3tests_boto3.functional.test_s3.test_set_bucket_tagging | FAIL | FAIL | | 1 | s3tests_boto3.functional.test_s3.test_set_bucket_tagging | FAIL | FAIL |
| 2 | s3tests_boto3.functional.test_s3.test_get_obj_tagging | ERROR | ok | | 2 | s3tests_boto3.functional.test_s3.test_get_obj_tagging | ok | ok |
| 3 | s3tests_boto3.functional.test_s3.test_get_obj_head_tagging | ERROR | ok | | 3 | s3tests_boto3.functional.test_s3.test_get_obj_head_tagging | ok | ok |
| 4 | s3tests_boto3.functional.test_s3.test_put_max_tags | ERROR | ok | | 4 | s3tests_boto3.functional.test_s3.test_put_max_tags | ok | ok |
| 5 | s3tests_boto3.functional.test_s3.test_put_excess_tags | FAIL | FAIL | | 5 | s3tests_boto3.functional.test_s3.test_put_excess_tags | FAIL | FAIL |
| 6 | s3tests_boto3.functional.test_s3.test_put_max_kvsize_tags | ERROR | ok | | 6 | s3tests_boto3.functional.test_s3.test_put_max_kvsize_tags | ok | ok |
| 7 | s3tests_boto3.functional.test_s3.test_put_excess_key_tags | FAIL | ok | | 7 | s3tests_boto3.functional.test_s3.test_put_excess_key_tags | ok | ok |
| 8 | s3tests_boto3.functional.test_s3.test_put_excess_val_tags | FAIL | ok | | 8 | s3tests_boto3.functional.test_s3.test_put_excess_val_tags | ok | ok |
| 9 | s3tests_boto3.functional.test_s3.test_put_modify_tags | ERROR | FAIL | | 9 | s3tests_boto3.functional.test_s3.test_put_modify_tags | ok | FAIL |
| 10 | s3tests_boto3.functional.test_s3.test_put_delete_tags | ERROR | ok | | 10 | s3tests_boto3.functional.test_s3.test_put_delete_tags | ok | ok |
| 11 | s3tests_boto3.functional.test_s3.test_put_obj_with_tags | ERROR | ok | | 11 | s3tests_boto3.functional.test_s3.test_put_obj_with_tags | ok | ok |
## Versioning ## Versioning