WIP: cache HEAD result in context

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
Evgenii Stratonikov 2024-02-09 14:15:37 +03:00
parent e66007b1f2
commit 8fd27be7d8
5 changed files with 92 additions and 40 deletions

View file

@ -9,6 +9,7 @@ import (
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
refsV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" refsV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -199,8 +200,19 @@ func (h *cfg) localObjectHeaders(ctx context.Context, cnr cid.ID, idObj *oid.ID)
addr.SetContainer(cnr) addr.SetContainer(cnr)
addr.SetObject(*idObj) addr.SetObject(*idObj)
reqCtx, _ := object.FromRequestContext(ctx)
if reqCtx != nil {
hdr := reqCtx.GetHeader(addr)
if hdr != nil {
return headersFromObject(hdr, cnr, idObj), true
}
}
obj, err := h.storage.Head(ctx, addr) obj, err := h.storage.Head(ctx, addr)
if err == nil { if err == nil {
if reqCtx != nil {
reqCtx.Header.Store(obj)
}
return headersFromObject(obj, cnr, idObj), true return headersFromObject(obj, cnr, idObj), true
} }
} }

View file

@ -115,13 +115,6 @@ func (w *wrappedGetObjectStream) Context() context.Context {
return object.NewRequestContext(w.GetObjectStream.Context(), w.requestContext) return object.NewRequestContext(w.GetObjectStream.Context(), w.requestContext)
} }
func newWrappedGetObjectStreamStream(getObjectStream object.GetObjectStream, reqInfo RequestInfo) object.GetObjectStream {
return &wrappedGetObjectStream{
GetObjectStream: getObjectStream,
requestContext: reqInfo.toRequestContext(),
}
}
// wrappedRangeStream propagates RequestContext into GetObjectRangeStream's context. // wrappedRangeStream propagates RequestContext into GetObjectRangeStream's context.
// This allows to retrieve already calculated immutable request-specific values in next handler invocation. // This allows to retrieve already calculated immutable request-specific values in next handler invocation.
type wrappedRangeStream struct { type wrappedRangeStream struct {
@ -134,13 +127,6 @@ func (w *wrappedRangeStream) Context() context.Context {
return object.NewRequestContext(w.GetObjectRangeStream.Context(), w.requestContext) return object.NewRequestContext(w.GetObjectRangeStream.Context(), w.requestContext)
} }
func newWrappedRangeStream(rangeStream object.GetObjectRangeStream, reqInfo RequestInfo) object.GetObjectRangeStream {
return &wrappedRangeStream{
GetObjectRangeStream: rangeStream,
requestContext: reqInfo.toRequestContext(),
}
}
// wrappedSearchStream propagates RequestContext into SearchStream's context. // wrappedSearchStream propagates RequestContext into SearchStream's context.
// This allows to retrieve already calculated immutable request-specific values in next handler invocation. // This allows to retrieve already calculated immutable request-specific values in next handler invocation.
type wrappedSearchStream struct { type wrappedSearchStream struct {
@ -153,13 +139,6 @@ func (w *wrappedSearchStream) Context() context.Context {
return object.NewRequestContext(w.SearchStream.Context(), w.requestContext) return object.NewRequestContext(w.SearchStream.Context(), w.requestContext)
} }
func newWrappedSearchStream(searchStream object.SearchStream, reqInfo RequestInfo) object.SearchStream {
return &wrappedSearchStream{
SearchStream: searchStream,
requestContext: reqInfo.toRequestContext(),
}
}
// Get implements ServiceServer interface, makes ACL checks and calls // Get implements ServiceServer interface, makes ACL checks and calls
// next Get method in the ServiceServer pipeline. // next Get method in the ServiceServer pipeline.
func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream) error { func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream) error {
@ -204,16 +183,23 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
reqInfo.obj = obj reqInfo.obj = obj
reqCtx := reqInfo.toRequestContext()
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo) return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(stream.Context(), request, reqInfo); err != nil { }
ctx := object.NewRequestContext(stream.Context(), reqCtx)
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return eACLErr(reqInfo, err) return eACLErr(reqInfo, err)
} }
} }
return b.next.Get(request, &getStreamBasicChecker{ return b.next.Get(request, &getStreamBasicChecker{
GetObjectStream: newWrappedGetObjectStreamStream(stream, reqInfo), GetObjectStream: &wrappedGetObjectStream{
GetObjectStream: stream,
requestContext: reqCtx,
},
info: reqInfo, info: reqInfo,
checker: b.checker, checker: b.checker,
}) })
@ -273,15 +259,18 @@ func (b Service) Head(
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil { }
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
resp, err := b.next.Head(requestContext(ctx, reqInfo), request) resp, err := b.next.Head(ctx, request)
if err == nil { if err == nil {
if err = b.checker.CheckEACL(ctx, resp, reqInfo); err != nil { if err = b.checker.CheckEACL(ctx, resp, reqInfo); err != nil {
err = eACLErr(reqInfo, err) err = eACLErr(reqInfo, err)
@ -326,17 +315,24 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
return err return err
} }
reqCtx := reqInfo.toRequestContext()
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo) return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(stream.Context(), request, reqInfo); err != nil { }
ctx := object.NewRequestContext(stream.Context(), reqCtx)
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return eACLErr(reqInfo, err) return eACLErr(reqInfo, err)
} }
} }
return b.next.Search(request, &searchStreamBasicChecker{ return b.next.Search(request, &searchStreamBasicChecker{
checker: b.checker, checker: b.checker,
SearchStream: newWrappedSearchStream(stream, reqInfo), SearchStream: &wrappedSearchStream{
SearchStream: stream,
requestContext: reqCtx,
},
info: reqInfo, info: reqInfo,
}) })
} }
@ -386,6 +382,7 @@ func (b Service) Delete(
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
@ -393,8 +390,7 @@ func (b Service) Delete(
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
return b.next.Delete(ctx, request)
return b.next.Delete(requestContext(ctx, reqInfo), request)
} }
func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetObjectRangeStream) error { func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetObjectRangeStream) error {
@ -439,17 +435,24 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
reqInfo.obj = obj reqInfo.obj = obj
reqCtx := reqInfo.toRequestContext()
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo) return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(stream.Context(), request, reqInfo); err != nil { }
ctx := object.NewRequestContext(stream.Context(), reqCtx)
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return eACLErr(reqInfo, err) return eACLErr(reqInfo, err)
} }
} }
return b.next.GetRange(request, &rangeStreamBasicChecker{ return b.next.GetRange(request, &rangeStreamBasicChecker{
checker: b.checker, checker: b.checker,
GetObjectRangeStream: newWrappedRangeStream(stream, reqInfo), GetObjectRangeStream: &wrappedRangeStream{
GetObjectRangeStream: stream,
requestContext: reqCtx,
},
info: reqInfo, info: reqInfo,
}) })
} }
@ -503,15 +506,18 @@ func (b Service) GetRangeHash(
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil { }
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
return b.next.GetRangeHash(requestContext(ctx, reqInfo), request) return b.next.GetRangeHash(ctx, request)
} }
func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequest) (*objectV2.PutSingleResponse, error) { func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequest) (*objectV2.PutSingleResponse, error) {
@ -562,16 +568,18 @@ func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleReque
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) || !b.checker.StickyBitCheck(reqInfo, idOwner) { if !b.checker.CheckBasicACL(reqInfo) || !b.checker.StickyBitCheck(reqInfo, idOwner) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} }
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil { if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
return b.next.PutSingle(requestContext(ctx, reqInfo), request) return b.next.PutSingle(ctx, request)
} }
func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error { func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error {

View file

@ -203,6 +203,7 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
SenderKey: hex.EncodeToString(reqCtx.SenderKey), SenderKey: hex.EncodeToString(reqCtx.SenderKey),
ContainerOwner: reqCtx.ContainerOwner, ContainerOwner: reqCtx.ContainerOwner,
SoftAPECheck: reqCtx.SoftAPECheck, SoftAPECheck: reqCtx.SoftAPECheck,
Header: reqCtx.Header.Load().ToV2().GetHeader(),
}) })
if err != nil { if err != nil {
return nil, toStatusErr(err) return nil, toStatusErr(err)

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -50,6 +51,12 @@ func (r *request) executeLocal(ctx context.Context) {
func (r *request) get(ctx context.Context) (*objectSDK.Object, error) { func (r *request) get(ctx context.Context) (*objectSDK.Object, error) {
if r.headOnly() { if r.headOnly() {
reqCtx, _ := object.FromRequestContext(ctx)
if reqCtx != nil && !r.isRaw() {
if hdr := reqCtx.GetHeader(r.address()); hdr != nil {
return hdr, nil
}
}
return r.localStorage.Head(ctx, r.address(), r.isRaw()) return r.localStorage.Head(ctx, r.address(), r.isRaw())
} }
if rng := r.ctxRange(); rng != nil { if rng := r.ctxRange(); rng != nil {

View file

@ -3,8 +3,12 @@ package object
import ( import (
"context" "context"
"fmt" "fmt"
"sync/atomic"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
) )
@ -23,6 +27,8 @@ type RequestContext struct {
Role acl.Role Role acl.Role
SoftAPECheck bool SoftAPECheck bool
Header atomic.Pointer[objectSDK.Object]
} }
// NewRequestContext returns a copy of ctx which carries value. // NewRequestContext returns a copy of ctx which carries value.
@ -38,3 +44,21 @@ func FromRequestContext(ctx context.Context) (*RequestContext, error) {
} }
return reqCtx, nil return reqCtx, nil
} }
// GetHeader returns header if it is present and matches cid + oid pair.
func (r *RequestContext) GetHeader(addr oid.Address) *objectSDK.Object {
if r == nil {
return nil
}
hdr := r.Header.Load()
if hdr == nil {
return nil
}
storedAddr := object.AddressOf(hdr)
if addr.Equals(storedAddr) {
return hdr
}
return nil
}