Skip to content

Conversation

@ianton-ru
Copy link

Changelog category (leave one):

  • Improvement

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

More metrics for Iceberg, S3 and Azure

Documentation entry for user-facing changes

New Iceberg profile metrics:

  • IcebergAvroFileParsing - counter of parsed avro metadata files
  • IcebergAvroFileParsingMicroseconds - time spent on parsing avro metadata files
  • IcebergJsonFileParsing - counter of parsed json metadata files
  • IcebergJsonFileParsingMicroseconds - time spent on parsing json metadata files

New S3 profile metrics:

  • S3ListObjectsMicroseconds - time spent on ListObjects requests
  • S3HeadObjectMicroseconds - time spent on HeadObject requests

New Azure profile metric:

  • AzureListObjectsMicroseconds - time spent on ListObjects requests

Small optimization - dumpMetadataObjectToString called always, including case when insertRowToLogTable dumps nothing and exits after iceberg_metadata_log_level setting check. Now serialization is only when required.

CI/CD Options

Exclude tests:

  • Fast test
  • Integration Tests
  • Stateless tests
  • Stateful tests
  • Performance tests
  • All with ASAN
  • All with TSAN
  • All with MSAN
  • All with UBSAN
  • All with Coverage
  • All with Aarch64
  • All Regression
  • Disable CI Cache

Regression jobs to run:

  • Fast suites (mostly <1h)
  • Aggregate Functions (2h)
  • Alter (1.5h)
  • Benchmark (30m)
  • ClickHouse Keeper (1h)
  • Iceberg (2h)
  • LDAP (1h)
  • Parquet (1.5h)
  • RBAC (1.5h)
  • SSL Server (1h)
  • S3 (2h)
  • Tiered Storage (2h)

@github-actions
Copy link

github-actions bot commented Nov 4, 2025

Workflow [PR], commit [b746e01]

ProfileEvents::increment(ProfileEvents::AzureListObjects);
if (client->IsClientForDisk())
ProfileEvents::increment(ProfileEvents::DiskAzureListObjects);
ProfileEventTimeIncrement<Microseconds> watch(ProfileEvents::AzureListObjectsMicroseconds);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps putting it in a scope will be more accurate and safer (e.g, future changes to this method that introduce slow operations could lead to wrong values)?

Example:

ListBlobsPagedResponse blob_list_response;

{
    ProfileEventTimeIncrement<Microseconds> watch(ProfileEvents::AzureListObjectsMicroseconds);
    blob_list_response = client->ListBlobs(options);
}

The same comment applies to the other list object operations.

In any case, I see that it is already implemented without this "protection". So it is not a must to implement this 👍

Copy link
Author

Choose a reason for hiding this comment

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

You are right, I want to get time for S3/Azure communication, not for parsing for response.

return removeEscapedSlashes(oss.str());
}

void insertRowToLogTable(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you still need the "old" overload? If so, add a comment explaining the difference between both overloads..

And btw, is dumpMetadataObjectToString that expensive that you need to optimize it in a few code paths?

Copy link
Author

Choose a reason for hiding this comment

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

In other places insertRowToLogTable called with strings from other sources like https://github.com/Altinity/ClickHouse/blob/antalya-25.8/src/Storages/ObjectStorage/DataLakes/Iceberg/AvroForIcebergDeserializer.cpp#L119.
It is also JSON string, but with some complex procedure to get.

In my test on my desktop with ~7k parquet files metadata.json has size near 5Mb.
Query select count() from iceberg.table does not read data, gets size from metadata only.
Speed changed from 3.5 seconds to 0.3 seconds.
JSON parsing is fast, but serialization back to string isn't.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok. What if you unified the interface?

void insertRowToLogTable(
...
const std::function<const std::string &()> & metadata_string_resolver)
{
...
Context::getGlobalContextInstance()->getIcebergMetadataLog()->add(
     ...
    .metadata_content = metadata_string_resolver();
}

The above would potentially allow a single method, but not sure it is a good idea. Adding a comment is enough I guess.

Copy link
Author

Choose a reason for hiding this comment

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

But getting json after condition check make sense in all case. Changed for all.

arthurpassos
arthurpassos previously approved these changes Nov 5, 2025
arthurpassos
arthurpassos previously approved these changes Nov 5, 2025
Copy link
Collaborator

@arthurpassos arthurpassos left a comment

Choose a reason for hiding this comment

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

Looking good 👍


void insertRowToLogTable(
const ContextPtr & local_context,
String row,
Copy link
Member

@Enmk Enmk Nov 7, 2025

Choose a reason for hiding this comment

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

That needs a comment that explains WHY we need NOT a value, but a function here... do I get it right that major reason is not to skew the the time measurements by getting metadata content?

Copy link
Author

Choose a reason for hiding this comment

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

To make row string only when actually required. Serialization from JSON structure to string can take a lot of time, in my test select count() from iceberg.table reduced from 3.5 seconds to 0.2 only with this optimization. Several thousands parquet files, metadata.json near 5Mb.

Copy link
Member

@Enmk Enmk left a comment

Choose a reason for hiding this comment

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

Minor changes required

Refactor insertRowToLogTable to use get_row function for lazy evaluation and improve exit logic.
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