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

perf(node): caching implementation for node operations #830

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.18

require (
github.com/derekparker/trie v0.0.0-20221221181808-1424fce0c981
github.com/goccy/go-json v0.10.0
github.com/golang/glog v1.0.0
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.9
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
Expand Down
27 changes: 27 additions & 0 deletions util/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,16 @@ func InsertIntoStruct(parentStruct interface{}, fieldName string, fieldValue int
// generated code. Here we cast the value to the type in the generated code.
if ft.Type.Kind() == reflect.Bool && t.Kind() == reflect.Bool {
nv := reflect.New(ft.Type).Elem()

// If the field is not nil, do not create a new pointer which modifies the
// field's memory address under its parent.
if pv.Elem().FieldByName(fieldName).Addr().UnsafePointer() != nil {
nv = reflect.NewAt(
ft.Type,
pv.Elem().FieldByName(fieldName).Addr().UnsafePointer(),
).Elem()
}

nv.SetBool(v.Bool())
v = nv
}
Expand All @@ -265,13 +275,30 @@ func InsertIntoStruct(parentStruct interface{}, fieldName string, fieldValue int
// This will also cast a []uint8 value since byte is an alias for uint8.
if ft.Type.Kind() == reflect.Slice && t.Kind() == reflect.Slice && ft.Type.Elem().Kind() == reflect.Uint8 && t.Elem().Kind() == reflect.Uint8 {
nv := reflect.New(ft.Type).Elem()

// If the field is not nil, do not create a new pointer which modifies the
// field's memory address under its parent.
if pv.Elem().FieldByName(fieldName).Addr().UnsafePointer() != nil {
nv = reflect.NewAt(
ft.Type,
pv.Elem().FieldByName(fieldName).Addr().UnsafePointer(),
).Elem()
}

nv.SetBytes(v.Bytes())
v = nv
}

n := v
if n.IsValid() && (ft.Type.Kind() == reflect.Ptr && t.Kind() != reflect.Ptr) {
n = reflect.New(t)

// If the field is not nil, do not create a new pointer which modifies the
// field's memory address under its parent.
if pv.Elem().FieldByName(fieldName).UnsafePointer() != nil {
n = reflect.NewAt(t, pv.Elem().FieldByName(fieldName).UnsafePointer())
}

n.Elem().Set(v)
}

Expand Down
69 changes: 33 additions & 36 deletions ytypes/gnmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,44 @@ func UnmarshalNotifications(schema *Schema, ns []*gpb.Notification, opts ...Unma
// If an error occurs during unmarshalling, schema.Root may already be
// modified. A rollback is not performed.
func UnmarshalSetRequest(schema *Schema, req *gpb.SetRequest, opts ...UnmarshalOpt) error {
preferShadowPath := hasPreferShadowPath(opts)
ignoreExtraFields := hasIgnoreExtraFields(opts)
if req == nil {
return nil
}

// Use option slices instead of flags to pass options down to the function calls.
var getOrCreateOpts []GetOrCreateNodeOpt
var deleteOpts []DelNodeOpt
var updateOpts []SetNodeOpt

for _, opt := range opts {
switch o := opt.(type) {
case *PreferShadowPath:
getOrCreateOpts = append(getOrCreateOpts, &PreferShadowPath{})
deleteOpts = append(deleteOpts, &PreferShadowPath{})
updateOpts = append(updateOpts, &PreferShadowPath{})
case *IgnoreExtraFields:
updateOpts = append(updateOpts, &IgnoreExtraFields{})
case *NodeCacheOpt:
getOrCreateOpts = append(getOrCreateOpts, o)
deleteOpts = append(deleteOpts, o)
updateOpts = append(updateOpts, o)
}
}

root := schema.Root
node, nodeName, err := getOrCreateNode(schema.RootSchema(), root, req.Prefix, preferShadowPath)
node, nodeName, err := getOrCreateNode(schema.RootSchema(), root, req.Prefix, getOrCreateOpts)
if err != nil {
return err
}

// Process deletes, then replace, then updates.
if err := deletePaths(schema.SchemaTree[nodeName], node, req.Delete, preferShadowPath); err != nil {
if err := deletePaths(schema.SchemaTree[nodeName], node, req.Delete, deleteOpts); err != nil {
return err
}
if err := replacePaths(schema.SchemaTree[nodeName], node, req.Replace, preferShadowPath, ignoreExtraFields); err != nil {
if err := replacePaths(schema.SchemaTree[nodeName], node, req.Replace, deleteOpts, updateOpts); err != nil {
return err
}
if err := updatePaths(schema.SchemaTree[nodeName], node, req.Update, preferShadowPath, ignoreExtraFields); err != nil {
if err := updatePaths(schema.SchemaTree[nodeName], node, req.Update, updateOpts); err != nil {
return err
}

Expand All @@ -71,11 +90,7 @@ func UnmarshalSetRequest(schema *Schema, req *gpb.SetRequest, opts ...UnmarshalO

// getOrCreateNode instantiates the node at the given path, and returns that
// node along with its name.
func getOrCreateNode(schema *yang.Entry, goStruct ygot.GoStruct, path *gpb.Path, preferShadowPath bool) (ygot.GoStruct, string, error) {
var gcopts []GetOrCreateNodeOpt
if preferShadowPath {
gcopts = append(gcopts, &PreferShadowPath{})
}
func getOrCreateNode(schema *yang.Entry, goStruct ygot.GoStruct, path *gpb.Path, gcopts []GetOrCreateNodeOpt) (ygot.GoStruct, string, error) {
// Operate at the prefix level.
nodeI, _, err := GetOrCreateNode(schema, goStruct, path, gcopts...)
if err != nil {
Expand All @@ -90,12 +105,7 @@ func getOrCreateNode(schema *yang.Entry, goStruct ygot.GoStruct, path *gpb.Path,
}

// deletePaths deletes a slice of paths from the given GoStruct.
func deletePaths(schema *yang.Entry, goStruct ygot.GoStruct, paths []*gpb.Path, preferShadowPath bool) error {
var dopts []DelNodeOpt
if preferShadowPath {
dopts = append(dopts, &PreferShadowPath{})
}

func deletePaths(schema *yang.Entry, goStruct ygot.GoStruct, paths []*gpb.Path, dopts []DelNodeOpt) error {
for _, path := range paths {
if err := DeleteNode(schema, goStruct, path, dopts...); err != nil {
return err
Expand All @@ -107,17 +117,12 @@ func deletePaths(schema *yang.Entry, goStruct ygot.GoStruct, paths []*gpb.Path,
// replacePaths unmarshals a slice of updates into the given GoStruct. It
// deletes the values at these paths before unmarshalling them. These updates
// can either by JSON-encoded or gNMI-encoded values (scalars).
func replacePaths(schema *yang.Entry, goStruct ygot.GoStruct, updates []*gpb.Update, preferShadowPath, ignoreExtraFields bool) error {
var dopts []DelNodeOpt
if preferShadowPath {
dopts = append(dopts, &PreferShadowPath{})
}

func replacePaths(schema *yang.Entry, goStruct ygot.GoStruct, updates []*gpb.Update, dopts []DelNodeOpt, uopts []SetNodeOpt) error {
for _, update := range updates {
if err := DeleteNode(schema, goStruct, update.Path, dopts...); err != nil {
return err
}
if err := setNode(schema, goStruct, update, preferShadowPath, ignoreExtraFields); err != nil {
if err := setNode(schema, goStruct, update, uopts); err != nil {
return err
}
}
Expand All @@ -126,9 +131,9 @@ func replacePaths(schema *yang.Entry, goStruct ygot.GoStruct, updates []*gpb.Upd

// updatePaths unmarshals a slice of updates into the given GoStruct. These
// updates can either by JSON-encoded or gNMI-encoded values (scalars).
func updatePaths(schema *yang.Entry, goStruct ygot.GoStruct, updates []*gpb.Update, preferShadowPath, ignoreExtraFields bool) error {
func updatePaths(schema *yang.Entry, goStruct ygot.GoStruct, updates []*gpb.Update, uopts []SetNodeOpt) error {
for _, update := range updates {
if err := setNode(schema, goStruct, update, preferShadowPath, ignoreExtraFields); err != nil {
if err := setNode(schema, goStruct, update, uopts); err != nil {
return err
}
}
Expand All @@ -137,14 +142,6 @@ func updatePaths(schema *yang.Entry, goStruct ygot.GoStruct, updates []*gpb.Upda

// setNode unmarshals either a JSON-encoded value or a gNMI-encoded (scalar)
// value into the given GoStruct.
func setNode(schema *yang.Entry, goStruct ygot.GoStruct, update *gpb.Update, preferShadowPath, ignoreExtraFields bool) error {
sopts := []SetNodeOpt{&InitMissingElements{}}
if preferShadowPath {
sopts = append(sopts, &PreferShadowPath{})
}
if ignoreExtraFields {
sopts = append(sopts, &IgnoreExtraFields{})
}

return SetNode(schema, goStruct, update.Path, update.Val, sopts...)
func setNode(schema *yang.Entry, goStruct ygot.GoStruct, update *gpb.Update, uopts []SetNodeOpt) error {
return SetNode(schema, goStruct, update.Path, update.Val, append(uopts, &InitMissingElements{})...)
}
Loading