Skip to content

Iceberg ALTER TABLE via REST catalog fails when catalog commit fails after metadata is already written to S3 #2090

Description

@DimensionWieldr

Describe the bug
PR #1841 adds Iceberg ALTER support by writing metadata to S3 and then committing to the REST catalog.
When running 150+ ALTERs (resulting in 150+ metadata files) it eventually throws a catalog commit failed error.

To Reproduce
Basically do some sort of ALTER many many times:

ALTER TABLE datalake.`namespace.iceberg_table` RENAME COLUMN some_column TO renamed_column

It is difficult to reproduce manually, but the relevant regression test can be run at:

cd clickhouse-regression/iceberg

using 26.3.13.20001.altinityantalya PR 1841 build:

./regression.py --clickhouse https://altinity-build-artifacts.s3.amazonaws.com/PRs/1841/78d272e18d7641866ac4f4ded573708cf005a3f9/build_amd_release/clickhouse-common-static_26.3.13.20001.altinityantalya_amd64.deb --only "/iceberg/iceberg engine/rest catalog/alter support/*" -l test.log

Expected behavior
Many consecutive or parallel ALTERs (modify, add, delete, rename, etc.) should work on Iceberg tables.

Actual Behaviour

Received exception from server (version 26.3.13):
Code: 736. DB::Exception: Received from localhost:9000. DB::Exception: Iceberg alter: catalog commit failed for 's3://warehouse/data/metadata/v158-374279d8-f15e-4221-a895-9f71e342ac4f.metadata.json' after metadata file was written successfully. (DATALAKE_DATABASE_ERROR)
(query: ALTER TABLE datalake_db_8534952e_8488_11f1_9125_cb5bdb943619.`namespace_851ef23c_8488_11f1_9125_cb5bdb943619.iceberg_table_851ef278_8488_11f1_9125_cb5bdb943619` RENAME COLUMN new_column_0277ad78_8489_11f1_9125_cb5bdb943619 TO renamed_151816d4_8489_11f1_9125_cb5bdb943619)

Server error log

2026.07.21 00:15:23.412753 [ 35 ] {} <Warning> AsynchronousMetrics: Hardware monitor 'thinkpad', sensor '2' exists but could not be read: errno: 6, strerror: No such device or address.
2026.07.21 00:15:23.418920 [ 35 ] {} <Warning> AsynchronousMetrics: Hardware monitor 'thinkpad', sensor '8' exists but could not be read: errno: 6, strerror: No such device or address.
2026.07.21 00:15:23.423055 [ 35 ] {} <Warning> Application: Integrity check of the executable skipped because the reference checksum could not be read.
2026.07.21 00:15:23.439637 [ 35 ] {} <Warning> ConfigProcessor: Include not found: compression
2026.07.21 00:15:23.448474 [ 35 ] {} <Warning> Access(local_directory): File /var/lib/clickhouse/access/users.list doesn't exist
2026.07.21 00:15:23.448477 [ 35 ] {} <Warning> Access(local_directory): Recovering lists in directory /var/lib/clickhouse/access/
2026.07.21 00:15:23.450034 [ 35 ] {} <Warning> ConfigProcessor: Include not found: networks
2026.07.21 00:15:23.453191 [ 35 ] {} <Warning> Context: Delay accounting is not enabled, OSIOWaitMicroseconds will not be gathered. You can enable it using `sudo sh -c 'echo 1 > /proc/sys/kernel/task_delayacct'` or by using sysctl.
2026.07.21 00:19:33.897769 [ 44 ] {f22d42ed-de72-4caa-960e-3c878ca7fd7f} <Warning> RestCatalog(s3://bucket1/): Iceberg REST updateMetadata for namespace_851ef23c_8488_11f1_9125_cb5bdb943619.iceberg_table_851ef278_8488_11f1_9125_cb5bdb943619 failed: DB::HTTPException: Received error from remote server http://ice-rest-catalog:5000/v1/namespaces/namespace_851ef23c_8488_11f1_9125_cb5bdb943619/tables/iceberg_table_851ef278_8488_11f1_9125_cb5bdb943619. HTTP status code: 400 'Bad Request', body length: 118 bytes, body: '{"error":{"message":"Cannot set last added schema: no schema has been added","type":"ValidationException","code":400}}'
2026.07.21 00:19:33.902380 [ 44 ] {f22d42ed-de72-4caa-960e-3c878ca7fd7f} <Error> executeQuery: Code: 736. DB::Exception: Iceberg alter: catalog commit failed for 's3://warehouse/data/metadata/v158-374279d8-f15e-4221-a895-9f71e342ac4f.metadata.json' after metadata file was written successfully. (DATALAKE_DATABASE_ERROR) (version 26.3.13.20001.altinityantalya) (from 127.0.0.1:44822) (query 2, line 1) (in query: ALTER TABLE datalake_db_8534952e_8488_11f1_9125_cb5bdb943619.`namespace_851ef23c_8488_11f1_9125_cb5bdb943619.iceberg_table_851ef278_8488_11f1_9125_cb5bdb943619` RENAME COLUMN new_column_0277ad78_8489_11f1_9125_cb5bdb943619 TO renamed_151816d4_8489_11f1_9125_cb5bdb943619 ), Stack trace (when copying this message, always include the lines below):

0. DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0x000000001790c6aa
1. DB::Exception::Exception(String&&, int, String, bool) @ 0x000000000fb62b4e
2. DB::Exception::Exception(PreformattedMessage&&, int) @ 0x000000000fb624c9
3. DB::Exception::Exception<String&>(int, FormatStringHelperImpl<std::type_identity<String&>::type>, String&) @ 0x000000000fb8df36
4. DB::Iceberg::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, std::shared_ptr<DB::IObjectStorage>, DB::DataLakeStorageSettings const&, DB::Iceberg::PersistentTableComponents&, String const&, DB::StorageID, std::shared_ptr<DataLake::ICatalog>) @ 0x000000001b096b10
5. DB::IcebergMetadata::alter(DB::AlterCommands const&, std::shared_ptr<DB::StorageObjectStorageConfiguration>, std::shared_ptr<DB::Context const>, DB::StorageID const&, std::shared_ptr<DataLake::ICatalog>) @ 0x000000001afc5a94
6. DB::DataLakeConfiguration<DB::StorageS3Configuration, DB::IcebergMetadata, true>::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, DB::StorageID const&, std::shared_ptr<DataLake::ICatalog>) @ 0x000000001a02d1f3
7. DB::StorageObjectStorage::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, std::unique_lock<std::timed_mutex>&) @ 0x000000001adffcd8
8. DB::StorageObjectStorageCluster::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, std::unique_lock<std::timed_mutex>&) @ 0x000000001ae16d34
9. DB::InterpreterAlterQuery::executeToTable(DB::ASTAlterQuery const&) @ 0x000000001cbb7612
10. DB::InterpreterAlterQuery::execute() @ 0x000000001cbb31d3
11. DB::executeQueryImpl(char const*, char const*, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum, std::unique_ptr<DB::ReadBuffer, std::default_delete<DB::ReadBuffer>>&, boost::intrusive_ptr<DB::IAST>&, std::shared_ptr<DB::ImplicitTransactionControlExecutor>, std::function<void ()>, DB::QueryResultDetails&) @ 0x000000001d0ebc3e
12. DB::executeQuery(std::basic_string_view<char, std::char_traits<char>>, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum) @ 0x000000001d0e6085
13. DB::TCPHandler::runImpl() @ 0x000000001ea8c6c0
14. DB::TCPHandler::run() @ 0x000000001eab17a4
15. Poco::Net::TCPServerConnection::start() @ 0x00000000243e4e47
16. Poco::Net::TCPServerDispatcher::run() @ 0x00000000243e5366
17. Poco::PooledThread::run() @ 0x000000002439de05
18. Poco::ThreadImpl::runnableEntry(void*) @ 0x000000002439c38f
19. ? @ 0x0000000000094ac3
20. ? @ 0x0000000000126850

2026.07.21 00:19:33.902668 [ 44 ] {} <Error> TCPHandler: Code: 736. DB::Exception: Iceberg alter: catalog commit failed for 's3://warehouse/data/metadata/v158-374279d8-f15e-4221-a895-9f71e342ac4f.metadata.json' after metadata file was written successfully. (DATALAKE_DATABASE_ERROR), Stack trace (when copying this message, always include the lines below):

0. DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0x000000001790c6aa
1. DB::Exception::Exception(String&&, int, String, bool) @ 0x000000000fb62b4e
2. DB::Exception::Exception(PreformattedMessage&&, int) @ 0x000000000fb624c9
3. DB::Exception::Exception<String&>(int, FormatStringHelperImpl<std::type_identity<String&>::type>, String&) @ 0x000000000fb8df36
4. DB::Iceberg::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, std::shared_ptr<DB::IObjectStorage>, DB::DataLakeStorageSettings const&, DB::Iceberg::PersistentTableComponents&, String const&, DB::StorageID, std::shared_ptr<DataLake::ICatalog>) @ 0x000000001b096b10
5. DB::IcebergMetadata::alter(DB::AlterCommands const&, std::shared_ptr<DB::StorageObjectStorageConfiguration>, std::shared_ptr<DB::Context const>, DB::StorageID const&, std::shared_ptr<DataLake::ICatalog>) @ 0x000000001afc5a94
6. DB::DataLakeConfiguration<DB::StorageS3Configuration, DB::IcebergMetadata, true>::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, DB::StorageID const&, std::shared_ptr<DataLake::ICatalog>) @ 0x000000001a02d1f3
7. DB::StorageObjectStorage::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, std::unique_lock<std::timed_mutex>&) @ 0x000000001adffcd8
8. DB::StorageObjectStorageCluster::alter(DB::AlterCommands const&, std::shared_ptr<DB::Context const>, std::unique_lock<std::timed_mutex>&) @ 0x000000001ae16d34
9. DB::InterpreterAlterQuery::executeToTable(DB::ASTAlterQuery const&) @ 0x000000001cbb7612
10. DB::InterpreterAlterQuery::execute() @ 0x000000001cbb31d3
11. DB::executeQueryImpl(char const*, char const*, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum, std::unique_ptr<DB::ReadBuffer, std::default_delete<DB::ReadBuffer>>&, boost::intrusive_ptr<DB::IAST>&, std::shared_ptr<DB::ImplicitTransactionControlExecutor>, std::function<void ()>, DB::QueryResultDetails&) @ 0x000000001d0ebc3e
12. DB::executeQuery(std::basic_string_view<char, std::char_traits<char>>, std::shared_ptr<DB::Context>, DB::QueryFlags, DB::QueryProcessingStage::Enum) @ 0x000000001d0e6085
13. DB::TCPHandler::runImpl() @ 0x000000001ea8c6c0
14. DB::TCPHandler::run() @ 0x000000001eab17a4
15. Poco::Net::TCPServerConnection::start() @ 0x00000000243e4e47
16. Poco::Net::TCPServerDispatcher::run() @ 0x00000000243e5366
17. Poco::PooledThread::run() @ 0x000000002439de05
18. Poco::ThreadImpl::runnableEntry(void*) @ 0x000000002439c38f
19. ? @ 0x0000000000094ac3
20. ? @ 0x0000000000126850

AI Suggested Fix Directions - not sure if helpful, but leaving it here just in case

  1. Retry catalog commit on transient HTTP / conflict errors (PR Antalya 26.3: Iceberg client, handle namespaceExists error #1918 direction)
  2. Use actual catalog current-schema-id for assert-current-schema-id, not new_schema_id - 1
  3. Propagate REST catalog error into the 736 exception (or system.errors / query log) so failures are diagnosable
  4. Consider commit ordering / atomicity — catalog-first or transactional pattern to avoid orphan metadata writes

Metadata

Metadata

Assignees

Labels

antalyabugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions