Page MenuHomePhabricator

Setup and populate initial version of user_agents_info table
Closed, ResolvedPublic

Description

Background

T429280#12040549

Scope

  • Create and start populating an initial version of user_agents_info table
  • Note: The initial version (more like a beta version) will not have all the fields populated.
    • We will have some placeholder dimensions that will be populated later in July 2026.

Acceptance criteria

  • DDL for new table, wmf_traffic.user_agents_info is defined and reviewed (aligned with KAPOW and potential future information to be added)
  • Table wmf_traffic.user_agents_info is created
  • Addition DAG task or a new DAG populates the table (compute_user_agent_compliance_hourly_dag.py)

Schema

column namedescription
user_agentUser-Agent string
sourceSource dataset of the User-Agent. For example, webrequest or Dumps
user_agent_structUser-agent (map processed to struct) with browser_family, browser_major, device_family, os_family, os_major, os_minor and wmf_app_version keys and associated values
contact_infoStruct of contact information with like {"email": "foo@bar.com", "url": "https://foo.bar.com", ...}
ua_policy_compliance_levelcompliant, partially_compliant, non_compliant, non_compliant_placeholder, or browser_like
bot_categorywill be based on Cloudflare's bot taxonomy, T429280#12037609 - will NULL for the initial version
identified_agentInternally identified agent types, wikipedia_app, instant_commons, mw_foreign_repo etc.
request_counttotal rows in the source where the UA appeared during that hour
cached_requests_proportionproportion of requests to cached content
response_size_totaltotal bandwidth served to the User-Agent during that hour
cached_response_size_total_proportionproportion of response size to cached content
distinct_ip_countdistinct IPs that have sent this UA (for the source in that hour)
distinct_uri_hostdistinct hosts that the UA has hit during that hour
hourThe hour during which the User-Agent was seen, at the respective source

Details

Event Timeline

Instead of a map as in webrequest, let's use a struct to store parsed user agent data.

I'd also add some statistics in this table: how many rows seen for the source/hour for instance.

You can retrieve the metadata (including category) for all bots listed in the Cloudflare Radar Bots Directory with this Bash script used to collect this data (it only requires a free API token with the minimal read permission scope).

Exciting work!
My thoughts:

  • Are we going to store only bot-like User-Agents or all of them? Maybe, since we can not extract contact/agent/policy information from them and they are also not useful for KAPOW, we might not want to store them to save storage space.
  • There might be cases where a User-Agent comes from more than one source. Will we store an array of sources?
  • The contact method and contact info could be a single map field maybe? Since, whenever there's an i.e. an "email" method, there should be an email value no? Like {"email": "foo@bar.com", "url": "https://foo.bar.com", ...}. Please, disagree with this, it's just a suggestion.
  • Maybe it would be useful to have a request count for each User-Agent? This helps identifying which User-Agents are common vs. long-tail, which can sometimes be valuable? But not sure the request count concept applies to all sources. So.. maybe not? (Oh, I see that Joseph said something similar, well +1)

Are we going to store only bot-like User-Agents or all of them? Maybe, since we can not extract contact/agent/policy information from them and they are also not useful for KAPOW, we might not want to store them to save storage space.

That seems fair to me, if anyone is interested in "the count of requests for any UA" they can also aggregate wmf_traffic.ja3n_ua_hourly.

I like the mention of "bot-like"; to be explicit, I think we should keep UAs that say "Bot" somewhere but offer no other structured information for us to extract, as a catch-all for us to investigate later.

I'd also add some statistics in this table: how many rows seen for the source/hour for instance.

Yep, good idea!

Are we going to store only bot-like User-Agents or all of them? Maybe, since we can not extract contact/agent/policy information from them and they are also not useful for KAPOW, we might not want to store them to save storage space.

I'd prefer include classification of all User-Agents. Although a decent chunk would be brower-like, they are not all browsers. There are also certain non-compliant user-agents, like with example.com and other placeholder domains that we're tracking with non_compliant_placeholder category. Agents that may not be bot-like, but we still need to determine their compliance, and have that info, which can be used to join with other tables downstream like API requests and Dumps to calculate compliance at various access paths. Regarding storage, wmf_traffic.user_agent_compliance_classified_hourly (which the new table will be similar to) is about 1-2 GB / day of unique User-Agents seen in webrequest, stored for 90 days at once.

There might be cases where a User-Agent comes from more than one source. Will we store an array of sources?

yes, we will have source information stored as one row per source approach.

The contact method and contact info could be a single map field maybe?

Sounds good.

I have the updated the proposed schema in the description based on the discussion and also additional stats columns.

Looks good to me!

I'm just wondering whether is_bot_like should be a subcategory under bot_category: semantically, it's a fallback that says "we think this is a bot, but we don't know how to categorize it".

Looks good to me!

I'm just wondering whether is_bot_like should be a subcategory under bot_category: semantically, it's a fallback that says "we think this is a bot, but we don't know how to categorize it".

Good point, we can have it like -

  • for non-bot like agents, bot_category will be NULL.
  • For bots
    • if we identify it would be the category name
    • for which couldn't be categorized, we can set it to something like generic_bot.

Change #1306491 had a related patch set uploaded (by KCVelaga; author: KCVelaga):

[analytics/refinery@master] DDL for wmf_traffic.user_agents_info table.

https://gerrit.wikimedia.org/r/1306491

I just had a chat with @Pablo - reviewing the Cloudflare's taxonomy.

There are additional pieces of information in the taxonomy JSON that Cloudflare is providing that might be useful for us to consider including in this table:

  • kind: bot or agent
    • can be helpful to differentiate traffic from AI agents vs. regular bots when needed.
  • bot_name: normalized version of the bot name (aka Slug)
    • for example, a User-Agents are like (a public example): AdsBot-Google (+http://www.google.com/adsbot.html) and normalized_bot_name would be AdsBot-Google
    • could be helpful during visualization and reporting
  • bot_operator: operator (usually company) of the bot. One company can have multiple bots.
    • Google for instance has multiple bots and agents, it can be helpful to look up and aggregate all the traffic originating from a specific company.
    • I definitely have a use case for this for something I am planning in Q1.

These are just additional keys in the JSON, so not much additional work to extract - if we decide to include them.

What do you all think?

That would mean we'd have to regularly import the JSON to be able to exact-match individual UAs against it, rather than extracting information from the UA ourselves like we currently do. Is that right?

I think this makes sense as a future improvement for user_agents_info, but we probably just want to file it as a follow-up ticket.

I do see that bot_name and bot_operator would be good to have.

About bot or agent, if I'm reading the link right, this one is less useful to us. It just means that Cloudflare is able to verify the identity of the bot and pass it on to its client for use in firewall rules. This is done not using the UA, but with IP lists or Web Bot Auth. Since we don't do the same verification, the field doesn't quite carry the same meaning.

That would mean we'd have to regularly import the JSON to be able to exact-match individual UAs against it, rather than extracting information from the UA ourselves like we currently do. Is that right?

I think this makes sense as a future improvement for user_agents_info, but we probably just want to file it as a follow-up ticket.

I do see that bot_name and bot_operator would be good to have.

About bot or agent, if I'm reading the link right, this one is less useful to us. It just means that Cloudflare is able to verify the identity of the bot and pass it on to its client for use in firewall rules. This is done not using the UA, but with IP lists or Web Bot Auth. Since we don't do the same verification, the field doesn't quite carry the same meaning.

Sounds good, let's get the initial version out and plan improvements later :)

Change #1306491 merged by Joal:

[analytics/refinery@master] DDL for wmf_traffic.user_agents_info table.

https://gerrit.wikimedia.org/r/1306491

Mentioned in SAL (#wikimedia-analytics) [2026-07-10T08:30:10Z] <JavierMonton> Deploying Refinery at 1abf22ea for changes 1308121/T427068 1306491/T430020 and 1308190

The initial version of the table is setup and is being populated:

There are planned follow-up T430673 to extend this table with other info. I will create a follow-up task to switch some of the downstream pipelines and also deprecate the User-Agent compliance related pipeline / code.