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

Add package dynamic. #599

Merged
merged 16 commits into from
Nov 23, 2017
Merged

Add package dynamic. #599

merged 16 commits into from
Nov 23, 2017

Conversation

echlebek
Copy link
Contributor

@echlebek echlebek commented Nov 22, 2017

What is this change?

Package dynamic can be used to with json.Marshaler and
json.Unmarshaler to create types that can support arbitrary
custom attributes.

The package achieves this by parsing json messages with
the json-iterator package in a streaming fashion, and mapping the
results onto dynamically created types with package reflect.

Package dynamic also implements GetField, which can be used to
implement the govaluate.Parameters interface.

Why is this change necessary?

Fixes #586

Were there any complications while making this change?

There were several. As part of this change, I've filed this PR against govaluate: Knetic/govaluate#84

@codecov
Copy link

codecov bot commented Nov 22, 2017

Codecov Report

Merging #599 into master will decrease coverage by 0.26%.
The diff coverage is 85.54%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #599      +/-   ##
==========================================
- Coverage   56.44%   56.18%   -0.27%     
==========================================
  Files         331      332       +1     
  Lines       21971    22137     +166     
  Branches       50       50              
==========================================
+ Hits        12402    12437      +35     
- Misses       7668     7823     +155     
+ Partials     1901     1877      -24
Flag Coverage Δ
#go 56.89% <85.54%> (-0.28%) ⬇️
#javascript 18.38% <ø> (ø) ⬆️
Impacted Files Coverage Δ
types/dynamic/dynamic.go 85.54% <85.54%> (ø)
types/tls.pb.go 40.76% <0%> (-9.52%) ⬇️
types/asset.pb.go 50.72% <0%> (-7.3%) ⬇️
types/mutator.pb.go 44.56% <0%> (-6.21%) ⬇️
types/organization.pb.go 43.14% <0%> (-6.03%) ⬇️
types/rbac.pb.go 50.35% <0%> (-5.76%) ⬇️
types/keepalive.pb.go 50.68% <0%> (-5.21%) ⬇️
types/user.pb.go 46.33% <0%> (-4.98%) ⬇️
types/authentication.pb.go 47.56% <0%> (-4.58%) ⬇️
types/check.pb.go 54.51% <0%> (-1.05%) ⬇️
... and 10 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7dacc4c...6a3137d. Read the comment docs.

@grepory
Copy link
Contributor

grepory commented Nov 22, 2017

We will eventually be serializing to protobuf in etcd, can we adapt this package to also work with protobuf's any?

@echlebek
Copy link
Contributor Author

echlebek commented Nov 22, 2017

@grepory the intent is that extended attributes will remain JSON-encoded regardless of how we marshal the envelope around the extended attributes. This will make sure we preserve the semantics of what the user gave us.

So the protobuf field for extended attributes would be bytes, or we can promote dynamic.Attributes to be types.DynamicAttributes that has a protobuf encoder and decoder.

@grepory
Copy link
Contributor

grepory commented Nov 22, 2017

Oh right! Thanks for the clarification.

Package dynamic can be used to with json.Marshaler and
json.Unmarshaler to create types that can support arbitrary
custom attributes.

The package achieves this by parsing json messages with
the json-iterator package in a streaming fashion, and mapping the
results onto dynamically created types with package reflect.

Package dynamic also implements GetField, which can be used to
implement the govaluate.Parameters interface.
Add tests for json Marshal/Unmarshal.

Refs #586
Refs #586

goos: linux
goarch: amd64
pkg: github.com/sensu/sensu-go/types/dynamic
BenchmarkQueryGovaluateSimple-4    	  500000	      2069 ns/op	     992 B/op	      25 allocs/op
BenchmarkQueryGovaluateComplex-4   	  200000	      6212 ns/op	    2544 B/op	      75 allocs/op
BenchmarkUnmarshal-4               	  200000	      6492 ns/op	    6568 B/op	      57 allocs/op
BenchmarkMarshal-4                 	  300000	      4556 ns/op	    6512 B/op	      40 allocs/op
PASS
ok  	github.com/sensu/sensu-go/types/dynamic	6.172s
This change makes the package significantly easier to use, and also
presents a greater symmetry between its types and functions.

This comes at a cost of additional reflection, which can be seen
in the benchmark for Unmarshal.

Old:
BenchmarkUnmarshal-4    6492 ns/op    6568 B/op      57 allocs/op

New:
BenchmarkUnmarshal-4    9244 ns/op    7592 B/op      77 allocs/op

Refs #586
Now that govaluate has been modified to recursively query
Parameters, we can return a lazy tree of AnyParameters instead of
dynamically generating structs. This saves quite a bit of code,
CPU time, and memory allocations.

Old struct generation
    BenchmarkQueryGovaluateSimple-4    2069 ns/op    992 B/op   25 allocs/op
    BenchmarkQueryGovaluateComplex-4   6212 ns/op   2544 B/op   75 allocs/op

New lazy json parsing
    BenchmarkQueryGovaluateSimple-4    1404 ns/op    320 B/op   18 allocs/op
    BenchmarkQueryGovaluateComplex-4   2617 ns/op    512 B/op   30 allocs/op

Refs #586
Reduces cost of querying in the most common case.

Refs #586
* Break up Attributer interface into AttrGetter and AttrSetter.
* Get rid of the Attributes type in favour of simple []byte.
* Make AttrGetter method set match what protobuf might generate.
* When returning struct fields from GetField, use reflect.Indirect
  so that pointers are never returned.
* Make sure that extended attributes are never returned by GetField
  and never marshaled by Marshal.

Refs #586
@echlebek echlebek merged commit a4ee504 into master Nov 23, 2017
@echlebek echlebek deleted the proposal/package-dynamic branch November 27, 2017 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants