From 398da6509501c00f609cabe63b6406b01edb9d42 Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Wed, 11 Dec 2024 19:45:15 -0500 Subject: [PATCH] Use sync.Pool for relation filter buffers (#118) --- pkg/directory/v3/reader.go | 14 ++++++--- pkg/ds/check.go | 7 +++-- pkg/ds/pool.go | 24 +++++++++++++++ pkg/ds/relation.go | 60 +++++++++++++++----------------------- 4 files changed, 63 insertions(+), 42 deletions(-) create mode 100644 pkg/ds/pool.go diff --git a/pkg/directory/v3/reader.go b/pkg/directory/v3/reader.go index 48ed49f..ccc6071 100644 --- a/pkg/directory/v3/reader.go +++ b/pkg/directory/v3/reader.go @@ -184,13 +184,16 @@ func (s *Reader) GetRelation(ctx context.Context, req *dsr3.GetRelationRequest) return resp, err } - path, filter, err := getRelation.PathAndFilter() + filter := ds.RelationIdentifierBuffer() + defer ds.ReturnRelationIdentifierBuffer(filter) + + path, err := getRelation.PathAndFilter(filter) if err != nil { return resp, err } err = s.store.DB().View(func(tx *bolt.Tx) error { - relations, err := bdb.Scan[dsc3.Relation](ctx, tx, path, filter) + relations, err := bdb.Scan[dsc3.Relation](ctx, tx, path, filter.Bytes()) if err != nil { return err } @@ -259,11 +262,14 @@ func (s *Reader) GetRelations(ctx context.Context, req *dsr3.GetRelationsRequest return resp, err } - path, keyFilter, valueFilter := getRelations.RelationValueFilter() + keyFilter := ds.RelationIdentifierBuffer() + defer ds.ReturnRelationIdentifierBuffer(keyFilter) + + path, valueFilter := getRelations.RelationValueFilter(keyFilter) opts := []bdb.ScanOption{ bdb.WithPageToken(req.Page.Token), - bdb.WithKeyFilter(keyFilter), + bdb.WithKeyFilter(keyFilter.Bytes()), } err := s.store.DB().View(func(tx *bolt.Tx) error { diff --git a/pkg/ds/check.go b/pkg/ds/check.go index a8b3f98..4cc619f 100644 --- a/pkg/ds/check.go +++ b/pkg/ds/check.go @@ -31,9 +31,12 @@ func (i *check) Exec(ctx context.Context, tx *bolt.Tx, mc *cache.Cache) (*dsr3.C func getRelations(ctx context.Context, tx *bolt.Tx) graph.RelationReader { return func(r *dsc3.RelationIdentifier, pool graph.RelationPool, out *[]*dsc3.RelationIdentifier) error { - path, keyFilter, valueFilter := RelationIdentifier(r).Filter() + keyFilter := RelationIdentifierBuffer() + defer ReturnRelationIdentifierBuffer(keyFilter) - return bdb.ScanWithFilter(ctx, tx, path, keyFilter, valueFilter, pool, out) + path, valueFilter := RelationIdentifier(r).Filter(keyFilter) + + return bdb.ScanWithFilter(ctx, tx, path, keyFilter.Bytes(), valueFilter, pool, out) } } diff --git a/pkg/ds/pool.go b/pkg/ds/pool.go new file mode 100644 index 0000000..9503af8 --- /dev/null +++ b/pkg/ds/pool.go @@ -0,0 +1,24 @@ +package ds + +import ( + "bytes" + + "github.com/aserto-dev/azm/mempool" +) + +const maxRelationIdentifierSize = 384 + +var relidBufPool = mempool.NewPool[*bytes.Buffer](NewRelationIdentifierBuf) + +func NewRelationIdentifierBuf() *bytes.Buffer { + return bytes.NewBuffer(make([]byte, 0, maxRelationIdentifierSize)) +} + +func RelationIdentifierBuffer() *bytes.Buffer { + return relidBufPool.Get() +} + +func ReturnRelationIdentifierBuffer(b *bytes.Buffer) { + b.Reset() + relidBufPool.Put(b) +} diff --git a/pkg/ds/relation.go b/pkg/ds/relation.go index 1e46f65..d2b46fe 100644 --- a/pkg/ds/relation.go +++ b/pkg/ds/relation.go @@ -100,103 +100,95 @@ func (i *relation) SubKey() []byte { return buf.Bytes() } -func (i *relation) PathAndFilter() ([]string, []byte, error) { +func (i *relation) PathAndFilter(filter *bytes.Buffer) ([]string, error) { switch { case ObjectSelector(i.Object()).IsComplete(): - return bdb.RelationsObjPath, i.ObjFilter(), nil + i.ObjFilter(filter) + return bdb.RelationsObjPath, nil case ObjectSelector(i.Subject()).IsComplete(): - return bdb.RelationsSubPath, i.SubFilter(), nil + i.SubFilter(filter) + return bdb.RelationsSubPath, nil default: - return []string{}, []byte{}, ErrNoCompleteObjectIdentifier + return []string{}, ErrNoCompleteObjectIdentifier } } // ObjFilter // format: obj_type : obj_id # relation @ sub_type : sub_id (# sub_relation). // TODO: if subject relation exists add subject relation to filter clause. -func (i *relation) ObjFilter() []byte { - buf := newRelationBuffer() - +func (i *relation) ObjFilter(buf *bytes.Buffer) { buf.WriteString(i.GetObjectType()) buf.WriteByte(TypeIDSeparator) buf.WriteString(i.GetObjectId()) buf.WriteByte(InstanceSeparator) if IsNotSet(i.GetRelation()) { - return buf.Bytes() + return } buf.WriteString(i.GetRelation()) buf.WriteByte(InstanceSeparator) if IsNotSet(i.GetSubjectType()) { - return buf.Bytes() + return } buf.WriteString(i.GetSubjectType()) buf.WriteByte(TypeIDSeparator) if IsNotSet(i.GetSubjectId()) { - return buf.Bytes() + return } buf.WriteString(i.GetSubjectId()) - - return buf.Bytes() } // SubFilter // format: sub_type : sub_id (# sub_relation) | obj_type : obj_id # relation. // TODO: if subject relation exists add subject relation to filter clause. -func (i *relation) SubFilter() []byte { - buf := newRelationBuffer() - +func (i *relation) SubFilter(buf *bytes.Buffer) { buf.WriteString(i.GetSubjectType()) buf.WriteByte(TypeIDSeparator) buf.WriteString(i.GetSubjectId()) buf.WriteByte(InstanceSeparator) if IsNotSet(i.GetRelation()) { - return buf.Bytes() + return } buf.WriteString(i.GetRelation()) buf.WriteByte(InstanceSeparator) if IsNotSet(i.GetObjectType()) { - return buf.Bytes() + return } buf.WriteString(i.GetObjectType()) buf.WriteByte(TypeIDSeparator) if IsNotSet(i.GetObjectId()) { - return buf.Bytes() + return } buf.WriteString(i.GetObjectId()) - - return buf.Bytes() } // nolint: gocritic -func (i *relation) Filter() (path bdb.Path, keyFilter []byte, valueFilter func(*dsc3.RelationIdentifier) bool) { +func (i *relation) Filter(keyFilter *bytes.Buffer) (path bdb.Path, valueFilter func(*dsc3.RelationIdentifier) bool) { // #1 determine if object identifier is complete (has type+id) // set index path accordingly // set keyFilter to match covering path // when no complete object identifier, fallback to a full table scan if ObjectIdentifier(i.Object()).IsComplete() { path = bdb.RelationsObjPath - keyFilter = i.ObjFilter() - } - if ObjectIdentifier(i.Subject()).IsComplete() { + i.ObjFilter(keyFilter) + } else if ObjectIdentifier(i.Subject()).IsComplete() { path = bdb.RelationsSubPath - keyFilter = i.SubFilter() + i.SubFilter(keyFilter) } if len(path) == 0 { log.Debug().Msg("no covering index path, default to scan of relation object path") path = bdb.RelationsObjPath - keyFilter = []byte{} } // #2 build valueFilter function @@ -260,27 +252,25 @@ func (i *relation) Filter() (path bdb.Path, keyFilter []byte, valueFilter func(* return true } - return path, keyFilter, valueFilter + return path, valueFilter } // nolint: gocritic // commentedOutCode -func (i *relation) RelationValueFilter() (path bdb.Path, keyFilter []byte, valueFilter func(*dsc3.Relation) bool) { +func (i *relation) RelationValueFilter(keyFilter *bytes.Buffer) (path bdb.Path, valueFilter func(*dsc3.Relation) bool) { // #1 determine if object identifier is complete (has type+id) // set index path accordingly // set keyFilter to match covering path // when no complete object identifier, fallback to a full table scan if ObjectIdentifier(i.Object()).IsComplete() { path = bdb.RelationsObjPath - keyFilter = i.ObjFilter() - } - if ObjectIdentifier(i.Subject()).IsComplete() { + i.ObjFilter(keyFilter) + } else if ObjectIdentifier(i.Subject()).IsComplete() { path = bdb.RelationsSubPath - keyFilter = i.SubFilter() + i.SubFilter(keyFilter) } if len(path) == 0 { log.Debug().Msg("no covering index path, default to scan of relation object path") path = bdb.RelationsObjPath - keyFilter = []byte{} } // #2 build valueFilter function @@ -344,11 +334,9 @@ func (i *relation) RelationValueFilter() (path bdb.Path, keyFilter []byte, value return true } - return path, keyFilter, valueFilter + return path, valueFilter } -const maxRelationIdentifierSize = 384 - func newRelationBuffer() *bytes.Buffer { return bytes.NewBuffer(make([]byte, 0, maxRelationIdentifierSize)) }