Skip to content

[🐛 Bug]: Selenium Manager telemetry usage #17881

Description

@titusfortner

Description

Selenium's Plausible telemetry usage has exceeded our plan's limit, which locks the dashboard.
Typical usage is well within our limits until we hit outliers. The last time this happened we had Plausible filter out everything coming from a specific version of Selenium that we think was bundled into someone's .NET application.
We need to find a more reliable solution to minimizing large signals that do not provide useful information.

Essentially we've been relying on TTL values in a local cache to minimize reporting volume, with the goal of one update per configuration per hour per system.

The primary issue is containerized systems that run single sessions and close. If Selenium Manager is called, then each execution will send telemetry data.

While digging into possible solutions, I've found a number of things that I think we should address, I'm listing everything here so we can keep track of it. The first two are the ones I'll address first as a priority.

Reduce excessive reporting

  1. Don't report on the first run. Environments that don't keep the cache (ephemeral per-session containers, caches wiped on teardown) cannot be throttled by cache/ttl; we can avoid all of it by ensuring it is the second run instead of the first.

  2. Don't report if the cache can't be read. If the cache is unreadable or get_cache_path() returns None, get_metadata returns empty and telemetry is sent by default.

Bugs (unintended defects)

  1. Panics on an existing-but-unreadable metadata file. File::open(&metadata_path).unwrap() (rust/src/metadata.rs:102): exists() can be true while the open fails with EACCES (write-only mode, cross-UID between runs, SELinux/mount policy), crashing the whole tool. The serde parse error immediately below is handled; the open error is not.

  2. Unsynchronized, non-atomic metadata writes. stats() rewrites the whole se-metadata.json with fs::write and holds no lock (the existing sm.lock only guards per-asset downloads, and runs later than stats()). Concurrent processes (a launch burst, or stats() racing another run's setup()) can lose updates or read a truncated/empty file.

Assumptions we should stop relying on

  1. The only persistence record is discarded when the send-TTL expires. get_metadata prunes the stats list with retain(stats_ttl > now), deleting a config's entire entry at expiry. This welds "suppress sending" to "we've run here before," so there is no durable signal that a cache actually persists across runs.

  2. Persistence is tracked per configuration, though it's really a per-machine property. Whether a cache persists is a property of the machine, but the entry is keyed per browser + browser_version + os + arch + lang + selenium_version. A brand-new config on a durable machine is indistinguishable from a first-ever run on an ephemeral one.

  3. A write→read round-trip of se-metadata.json isn't guaranteed across runs. Violated by write-only, eventually-consistent (network FS), or wiped-between-runs caches. Persistence detection built on this must fail closed (send nothing, don't crash), not assume the read reflects the write.

  4. Telemetry cadence is coupled to the version-discovery TTL. get_ttl() also governs how quickly SM notices new driver/browser releases (browser_ttl / driver_ttl), so retuning it to change telemetry frequency would degrade driver resolution. Telemetry cadence must be decoupled from the version-discovery TTL.

  5. A fire-and-forget send isn't a confirmed delivery. The send is a detached thread::spawn with a 3s timeout; the process can exit before the POST completes, so "decided to send" ≠ "delivered." Any accounting is about decisions, not deliveries.

  6. The stats schema can't change freely once SM releases independently. With Selenium Manager moving to independent releases (see [adr] Selenium Manager released API #17741), two SM versions can share one cache. Renaming, removing, or repurposing fields in se-metadata.json (e.g. changing the meaning of stats_ttl) would break older readers; only additive, serde-default fields are safe.

Status / follow-ups

The immediate fix for the overage is one fail-closed change: defer reporting until an entry written on a prior run is found still present. That resolves #1 and #2 together (with no readable, persisted cache there is never a prior entry, so nothing is sent). Nothing else here is required to get back under the limit.

The rest is tracked for later, not needed now: the robustness bugs (#3, #4), and the design assumptions (#5 to #10) to revisit only if we want more headroom, chiefly decoupling telemetry cadence from the version-discovery TTL and moving persistence detection to an environment-level signal. Two ideas from the investigation, a longer default reporting cadence and an approximate count in the payload, are optional; note Plausible can't sum a numeric property, so count is low value for now, and once-a-day vs once-an-hour barely changes the "what percent uses what" signal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    I-defectSomething is not working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions