Skip to content

Conversation

@mayagbarnes
Copy link
Collaborator

@mayagbarnes mayagbarnes commented Oct 24, 2024

Describe your changes

Final feature PR to execute the (soft) transition our metrics provider. We plan to use both systems for one release, allowing us to test/compare/adjust based on metrics at scale through the 1.40 release. We will then remove legacy provider code & switch to the production webhook after validating the new system is working as expected.

This PR consolidates the following logic from PRs into this branch:

Outside these PRs:

  • one commit was added to ensure consistency of anonymousIds between the two systems by manually setting the anonymousId for Segment
  • another commit tweaked how anonymousId was set in the cookie from localStorage to address an issue in Community Cloud where "%22" was showing up as part of the anonymousId
  • Enforced stricter typing in MetricsManager with new MetricsEvent proto

Testing Plan

  • Unit Tests: ✅
  • Manual Testing: ✅

@mayagbarnes mayagbarnes added impact:internal PR changes only affect internal code change:refactor PR contains code refactoring without behavior change labels Oct 24, 2024
mayagbarnes and others added 7 commits October 24, 2024 14:38
Initial renaming of `SegmentMetricsManager` class, files, & imports to `MetricsManager` - setting stage for telemetry migration
**Metrics Migration Part 1**: This PR extends the `host_config` endpoint
to add a `metricsUrl`, which will serve as the destination URL for
metrics data.

`metricsUrl` for `host_config` endpoint will handle 4 options:
* **`"off"`**: Turns off metrics collection
* **`"<url>"`**: Sends metrics events to the passed URL
* _The logic to build/send event with necessary fields will be added in
a separate PR_
* **`"postMessage"`**: Sends metrics events via `postMessage` API
    *  _This logic will be added in a separate PR_
* **`" "`**: Default (OS) -  telemetry destination url will be fetched

Should no url be received from the BE, the FE will attempt to fetch the
telemetry url from `data.streamlit.io/metrics.json`, caching it in
localStorage if successful. If there is no `metricsUrl`, no telemetry is
sent.
Update the `GUEST_READY` message payload to provide more information on Streamlit load time
**Metrics Migration Part 2:** This PR adds a `MetricsEvent` proto for a
single source of truth for event fields, as well as the infrastructure
necessary to send event data to the testing Fivetran webhook in parallel
with Segment. This includes building the `anonymousId` & `context` data
fields for FW that we received by default from Segment.
**Metrics Migration Part 3:** As a follow up to the metrics options in hostConfig endpoint (PR #9611), this PR adds the ability to send metrics events to the host via postMessage API using our existing Host <-> Guest communication.
@mayagbarnes mayagbarnes marked this pull request as ready for review October 24, 2024 23:57
@mayagbarnes mayagbarnes requested a review from a team as a code owner October 24, 2024 23:57
@mayagbarnes mayagbarnes added the security-assessment-completed Security assessment has been completed for PR label Oct 29, 2024
Copy link
Collaborator

@lukasmasuch lukasmasuch left a comment

Choose a reason for hiding this comment

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

I think the requestDefaultMetricsConfig might need to be awaited. Otherwise, LGTM 👍

*/

// Timestamp when the Streamlit execution started for GUEST_READY message
const streamlitExecutionStartedAt = Date.now()
Copy link
Collaborator

@lukasmasuch lukasmasuch Oct 26, 2024

Choose a reason for hiding this comment

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

I'm a bit on the fence if we should pass this through our app stack as a prop since its a global variable that does not change after initialization. The two alternatives are: 1) Put this variable into the context for this prop, see WindowDimensions as an example
2) Put it into a global window variable, e.g. window.streamlitExecutionStartedAt and read it out when the GUEST_READY message is send.

But this could easily be discussed and implemented as a follow-up after cut off. The way it's implemented right now is fine.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FWIW I also preferred window variable to passing as prop, though Pat & Benny leaned toward prop (discussion here). Now at least votes are tied 😂

Comment on lines 94 to 125
public initialize({
gatherUsageStats,
sendMessageToHost,
}: {
gatherUsageStats: boolean
sendMessageToHost: (message: IGuestToHostMessage) => void
}): void {
this.initialized = true
this.sendMessageToHost = sendMessageToHost
// Handle if the user or the host has disabled metrics
this.actuallySendMetrics = gatherUsageStats && this.metricsUrl !== "off"
this.getAnonymousId()

// Trigger fallback to fetch default metrics config if not provided by host
if (this.actuallySendMetrics && !this.metricsUrl) {
this.requestDefaultMetricsConfig()

// If metricsUrl still undefined, deactivate metrics
if (!this.metricsUrl) {
logError("Undefined metrics config - deactivating metrics tracking.")
this.actuallySendMetrics = false
}
}

if (this.actuallySendMetrics) {
// Segment will not initialize if this is rendered with SSR
initializeSegment()
this.sendPendingEvents()
}

logAlways("Gather usage stats: ", this.actuallySendMetrics)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the initialize needs to be async and await the requestDefaultMetricsConfig method. Otherwise, the metrics gathering might be deactivated because the async function has not yet finished execution. And it would probably be good to move the this.initialized = true to the end, so that it can initialize async and collect pending events at the same time.

  public async initialize({
    gatherUsageStats,
    sendMessageToHost,
  }: {
    gatherUsageStats: boolean
    sendMessageToHost: (message: IGuestToHostMessage) => void
  }): Promise<void> {
    this.sendMessageToHost = sendMessageToHost
    // Handle if the user or the host has disabled metrics
    this.actuallySendMetrics = gatherUsageStats && this.metricsUrl !== "off"
    this.getAnonymousId()

    // Trigger fallback to fetch default metrics config if not provided by host
    if (this.actuallySendMetrics && !this.metricsUrl) {
      await this.requestDefaultMetricsConfig()

      // If metricsUrl still undefined, deactivate metrics
      if (!this.metricsUrl) {
        logError("Undefined metrics config - deactivating metrics tracking.")
        this.actuallySendMetrics = false
      }
    }

    if (this.actuallySendMetrics) {
      // Segment will not initialize if this is rendered with SSR
      initializeSegment()
      this.sendPendingEvents()
    }

    logAlways("Gather usage stats: ", this.actuallySendMetrics)
    this.initialized = true
  }

@mayagbarnes mayagbarnes merged commit 245d749 into develop Oct 30, 2024
@mayagbarnes mayagbarnes deleted the metrics-migration branch October 30, 2024 08:30
edegp pushed a commit to edegp/streamlit that referenced this pull request Jan 19, 2025
Final feature PR to execute the (soft) transition of our metrics provider
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:refactor PR contains code refactoring without behavior change impact:internal PR changes only affect internal code security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants