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

JS API for user-specified high-cardinality metrics metadata #2766

Closed
na-- opened this issue Nov 10, 2022 · 1 comment · Fixed by #3037
Closed

JS API for user-specified high-cardinality metrics metadata #2766

na-- opened this issue Nov 10, 2022 · 1 comment · Fixed by #3037
Assignees
Labels
evaluation needed proposal needs to be validated or tested before fully implementing it in k6 feature
Milestone

Comments

@na--
Copy link
Member

na-- commented Nov 10, 2022

Feature Description

k6 v0.41.0 added a Metadata property for storing high-cardinality metadata to all metric.Sample values that is not part of the Sample's TimeSeries:

k6/metrics/sample.go

Lines 19 to 33 in e14f14d

// A Sample is a single metric measurement at a specific point in time. It can
// have two sets of string key=value metadata:
// - indexed Tags for low-cardinality data that are part of the TimeSeries
// - optional non-indexed Metadata that are meant for high-cardinality information
type Sample struct {
TimeSeries
Time time.Time
Value float64
// Optional high-cardinality metadata that won't be indexed in atlas.
//
// It can be nil if it wasn't explicitly specified, reduce memory
// allocations and GC overhead.
Metadata map[string]string
}

However, we decided (#2584 (comment)) to not merge the originally proposed JS APIs from #2654 that allowed users to set whatever metadata values they wanted from their scripts, since they presented a somewhat confusing UX. The Sample.Metadata property is currently only accessible from Go code, either the built-in k6 modules and outputs or from xk6 extensions.

I am adding this issue as a placeholder, so it can be referenced by other issues. We still haven't reached a consensus how (and even if) the Metadata should be made accessible from user scripts.

A major use case for this feature is distributed tracing (#2128), though it doesn't necessarily need JS code to be able to access and set custom metrics metadata. If anyone has another use cases that will benefit from this feature, please share them here.

Suggested Solution (optional)

No consensus and firm proposals yet. There are various ideas with different pros and cons, and this deserves a broader discussion. Some of them:

  • at this point the original API with non-indexed tags (Support non-indexable high-cardinality metric tags/metadata #2654) is probably out of the running, though it still has its benefits
  • a promising idea is to have a metrics.withMetadata(metaObj, callback) API similar to group(), though that has its own problems with async APIs (group doesn't work with async calls well  #2728)
  • a metadata property in all of the calls where we currently support tags has the problem that it's yet another custom thing that needs to be implemented in every protocol, it is ambiguous (is the "metadata" property about the call and not its metrics?) and it conflicts with the already existing metadata property in gRPC calls.

We are discussing this internally, but if someone has API proposals or ideas, please share them here.

Already existing or connected issues / PRs (optional)

@na-- na-- added feature evaluation needed proposal needs to be validated or tested before fully implementing it in k6 labels Nov 10, 2022
@mstoykov
Copy link
Contributor

mstoykov commented Feb 9, 2023

After some internal discussion there is some agreement on a stop-gappy solution that let's users set metadata from JS code, but skips over the problems above.

Proposal

Add metadata property under "k6/execution".vu probably as "k6/execution".vu.metrics.metadata.

That property behaves exactly as the current "k6/execution".vu.tags but insetad of changing the tags it changes the metadata.

Additional API changes:

Add "k6/execution".vu.metrics.tags which is an alias for "k6/execution".vu.tags to be more consistent.

Future APIs that do something with metrics and are per VU can use this as potential place to be added as well. But that is outside the scope of this discussion.

Async caveats:

Same as with "k6/execution".vu.tags
Note: examples are with tags so they are easier to try.

import exec from "k6/execution";
export default async () => {

   exec.vu.tags["someTag"] = "value"
   await asyncCall() // not only is this call tagged with `"someTag"`, but anything that runs while it is awaited
   delete exec.vu.tags["someTag"] 
}

In order to be able to use await you need to first get the promise from asyncCall , reset the tag and then await it.
You might do it in a helper function.

import exec from "k6/execution";
function helper(tagName, tagValue) {
  try { // theorethically this can also reset to the old value
    exec.vu.tags[tagName] = tagValue
    return asyncCall()
  } finally {
   delete exec.vu.tags[tagName] 
  }
}

export default async () => {
  await helper("someTag", "someValue")
}

If you want to implement something like withMetadata you could ... but it will have the same problems group does.

On a per call basis you can have somethign with lambda as in

import exec from "k6/execution";
function helper(f, tagName, tagValue) {
  try { // theorethically this can also reset to the old value
    exec.vu.tags[tagName] = tagValue
    return f() // f here is a call returning a promise
  } finally {
   delete exec.vu.tags[tagName] 
  }
}
export default async () => {
  await f(()=> {
    return asyncCall("with",  "some", "arguments")
  }, "someTag", "someValue"
})

@na-- na-- added this to the v0.45.0 milestone Mar 1, 2023
@mstoykov mstoykov self-assigned this Apr 19, 2023
mstoykov added a commit that referenced this issue Apr 26, 2023
A bare minimum API to set and get metadata set for the whole VU.

It is added on top of the vu property and on top of a new `metrics` one
so. If `k6/execution` is imported as `exec` -
`exec.vu.metrics.metadata["foo"]` will get you the currently set
metadata for `foo` the same way `exec.vu.tags["bar"]` will get you the
value of the tag `bar`.

Setting metadata is the same.

It also adds `exec.vu.metrics.tags` which is the same as `exec.vu.tags`
for consistency. While leaving the old one for backwards compatibility.

This also includes dropping the ability to list metadata keys and values
from `exec.vu.tags`. This was likely left in by mistake.

This is a breaking change due to the fact that before that `iter` and
`vu` could've been accessed this way but this will now need to be done
through the new `exec.vu.metrics.metadata`.

Closes #2766
mstoykov added a commit that referenced this issue May 9, 2023
A bare minimum API to set and get metadata set for the whole VU.

It is added on top of the vu property and on top of a new `metrics` one
so. If `k6/execution` is imported as `exec` -
`exec.vu.metrics.metadata["foo"]` will get you the currently set
metadata for `foo` the same way `exec.vu.tags["bar"]` will get you the
value of the tag `bar`.

Setting metadata is the same.

It also adds `exec.vu.metrics.tags` which is the same as `exec.vu.tags`
for consistency. While leaving the old one for backwards compatibility.

This also includes dropping the ability to list metadata keys and values
from `exec.vu.tags`. This was likely left in by mistake.

This is a breaking change due to the fact that before that `iter` and
`vu` could've been accessed this way but this will now need to be done
through the new `exec.vu.metrics.metadata`.

Closes #2766


Co-authored-by: Ivan <[email protected]>
Co-authored-by: Ivan Mirić <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
evaluation needed proposal needs to be validated or tested before fully implementing it in k6 feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants