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 page.on('metric') #1456

Merged
merged 49 commits into from
Oct 7, 2024
Merged

Add page.on('metric') #1456

merged 49 commits into from
Oct 7, 2024

Conversation

ankur22
Copy link
Collaborator

@ankur22 ankur22 commented Oct 3, 2024

What?

This adds the functionality to group url tags by intercepting all metrics that the browser module emits, compare and match with the supplied regex(s), and then set the name and url tags with the new name supplied by the user in the script.

Why?

A lot of websites will perform requests that contain dynamic parts in the request url, which could be ids that are generated on every new session, account ids, or for cache busting purposes.

This will help in two ways:

  1. Avoids high cardinality issues where tests work with websites with a lot of these dynamic urls.
  2. It becomes trivial to correlate urls which are for the same page but contain dynamic parts in the url (ids/cache busting/etc).

How

The user first needs to register an event handler:

sequenceDiagram
  box main goroutine
  participant page.on('metric')
  participant p.eventHandlers
  end
  
  page.on('metric') ->> p.eventHandlers: add metric handler
Loading

This is a high level overview of what happens when a http request is started and a metric is about to be emitted:

sequenceDiagram
    box CDP handler goroutine
    participant emitRequestMetrics
    participant handleURLTag
    participant page.urlTagName
    end
    box taskqueue goroutine
    participant handler
    participant MetricEvent.Tag
    end
    box sobek context
    participant _k6BrowserURLGroupingTest
    end

    emitRequestMetrics ->> handleURLTag: url, tags
    handleURLTag ->> page.urlTagName: url
    page.urlTagName ->> page.urlTagName: create MetricEvent{url}
    loop each p.eventHandler
    page.urlTagName ->> handler: MetricEvent{url}
    Note right of handler: Regex supplied by user in handler in URLOverrides{}
    handler ->> MetricEvent.Tag: URLTagPatterns{}
    loop each URLTagPatterns
    MetricEvent.Tag ->> _k6BrowserURLGroupingTest: URLTagPattern.urlRegEx, url
    Note right of _k6BrowserURLGroupingTest: if match found, return true, else false
    _k6BrowserURLGroupingTest ->> MetricEvent.Tag: return true|false
    end
    Note left of MetricEvent.Tag: assign user defined name to MetricEvent.name if match found.
    MetricEvent.Tag ->> handler: return
    handler ->> page.urlTagName: return
    end
    Note left of page.urlTagName: read MetricEvent.name when it's not nil and set urlMatched.
    page.urlTagName ->> handleURLTag: return newTagName, urlMatched
    Note left of handleURLTag: set tags.url and tags.name as newTagName if urlMatched is true
    handleURLTag ->> emitRequestMetrics: return tags
Loading

The same happens when we're about to emit response and web vital metrics.

Checklist

  • I have performed a self-review of my code
  • I have added tests for my changes
  • I have commented on my code, particularly in hard-to-understand areas

Related PR(s)/Issue(s)

Updates: #371

examples/pageon-metric.js Outdated Show resolved Hide resolved
common/page.go Outdated Show resolved Hide resolved
@ankur22 ankur22 requested a review from inancgumus October 3, 2024 12:35
@ankur22 ankur22 force-pushed the add/page-on-metric branch from 3c8aa19 to ea8df64 Compare October 3, 2024 12:37
Copy link
Member

@inancgumus inancgumus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely tackled with this complex issue 🙇! TBH, I should also admit that I'm not sure I understand this PR's code fully 😄 (my bad) So, I have some comments to discuss. None of them are strong opinions. Some suggestions are to hopefully make this change more descriptive, extendable, and maintainable.

browser/metric_mapping.go Outdated Show resolved Hide resolved
browser/page_mapping.go Show resolved Hide resolved
browser/page_mapping.go Show resolved Hide resolved
browser/page_mapping.go Show resolved Hide resolved
browser/page_mapping.go Show resolved Hide resolved
common/page.go Outdated Show resolved Hide resolved
common/page.go Show resolved Hide resolved
examples/pageon-metric.js Outdated Show resolved Hide resolved
tests/page_test.go Outdated Show resolved Hide resolved
tests/page_test.go Outdated Show resolved Hide resolved
@inancgumus
Copy link
Member

inancgumus commented Oct 3, 2024

Idea: If the generics solution doesn't turn out OK, we can also invent a new type, PageOnEvent, for Page.On events (eventHandlers map[string][]func(PageOnEvent)) to get rid of fragile runtime type assertions. PageOnEvent struct { ConsoleMessage *ConsoleMessage; ExportedMetric *ExportedMetric }. Each event can use only the field they are interested in, removing the need for type assertions.

@ankur22
Copy link
Collaborator Author

ankur22 commented Oct 4, 2024

Idea: If the generics solution doesn't turn out OK, we can also invent a new type, PageOnEvent, for Page.On events (eventHandlers map[string][]func(PageOnEvent)) to get rid of fragile runtime type assertions. PageOnEvent struct { ConsoleMessage *ConsoleMessage; ExportedMetric *ExportedMetric }. Each event can use only the field they are interested in, removing the need for type assertions.

Nice alternative!

I've linked this comment to a comment in the generalize page.on events issue.

We can tackle the generalization after merging in this PR 👍

@ankur22 ankur22 force-pushed the add/page-on-metric branch from 94f2063 to ef24fb2 Compare October 4, 2024 12:47
@ankur22
Copy link
Collaborator Author

ankur22 commented Oct 4, 2024

Nicely tackled with this complex issue 🙇! TBH, I should also admit that I'm not sure I understand this PR's code fully 😄 (my bad) So, I have some comments to discuss. None of them are strong opinions. Some suggestions are to hopefully make this change more descriptive, extendable, and maintainable.

Thanks for the review 🙏 I agree that this is a bit confusing. I will create a diagram to make things a bit clearer.

@ankur22 ankur22 force-pushed the add/page-on-metric branch from f53dfa5 to 66e3bce Compare October 4, 2024 13:56
@ankur22
Copy link
Collaborator Author

ankur22 commented Oct 4, 2024

@inancgumus i created the diagram and placed it in the description of the issue. Let me know if it helps or not 🙂

@ankur22 ankur22 force-pushed the add/page-on-metric branch 2 times, most recently from 84fb60f to 31050eb Compare October 7, 2024 15:10
ankur22 added 15 commits October 7, 2024 20:14
page.on only worked with console message, but we will need it to work
for metric messages too. This change allows us to send any type and
perform the type check in the handler.
It will need to be used in the page mapping.
Using switch to allow for multiple events.
This is what the user will receive in the page.on callback for the
metric event. It's just a skeleton for now.
We can now map and return the metric back to the user so that they can
work with it in the handle.
We want to use the regex engine in the sobek runtime which should be
the same or similar to other JS regex engines. This function will be
used to work with the user defined regex to group metrics with url
tags.
The callback is what will be used to work with the sobek runtime. We
want to avoid using sobek outside the mapping layer.
The name needs to be sent back to the caller that needs it. It is done
via a channel.

When running things on the taskqueue, they are done async. We need to
make this process synchronous. We're relying on the nameCh (the same
channel that the name is sent to the caller on) to notify the called
when the handler has complete.
This function will call the handlers one by one, allowing each handler
to check the url matches and return the name. Each handler will
overwrite the name if multiple handlers match with the given url. This
function needs to wait for the handler to complete before proceeding to
the next handler.
@ankur22 ankur22 force-pushed the add/page-on-metric branch from 760a380 to 45b6cd5 Compare October 7, 2024 19:14
Copy link
Member

@inancgumus inancgumus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work 🎉 I've also added some suggestions for you to consider before merging it in. These suggestions are highly likely due to my lack of understanding of this change enough and are not related to the code itself 😅

browser/metric_event_mapping.go Outdated Show resolved Hide resolved
common/page.go Outdated Show resolved Hide resolved
common/page.go Outdated Show resolved Hide resolved
common/page.go Outdated Show resolved Hide resolved
common/page.go Outdated Show resolved Hide resolved
common/network_manager.go Show resolved Hide resolved
common/network_manager.go Outdated Show resolved Hide resolved
common/network_manager_test.go Show resolved Hide resolved
tests/page_test.go Show resolved Hide resolved
common/page.go Show resolved Hide resolved
Copy link
Member

@inancgumus inancgumus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last note.

examples/pageon-metric.js Outdated Show resolved Hide resolved
@ankur22 ankur22 merged commit f6dd73c into main Oct 7, 2024
23 checks passed
@ankur22 ankur22 deleted the add/page-on-metric branch October 7, 2024 20:17
@ankur22 ankur22 mentioned this pull request Oct 7, 2024
This was referenced Oct 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants