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
Influxdb tags as fields #585
Conversation
Codecov Report
@@ Coverage Diff @@
## master #585 +/- ##
==========================================
- Coverage 62.8% 62.55% -0.25%
==========================================
Files 97 97
Lines 7374 7406 +32
==========================================
+ Hits 4631 4633 +2
- Misses 2489 2521 +32
+ Partials 254 252 -2
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've written some comments, but honestly I'd be fine to merge this as it is. One of the comments is probably a premature optimization, while the other one could be fixed when we update the influxdb configuration to use null-able types like the other configs in k6.
I've locally updated the cloud collector config to use them and I'll create a new issue for the influxdb collector, which will probably happen in conjunction with moving this in lib.Options
, as we talked on slack. @luizbafilho, thoughts?
stats/influxdb/collector.go
Outdated
@@ -108,10 +108,12 @@ func (c *Collector) commit() { | |||
} | |||
|
|||
for _, sample := range samples { | |||
tags := sample.Tags.CloneTags() //TODO: optimize when implementing https://github.com/loadimpact/k6/issues/569 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea with this TODO is that since this pull request, sample.Tags
is now comparable, it could be used as a map key for creating a simple cache. If you had something like this outside of the for
loop:
cache := map[sample.Tags]struct {
tags map[string]string
values map[string]interface{}
}{}
In the for
loop you'd be able to check if certain tags are already in the cache with
if cached, ok := cache[sample.Tags]; ok {
// Reuse the cached.tags, create a new map with {"value": sample.Value} and copy all elements from cached.values to it
} else {
// Do what you currently do and then save the resulting tags and values as a new entry in the cache
}
I'm fine with merging this as it is, if you don't want to bother with this probably premature optimization. If it turns out that it's an issue, we'll optimize it in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm looking at this now so we can hold off with the merge.
stats/influxdb/config.go
Outdated
@@ -131,3 +139,9 @@ func (c *Config) UnmarshalJSON(data []byte) error { | |||
func (c Config) MarshalJSON() ([]byte, error) { | |||
return json.Marshal(ConfigFields(c)) | |||
} | |||
|
|||
func (c *Config) defaultValues() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I'm not sure that doing this in the UnmarshalText()
and UnmarshalJSON()
(and actually having a defaultValues()
function that changes a receiver's property) is very idiomatic and potentially it could be confusing to Go developers. A much more idiomatic solution would be to create a "constructor" function called NewConfig()
that instantiates the config with the default values: https://hassansin.github.io/golang-constructor-like-function-use-cases
This would also solve the issues with the suboptimal tests. The overhead is that elsewhere in the code you'd have to keep in mind that you'd have to use NewConfig()
instead of directly instantiating a Config
struct or, in those cases:
- https://github.com/loadimpact/k6/blob/master/cmd/login_influxdb.go#L49
- https://github.com/loadimpact/k6/blob/master/cmd/collectors.go#L71
and others like them, something like:
conf := influxdb.NewConfig().Apply(config.Collectors.InfluxDB)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, it doesn't feel right as is. I'll take a look at changing this to a NewConfig() function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few minor issues I've commented in the code, but otherwise it looks good. I think it's a good idea to have the cache in commit()
, it will be most useful there and we don't have to worry about it leaking memory.
stats/influxdb/collector.go
Outdated
if cached, ok := cache[sample.Tags]; ok { | ||
tags = cached.tags | ||
values = cached.values | ||
values["value"] = sample.Value // Overwrite the "value" field |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this would be a bug that causes all values["value"]
to be the same: https://play.golang.org/p/xikPsAxZWFI
Basically assigning a map with =
doesn't do a deep copy of the values and changing the "copy" affects the original :/ One of those unpleasant Go gotchas...
You probably have to create a new map every time in the loop (values := map[string]interface{}{"value": sample.Value}
) and then copy all items from cached.values
in it with a loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm actually it's probably a better idea to create a new empty map, copy all cached.values
in it and then set values["value"]
to sample.Value
, just in case someone tries to create a new tag named "value"
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually works but as you said in private chat, I will change it to be safe if InfluxDB changes it's implementation and start caching/batching stuff internally.
stats/influxdb/collector.go
Outdated
cache[sample.Tags] = struct { | ||
tags map[string]string | ||
values map[string]interface{} | ||
}{sample.Tags.CloneTags(), values} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be tags
instead of sample.Tags.CloneTags()
stats/influxdb/collector.go
Outdated
tags = sample.Tags.CloneTags() | ||
values = c.extractFields(tags) | ||
values["value"] = sample.Value // Ok since the "value" field will always be overwritten | ||
cache[sample.Tags] = struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, you can create a very local type inside the commit()
function itself like this:
type cacheItem struct {
tags map[string]string
values map[string]interface{}
}
and then have cache := map[*stats.SampleTags]cacheItem{}
and cache[sample.Tags] = cacheItem{tags,values}
without having to redeclare the whole struct.
stats/influxdb/collector.go
Outdated
} else { | ||
tags = sample.Tags.CloneTags() | ||
values = c.extractFields(tags) | ||
values["value"] = sample.Value // Ok since the "value" field will always be overwritten |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment above, the whole values
generation should probably be outside of the if {} else {}
block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
This implements:
#569
The code can be optimised so that we don't create a new map each time in
func extractFields
but I would like some input on how to do that.To configure this see samples/config.json or you can use the env var INFLUXDB_TAGS_AS_FIELDS. Default value is "iter,vu,url".