Skip to content

Daily Active User Metrics #3735

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

Merged
merged 28 commits into from
Sep 1, 2022
Merged

Daily Active User Metrics #3735

merged 28 commits into from
Sep 1, 2022

Conversation

ammario
Copy link
Member

@ammario ammario commented Aug 29, 2022

image

User facing:

  • Add API for retrieving stats
  • Add Metrics view to Users page heading Active user metrics 📊  #3633
  • Add zero state for DAU chart.
  • Fill in empty dates in backend (0 DAUs)
  • Replace interval environment variables with some other approach to debug configuration that is less likely to be abused by a customer. Perhaps only parse these debug flags when CODER_EXPERIMENTAL is true.
  • Move time series into per-template presentation

Plumbing:

  • Add stats recording to the agent
  • Report stats to coder server
  • Record stats in database (agent_stats table)
  • Replace AgentReportStats context based cancellation with io.Closer
  • Add Foreign Keys to agent_stats table (not doing this since there is no deleted for users)
  • Add metrics cache service
    • Add old stat pruning
  • Add frontend tests
  • Move agent report endpoint into workspaceagents
  • Test multiple templates in Cache test

@ammario ammario linked an issue Aug 29, 2022 that may be closed by this pull request
@ammario ammario requested a review from kylecarbs August 30, 2022 05:54
@ammario
Copy link
Member Author

ammario commented Aug 30, 2022

@kylecarbs most of the backend is complete. I plan on adding the frontend tomorrow.

Copy link
Member

@Emyrk Emyrk left a comment

Choose a reason for hiding this comment

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

Putting this in the agent solves a lot of the issues we had with v1 👍.

This does only catch long lived connections. Like I would assume most ProtocolDials will be short http requests, right? Those would never likely be picked up.

return err
}

s := stats()
Copy link
Member

Choose a reason for hiding this comment

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

I am guessing this returns a threadsafe copy for the later json marshal. Would be nice if this copy didn't have a mutex in it. Can be misleading to see a mutex, and might assume to use it.

@socket-security
Copy link

socket-security bot commented Sep 1, 2022

Socket Security Report

Dependency issues detected. If you merge this pull request, you will not be alerted to the instances of these issues again.

📜 New install scripts detected

A dependency change in this PR is introducing new install scripts to your install step.

Package Script field Location
canvas@2.9.3 (added) binding.gyp site/package.json
canvas@2.9.3 (added) install site/package.json
core-js@3.25.0 (upgraded) postinstall site/package.json via @storybook/addon-actions@6.5.9
core-js-pure@3.25.0 (upgraded) postinstall site/package.json via @pmmmwh/react-refresh-webpack-plugin@0.5.7
🫣 Native code

Contains native code which could be a vector to obscure malicious code, and generally decrease the likelihood of reproducible or reliable installs.

Package Location
canvas@2.9.3 (added) site/package.json
Socket.dev scan summary
Issue Status
Did you mean? ✅ no new possible package typos
Install scripts ⚠️ 4 new install scripts detected
Telemetry ✅ no new telemetry
Troll package ✅ no new troll packages
Malware ✅ no new malware
Native code ⚠️ 1 new native module detected

Powered by socket.dev

@ammario ammario changed the title Metrics Daily Active User Metrics Sep 1, 2022
@ammario ammario marked this pull request as ready for review September 1, 2022 02:30
@ammario ammario requested a review from a team as a code owner September 1, 2022 02:30
@ammario ammario requested review from jsjoeio and removed request for a team September 1, 2022 02:30
@ammario ammario force-pushed the metrics branch 3 times, most recently from d593346 to 4b285fa Compare September 1, 2022 03:51
jsjoeio
jsjoeio previously requested changes Sep 1, 2022
Copy link
Contributor

@jsjoeio jsjoeio left a comment

Choose a reason for hiding this comment

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

FE: Didn't review the whole thing yet but can later today. So far looking good, just some housekeeping stuff mostly that we should address

@@ -113,6 +117,7 @@
"prettier": "2.7.1",
"prettier-plugin-organize-imports": "3.0.0",
"react-hot-loader": "4.13.0",
"resize-observer": "^1.0.4",
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we update the PR description explaining the new libraries added and the reason behind them? That'll help the rest of the team aware 👍🏼

Copy link
Member Author

Choose a reason for hiding this comment

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

I shall explain in the comments.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ugh... can't comment package.json...

Copy link
Contributor

Choose a reason for hiding this comment

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

PR description works well too

// From codersdk/metrics.go
export interface DAUEntry {
readonly date: string
readonly daus: number
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: DAUEntry.daus feels a bit weird. Would also prefer for us to use consistent casing if possible: DAU vs. dau

Maybe total?

Copy link
Member Author

Choose a reason for hiding this comment

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

The casing is an unfortunate side effect of the code generator, which is outside the scope of this PR. I can rename to total if you feel strongly, but I think daus is clear enough.

Comment on lines 7 to 9
Object.defineProperty(window, "ResizeObserver", {
value: ResizeObserver,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we adding this to the window? Feels like a codesmell but I blame my lack of context and understanding here.

Copy link
Contributor

Choose a reason for hiding this comment

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

If this is needed, could we add it in a beforeEach or beforeAll block inside your describe block?

We also need to remove it from the window or it could affect other tests so it should be removed in a teardown function like afterAll.

Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately, there's no way to do this w/o modifying the global state. Since jest runs parallel, even if we use beforeAll I believe we may have state issues.

Comment on lines 29 to 32
export const Language = {
loadingText: "DAU stats are loading. Check back later.",
chartTitle: "Daily Active Users",
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think now that we have internationalization, we're not using this pattern anymore and instead using the t() and then referencing strings from a json file. There should be some examples in the codebase but if you want specifics, let me know!

Copy link
Member Author

Choose a reason for hiding this comment

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

Slightly off-topic, but why are we pursuing internationalization? That seems like a feature that is 3+ years out.

Copy link
Member

Choose a reason for hiding this comment

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

Hey @ammario I was on vacation when you asked this, but I wanted to follow up. There are a couple reasons we're using this pattern, and some are outlined below:

  1. It's helpful to have all strings colocated. We can be better about using consistent language, and we can re-use strings more easily.
  2. Translation libraries have features like string interpolation or pluralization built into them, which helps us avoid a bunch of boilerplate JSX, keeping our component files lightweight.
  3. We've actually had an internationalization solution in our app since I started, but prior to using i18n, we were using Language objects which is a problematic pattern for several reasons -you can read more in our FE Variety notes if you're interested
  4. Even if translation is a long way off, it's best to establish a pattern before the app grows large.

Anyway, let me know if you want to chat about it further!

Copy link
Member

@kylecarbs kylecarbs left a comment

Choose a reason for hiding this comment

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

Take a look at my comments before merge, but overall LGTM!

agent_id uuid NOT NULL,
workspace_id uuid NOT NULL,
template_id uuid NOT NULL,
payload jsonb NOT NULL
Copy link
Member

Choose a reason for hiding this comment

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

Payload is weird since this is a specific stat type. Why not structure the data?

Copy link
Member Author

Choose a reason for hiding this comment

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

I anticipate significant churn in this structure, and there can be many agent versions simultaneously reporting to the same coderd. I'm thinking about long-lived workspaces that never stop.

@ammario ammario enabled auto-merge (squash) September 1, 2022 19:53
@ammario ammario dismissed jsjoeio’s stale review September 1, 2022 19:58

Mostly addressed

@ammario ammario merged commit 30f8fd9 into main Sep 1, 2022
@ammario ammario deleted the metrics branch September 1, 2022 19:58
@@ -38,6 +38,7 @@
"@xstate/react": "3.0.1",
"axios": "0.26.1",
"can-ndjson-stream": "1.0.2",
"chart.js": "^3.5.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we still have a ^ here

"react": "18.2.0",
"react-chartjs-2": "^4.3.1",
Copy link
Contributor

Choose a reason for hiding this comment

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

One here as well

"react-dom": "18.2.0",
"react-helmet-async": "1.3.0",
"react-i18next": "11.18.4",
"react-markdown": "8.0.3",
"react-router-dom": "6.3.0",
"react-router-dom": "^6.3.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

and here as well

@@ -87,6 +89,7 @@
"@typescript-eslint/eslint-plugin": "5.31.0",
"@typescript-eslint/parser": "5.31.0",
"@xstate/cli": "0.3.0",
"canvas": "^2.9.3",
Copy link
Contributor

Choose a reason for hiding this comment

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

and here

@@ -113,6 +117,7 @@
"prettier": "2.7.1",
"prettier-plugin-organize-imports": "3.0.0",
"react-hot-loader": "4.13.0",
"resize-observer": "^1.0.4",
Copy link
Contributor

Choose a reason for hiding this comment

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

PR description works well too

Comment on lines +7 to +10
// The Chart performs dynamic resizes which fail in tests without this.
Object.defineProperty(window, "ResizeObserver", {
value: ResizeObserver,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is needed for these tests, it's usually best to put it in a setup function like beforeEach or beforeAll. Otherwise it can have unintended side effects.

Comment on lines +39 to +42
export const Language = {
loadingText: "DAU stats are loading. Check back later.",
chartTitle: "Daily Active Users",
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the old pattern. As mentioned before, the new pattern is to create a json file in here and then use the t function like so.

Not a huge deal but in the future we should shoot for consistency 👍🏼

Comment on lines +16 to +19
Object.defineProperty(window, "ResizeObserver", {
value: ResizeObserver,
})

Copy link
Contributor

Choose a reason for hiding this comment

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

Similar comment as above, would be best to keep this in a setup and teardown function

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.

Active user metrics 📊
6 participants