Skip to content

feat: report user query bytes#27188

Merged
davidby-influx merged 12 commits intomaster-1.xfrom
DSB/UserQueryStats
Feb 6, 2026
Merged

feat: report user query bytes#27188
davidby-influx merged 12 commits intomaster-1.xfrom
DSB/UserQueryStats

Conversation

@davidby-influx
Copy link
Contributor

@davidby-influx davidby-influx commented Feb 4, 2026

In SHOW STATS, include per-user query bytes.

A new statistics object is returned by the httpd service, userquerybytes.

Off by default, it can be turned on with the environment variable INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=true
or the configuration parameter UserQueryBytesEnabled in the HTTP section.

This is available through SHOW STATS either when all statistics are reported, or alone with FOR 'userquerybytes'

It is returned by /debug/vars, and stored in the _internal database when that is enabled.

The username is a tag, as seen here:

> SHOW STATS for 'userquerybytes'
name: userquerybytes
tags: bind=:8086, user=FRED
userQueryRespBytes
------------------
9009874
> select * from userquerybytes
name: userquerybytes
time                bind  hostname                          user userQueryRespBytes
----                ----  --------                          ---- ------------------
1770405340000000000 :8086 davidby-ThinkPad-X1-Carbon-Gen-13 FRED 476
1770405350000000000 :8086 davidby-ThinkPad-X1-Carbon-Gen-13 FRED 9009874
1770405360000000000 :8086 davidby-ThinkPad-X1-Carbon-Gen-13 FRED 9010036
1770405370000000000 :8086 davidby-ThinkPad-X1-Carbon-Gen-13 FRED 9010036
1770405380000000000 :8086 davidby-ThinkPad-X1-Carbon-Gen-13 FRED 9010036

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds per-user query response bytes tracking to InfluxDB's SHOW STATS output. The feature tracks the total number of bytes sent in query responses for each user, making it easier to monitor user-specific resource consumption.

Changes:

  • Added per-user query bytes tracking using a concurrent map that stores atomic counters per username
  • Updated statistics reporting to include per-user metrics with keys formatted as queryRespBytesUser:<username>
  • Comprehensive test coverage for authenticated users, anonymous users, chunked queries, concurrent access, and SHOW STATS integration

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
services/httpd/handler.go Adds queryBytesPerUser field, modifies Statistics() to include per-user metrics, adds addQueryBytesForUser() helper, and integrates tracking into query endpoints (InfluxQL, Prometheus, and Flux)
services/httpd/handler_test.go Adds comprehensive test suite covering authenticated/anonymous users, chunked queries, concurrent access, and SHOW STATS output validation
pkg/data/gensyncmap/gensyncmap.go Adds IsEmpty() method to the generic sync.Map wrapper (though unused in this PR)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

statFluxQueryRequests = "fluxQueryReq" // Number of flux query requests served.
statFluxQueryRequestDuration = "fluxQueryReqDurationNs" // Number of (wall-time) nanoseconds spent executing Flux query requests.
statFluxQueryRequestBytesTransmitted = "fluxQueryRespBytes" // Sum of all bytes returned in Flux query responses.
statQueryRespBytesValue = "queryRespBytes" // Value field for per-user query response bytes.
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

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

The constant name statQueryRespBytesValue is somewhat redundant since it already contains "value" in the name while being assigned the string "queryRespBytes" (which doesn't include "value"). Consider renaming to statQueryRespBytes for consistency with other constants in this file like statQueryRequestBytesTransmitted.

Copilot uses AI. Check for mistakes.
@davidby-influx davidby-influx marked this pull request as ready for review February 5, 2026 03:10
@davidby-influx davidby-influx marked this pull request as draft February 5, 2026 16:32
@davidby-influx davidby-influx marked this pull request as ready for review February 5, 2026 18:59
Copy link

@devanbenz devanbenz left a comment

Choose a reason for hiding this comment

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

Just one comment in production code.
Need to take a look at the tests next.

devanbenz
devanbenz previously approved these changes Feb 5, 2026
Copy link

@devanbenz devanbenz left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@jdstrand jdstrand left a comment

Choose a reason for hiding this comment

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

@davidby-influx asked me to consider the security aspects of this feature. Note, I did not review the PR implementation.

IME, this isn't really different than the types of things an admin in an application can typically do or what might typically be in an audit log[1]. Today in 1.x, an admin (with WITH ALL PRIVILEGES) can SHOW USERS, SHOW STATS and can SHOW QUERIES. Adding the user tag via userquerybytes as in the PR isn't terribly different and well within the purview of an admin's actions. Before reading the PR, I was thinking that making the behavior opt in would be a good idea and then I saw you did this with INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED (nice!). In this light, I think you designed the feature well for the admin user case. If you haven't already, please ensure you have positive and negative tests that the new user tag is only present when INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=true AND the user is an admin (ie, that the new user tag is not displayed when INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=false with either admin or non-admin and when INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=true with non-admin (and the positive that INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=true with admin works)).

In terms of documentation, in addition to describing the feature, I think we should:

  • where the feature documents the opt-in behavior, be clear that admin is required
  • update https://docs.influxdata.com/influxdb/v1/tools/api/#debugvars-http-endpoint to document some example of sensitive statistics that /debug/vars shows (this does not need to be exhaustive; listing one or two and then using 'etc' is fine), with a link to https://docs.influxdata.com/influxdb/v1/administration/config/#pprof-auth-enabled on how to disable.
  • in the release notes for the feature, note that after upgrading with the feature enabled, /debug/vars will start showing it and all existing admins will be able to see these new stats via SHOW STATS (this isn't strictly required as it should be understood when reading the description of the feature, but it might connect the dots for some)

If at some future point you wanted to allow non-admin users access to this information, this could be allowed if SHOW STATS (with and without for 'userquerybytes') only shows stats for the current (non-admin) user.

[1] Aside: a future queryable 'audit log' feature should be limited to admin users or users who are GRANTed access to the audit log

Copy link

@devanbenz devanbenz left a comment

Choose a reason for hiding this comment

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

Left a comment

}

// TestHandler_QueryBytesPerUser tests that query response bytes are tracked per user.
func TestHandler_QueryBytesPerUser(t *testing.T) {

Choose a reason for hiding this comment

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

Semi-related to this PR: I just checked the codebase and I don't think we have any tests to verify that only Admins can call SHOW QUERIES. I know we use the queryAuthorizer to do so but I'm not seeing tests for this.

@davidby-influx
Copy link
Contributor Author

@jdstrand - The whole statistic is omitted when INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=false, so it is not that the tag will be missing, it is that the statistic (and corresponding measurement in _internal) will not be present.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

statFluxQueryRequests = "fluxQueryReq" // Number of flux query requests served.
statFluxQueryRequestDuration = "fluxQueryReqDurationNs" // Number of (wall-time) nanoseconds spent executing Flux query requests.
statFluxQueryRequestBytesTransmitted = "fluxQueryRespBytes" // Sum of all bytes returned in Flux query responses.
statQueryRespBytes = "queryRespBytes" // Value field for per-user query response bytes.
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

statQueryRespBytes duplicates the existing statQueryRequestBytesTransmitted constant value (both are "queryRespBytes"). Having two constants with the same value but different meanings makes it easy to use the wrong one and harder to grep/maintain. Consider reusing the existing constant or renaming/refactoring so there’s a single source of truth for the field key.

Suggested change
statQueryRespBytes = "queryRespBytes" // Value field for per-user query response bytes.
statQueryRespBytes = statQueryRequestBytesTransmitted // Value field for per-user query response bytes.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed field name to userQueryRespBytes. See first comment showing _internal measurement and SHOW STATS output.

for _, stat := range stats {
if stat.Name == "userquerybytes" && stat.Tags[httpd.StatUserTagKey] == user {
if v, ok := stat.Values["queryRespBytes"]; ok {
return v.(int64), true
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

findUserStat does a direct type assertion (v.(int64)) on the statistic value. If the underlying value type changes (e.g., to uint64 or json.Number) this will panic and produce a less-informative test failure. Prefer a checked assertion (type switch / require.IsType + cast) so failures report a clear mismatch instead of panicking.

Suggested change
return v.(int64), true
switch n := v.(type) {
case int64:
return n, true
case uint64:
if n <= math.MaxInt64 {
return int64(n), true
}
}

Copilot uses AI. Check for mistakes.
}}

// Add per-user query bytes as separate statistics (one per user) if enabled
if h.Config.UserQueryBytesEnabled && !h.queryBytesPerUser.IsEmpty() {
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The IsEmpty() check adds an extra full sync.Map traversal before the real Range() traversal. Since Range() is safe on an empty map, this doubles the work on every Statistics() call once any user entries exist. Consider removing the !h.queryBytesPerUser.IsEmpty() guard and just Range() when UserQueryBytesEnabled is true (or track a separate atomic entry count if you need a cheap emptiness check).

Suggested change
if h.Config.UserQueryBytesEnabled && !h.queryBytesPerUser.IsEmpty() {
if h.Config.UserQueryBytesEnabled {

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

IsEmpty() does not require a full traversal of the sync.Map. This comment is wrong

Copy link

@devanbenz devanbenz left a comment

Choose a reason for hiding this comment

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

One more comment. Mostly looks good though.

return false
}

t.Run("enabled=true admin=true: userquerybytes visible in /debug/vars", func(t *testing.T) {

Choose a reason for hiding this comment

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

These tests are testing the /debug/vars which is good. But just checking if we should add tests for authorization on the query SHOW QUERIES too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me look at the authorization path for that.

Choose a reason for hiding this comment

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

You could probably extend the tests I wrote for #27196?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SHOW QUERIES authorization testing should probably be part of your current work. Pertaining to this PR, SHOW STATS is protected by the InfluxQL standard privilege settings. These are tested here, and are unchanged by this PR.

I added a test to verify the privileges require admin users for SHOW STATS

Copy link

@devanbenz devanbenz left a comment

Choose a reason for hiding this comment

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

LGTM

@davidby-influx davidby-influx merged commit 858a2c6 into master-1.x Feb 6, 2026
9 checks passed
@davidby-influx davidby-influx deleted the DSB/UserQueryStats branch February 6, 2026 22:35
davidby-influx added a commit that referenced this pull request Feb 6, 2026
In SHOW STATS, include per-user query response
bytes. The httpd service, returns a new statistics
object. userquerybytes.

Off by default, it can be turned on with the
environment variable
INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED
or the configuration parameter
UserQueryBytesEnabled
in the HTTP section.

This is available through SHOW STATS either
when all statistics are reported, or alone
with `FOR 'userquerybytes'`

It is returned by /debug/vars, and stored in
the _internal database when that is enabled.

(cherry picked from commit 858a2c6)

Fixes #27195
davidby-influx added a commit that referenced this pull request Feb 6, 2026
In SHOW STATS, include per-user query response
bytes. The httpd service, returns a new statistics
object. userquerybytes.

Off by default, it can be turned on with the
environment variable
INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED
or the configuration parameter
UserQueryBytesEnabled
in the HTTP section.

This is available through SHOW STATS either
when all statistics are reported, or alone
with `FOR 'userquerybytes'`

It is returned by /debug/vars, and stored in
the _internal database when that is enabled.

(cherry picked from commit 858a2c6)

Fixes #27195
@jdstrand
Copy link
Contributor

jdstrand commented Feb 9, 2026

@jdstrand - The whole statistic is omitted when INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED=false, so it is not that the tag will be missing, it is that the statistic (and corresponding measurement in _internal) will not be present.

Ok, I phrased that wrong but just wanted to ensure we had positive and negative tests.

govambam added a commit to govambam/influxdb that referenced this pull request Feb 12, 2026
In SHOW STATS, include per-user query response
bytes. The httpd service, returns a new statistics
object. userquerybytes.

Off by default, it can be turned on with the
environment variable
INFLUXDB_HTTP_USER_QUERY_BYTES_ENABLED
or the configuration parameter
UserQueryBytesEnabled
in the HTTP section.

This is available through SHOW STATS either
when all statistics are reported, or alone
with `FOR 'userquerybytes'`

It is returned by /debug/vars, and stored in
the _internal database when that is enabled.

(cherry picked from commit 858a2c6)

Fixes influxdata#27195
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants