Skip to content

Add setting send_profile_events to reduce network traffic#80761

Closed
SpencerTorres wants to merge 2 commits intoClickHouse:masterfrom
SpencerTorres:setting_send_profile_events
Closed

Add setting send_profile_events to reduce network traffic#80761
SpencerTorres wants to merge 2 commits intoClickHouse:masterfrom
SpencerTorres:setting_send_profile_events

Conversation

@SpencerTorres
Copy link
Copy Markdown
Member

Changelog category (leave one):

  • New Feature

Changelog entry (a user-readable short description of the changes that goes to CHANGELOG.md):

Added send_profile_events setting which allows clients to reduce network traffic when profile events are unused.

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

For users who are doing many small async inserts over Native, it's possible that more data will be received from the server than sent by the client. The ProfileEvents packet was identified to be a large portion of this. To help reduce traffic, I have added a boolean setting called send_profile_events that defaults to 1. Clients can choose to disable profile events to reduce traffic. In the Go client for example, this data isn't used. It's parsed and immediately ignored. This can be applied per-query.

See sample Go client packet log using this new setting:

Packet log with ProfileEvents disabled

Settings: map[string]any{
	"send_profile_events": 0,
},
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [handshake] ->  0.0.0
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [handshake] <- ClickHouse (xxx) server version 25.6.1 revision 54477 (timezone America/New_York)
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [send query] compression="none" INSERT INTO function null('x UInt64') FORMAT Native
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [send data] compression="none"
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=1, rows=0
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [send data] compression="none"
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [end of stream]
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [released] connection [1]
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [send query] compression="none" SELECT 1
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [send data] compression="none"
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=1, rows=0
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=1, rows=1
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [profile info] rows=1, bytes=4104, blocks=1, rows before limit=0, applied limit=false, calculated rows before limit=true
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [progress] rows=1, bytes=1, total rows=1, wrote rows=0 wrote bytes=0 elapsed=487.041µs
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [progress] rows=1, bytes=1, total rows=1, wrote rows=0 wrote bytes=0 elapsed=487.041µs
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=0, rows=0
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [progress] rows=0, bytes=0, total rows=0, wrote rows=0 wrote bytes=0 elapsed=7.224µs
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [progress] rows=0, bytes=0, total rows=0, wrote rows=0 wrote bytes=0 elapsed=7.224µs
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [end of stream]
2025/05/23 19:00:28 [clickhouse][conn=1][[::1]:9000] [released] connection [1]

Packet log with ProfileEvents enabled (default, current behavior)

Settings: map[string]any{
	"send_profile_events": 1,
},
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [handshake] ->  0.0.0
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [handshake] <- ClickHouse (xxx) server version 25.6.1 revision 54477 (timezone America/New_York)
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [send query] compression="none" INSERT INTO function null('x UInt64') FORMAT Native
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [send data] compression="none"
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=1, rows=0
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [send data] compression="none"
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=6, rows=18
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [profile events] rows=18
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [end of stream]
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [released] connection [1]
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [send query] compression="none" SELECT 1
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [send data] compression="none"
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=1, rows=0
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=1, rows=1
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [profile info] rows=1, bytes=4104, blocks=1, rows before limit=0, applied limit=false, calculated rows before limit=true
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [progress] rows=1, bytes=1, total rows=1, wrote rows=0 wrote bytes=0 elapsed=485.818µs
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [progress] rows=1, bytes=1, total rows=1, wrote rows=0 wrote bytes=0 elapsed=485.818µs
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=6, rows=23
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [profile events] rows=23
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [read data] compression="none". block: columns=0, rows=0
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [progress] rows=0, bytes=0, total rows=0, wrote rows=0 wrote bytes=0 elapsed=25.218µs
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [progress] rows=0, bytes=0, total rows=0, wrote rows=0 wrote bytes=0 elapsed=25.218µs
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [end of stream]
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [released] connection [1]

Note the difference, these lines are excluded since the server never sent them:

2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [profile events] rows=18
2025/05/23 19:01:18 [clickhouse][conn=1][[::1]:9000] [profile events] rows=23

This change does not affect the ProfileInfo packet. Setting docs should be auto-generated, let me know if I need to manually add anything. Thanks!

@clickhouse-gh
Copy link
Copy Markdown
Contributor

clickhouse-gh bot commented May 23, 2025

Workflow [PR], commit [c001cdb]

@clickhouse-gh clickhouse-gh bot added the pr-feature Pull request with new product feature label May 23, 2025
@azat azat self-assigned this May 24, 2025

void TCPHandler::sendProfileEvents(QueryState & state)
{
if (!state.query_context->getSettingsRef()[Setting::send_profile_events])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I was worrying about this for a long time - #28364 (comment)

We cannot simply just not send them now, since some clients relies on that they are there, i.e. python driver here - https://github.com/mymarilyn/clickhouse-driver/blob/8a4e7c5b99b532df2b015651d893a6f36288a22c/clickhouse_driver/client.py#L725-L757

And using a setting for this does not looks correct because such settings will not work if they are specified in the query directly (i.e. select 1 settings) only in protocol, and we need to clarify this to users. And in general setting that controls the protocol looks odd.

So I guess we need to add one more revision, and let client decide does it need profile events or not (in hello addendum), and only those who can handle this packets will set this flag.

@alexey-milovidov I remember you wanted to avoid the complexity, but there is already a timer for how long does it take to send them... - #62752

@novikd @alexey-milovidov @Algunenano what do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

My concern is that the new setting will most likely be disabled in the Cloud and non-modifiable, because ProfileEvents may be used for accounting, and the fact that they are send between servers in the distributed query execution is also important.

Also, I've recently added the compression of ProfileEvents, which makes this feature less relevant.

PS.

We cannot simply just not send them now, since some clients relies on that they are there

It is a mistake in the Python client if it relies on this specific order of packets. However, it is unlikely to be a problem - for it to happen, the user will have to explicitly disable the profile events.

because such settings will not work if they are specified in the query directly (i.e. select 1 settings) only in protocol

We should make it in a way that the server stops sending ProfileEvents as soon as it applies this setting for a query.

@alexey-milovidov
Copy link
Copy Markdown
Member

@azat @SpencerTorres Let's ensure that ProfileEvents are not sent more frequently than interactive_delay (similarly to the progress packets). If the server receives ProfileEvents from many other servers, it can aggregate them for the interactive_delay period before sending to the client. This should solve the problem with no extra settings.

@azat
Copy link
Copy Markdown
Member

azat commented May 25, 2025

Let's ensure that ProfileEvents are not sent more frequently than interactive_delay (similarly to the progress packets). If the server receives ProfileEvents from many other servers, it can aggregate them for the interactive_delay period before sending to the client. This should solve the problem with no extra settings.

This change will break the mentioned driver, but perhaps that's acceptable.

Regarding your suggestion - are you proposing not to send ProfileEvents at all if interactive_delay hasn’t elapsed? Or to send the first one immediately and then delay subsequent ones until the interactive_delay interval passes?

The second option might still cause issues. I'm not entirely sure what was causing the original problem - it could be related to the waiting phase during async INSERTs, where ProfileEvents might be received, if so, then this should work.

@alexey-milovidov
Copy link
Copy Markdown
Member

Or to send the first one immediately and then delay subsequent ones until the interactive_delay interval passes?

Yes, the first and the last one can always be sent, and everything in between not frequently than the interactive_delay.

@nikitamikhaylov
Copy link
Copy Markdown
Member

Maybe we can change the protocol itself making all the clients to explicitly say whether they want profile events back or not? We can put that in the receiveAddendum function in the TCPHander.

@SpencerTorres
Copy link
Copy Markdown
Member Author

Maybe we can change the protocol itself making all the clients to explicitly say whether they want profile events back or not? We can put that in the receiveAddendum function in the TCPHander.

This would be useful; @azat suggested the same. If we're going to make a new revision, are there other packets we should conditionally exclude? We could do a boolean to toggle ProfileEvents, or a 1 byte bit set to determine which optional packets to receive (this would be more future-proof and save space if we ever wanted to disable other packets in the future).

I see what @alexey-milovidov said about the interactive_delay, but in this case the client doesn't use these packets at all and it would be better if they were never sent. If there's an issue with how frequently ProfileEvents are sent then we can put that in a separate issue.

@alexey-milovidov How do you feel about the suggested revision? Is it reasonable for clients to decide not to receive these packets? By disabling them we save some CPU/network resources on both client and server.

@azat
Copy link
Copy Markdown
Member

azat commented May 28, 2025

If we're going to make a new revision, are there other packets we should conditionally exclude?

AFAIR - no (logs are already under a setting)

We could do a boolean to toggle ProfileEvents, or a 1 byte bit set to determine which optional packets to receive

I like the idea of using bit mask, but, we still will have to bump the revision I guess, and I don't expect too much changes here, so maybe the complexity does not worth it

@andreev-io
Copy link
Copy Markdown
Member

Loosely related to #75719.

@alexey-milovidov
Copy link
Copy Markdown
Member

Related to #63065

@alexey-milovidov
Copy link
Copy Markdown
Member

Yes, the first and the last one can always be sent, and everything in between not frequently than the interactive_delay.

Ok, this is not applicable because metrics from different servers have different hostname fields, so they cannot be squashed without losing this info.

@alexey-milovidov
Copy link
Copy Markdown
Member

I think this solution is good enough, and it is flexible enough.
It's not a problem if send_profile_events cannot be disabled entirely in the SETTINGS clause.
It's not a problem if some clients break if sending profile events is disabled on the profile level.

@clickhouse-gh
Copy link
Copy Markdown
Contributor

clickhouse-gh bot commented Jun 24, 2025

Workflow [PR], commit [358c3a8]

Summary:
15 failures out of 109 shown:

job_name test_name status info comment
Docs check dropped
Fast test failure
02995_new_settings_history FAIL
Build (amd_debug) dropped
Build (amd_release) dropped
Build (amd_asan) dropped
Build (amd_tsan) dropped
Build (amd_msan) dropped
Build (amd_ubsan) dropped
Build (amd_binary) dropped
Build (arm_release) dropped
Build (arm_asan) dropped
Build (arm_coverage) dropped
Build (arm_binary) dropped
Build (amd_darwin) dropped
Build (arm_darwin) dropped

Copy link
Copy Markdown
Member

@alexey-milovidov alexey-milovidov left a comment

Choose a reason for hiding this comment

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

And everything looks good.

@alexey-milovidov alexey-milovidov self-assigned this Jun 24, 2025
@SpencerTorres
Copy link
Copy Markdown
Member Author

SpencerTorres commented Jun 24, 2025

No need to rush this change, we can go with the other solution of adding a revision with a boolean in the client Hello. I updated my local code to do this, but I need to update the related tests/docs. I can close this PR and submit the other when its ready.

I want to make sure we enable this ability the proper way, if a setting isn't the right approach then let's wait to add it as a revision to the Hello packet. Let me know what the preferred approach is

@alexey-milovidov
Copy link
Copy Markdown
Member

Ok.

@clickhouse-gh
Copy link
Copy Markdown
Contributor

clickhouse-gh bot commented Jul 29, 2025

Dear @azat, @alexey-milovidov, this PR hasn't been updated for a while. You will be unassigned. Will you continue working on it? If so, please feel free to reassign yourself.

@azat azat self-assigned this Sep 17, 2025
@clickhouse-gh
Copy link
Copy Markdown
Contributor

clickhouse-gh bot commented Oct 21, 2025

Dear @azat, this PR hasn't been updated for a while. You will be unassigned. Will you continue working on it? If so, please feel free to reassign yourself.

@erezrokah
Copy link
Copy Markdown

Related to ClickHouse/clickhouse-go#1685 and ClickHouse/clickhouse-go#1686. Skipping the consumption of profile events on the client side can reduce memory allocations by over 3x.

It would be great to be able to disable these at the session level. This is especially important for async inserts where profile events take up a bigger chunk of the data that is streamed from the server.

@alexey-milovidov
Copy link
Copy Markdown
Member

See #89588.

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

Labels

pr-feature Pull request with new product feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants