ClickHouse / ClickHouse/ClickHouse

Coordinated Refreshable MV in Replicated DB fails while using ZooKeeper

Open
#87,724 2 comments 2 reactions 0 assignees View on GitHub
comp-refreshable-materialized-view external unfinished code
Dominant language
C++
Stars
49.9k
Forks
9k
Avg merge
21h 32m
Merged PRs (30d)
515

Description

### Describe the unexpected behaviour

> `Code: 48. DB::Exception: Keeper server doesn't support multi-reads. (NOT_IMPLEMENTED)`

on `CREATE MATERIALIZED VIEW ... REFRESH EVERY ...` in ClickHouse **25.8.7**.

- Coordinated refreshable materialized views depend on the **MultiRead** capability.
- The ClickHouse docs explicitly state that coordination for refreshable MVs in Replicated DBs is done through **Keeper**.
- ZooKeeper supports multiRead from version 3.6, but does not report features on handshake.

### How to reproduce

```
CREATE DATABASE r ENGINE = Replicated('databases/r', '{shard}', '{replica}');
use r;
CREATE TABLE a ( `id` Int8) ENGINE = ReplicatedMergeTree ORDER BY id;
CREATE MATERIALIZED VIEW tt REFRESH EVERY 1 HOUR TO a AS
SELECT number AS id FROM numbers(10);
```

Observed: `Code: 48. DB::Exception: Keeper server doesn't support multi-reads. (NOT_IMPLEMENTED)`

### Expected behavior

**Expected behavior**

1. Coordinated Refreshable MV works with ZooKeeper **if** it supports batched read operations (multiRead introduced in ZK 3.6.0), * If `keeper_api_version = 0` (ZooKeeper), **probe** for native ZooKeeper `multiRead`; if present, use it. If absent, **fall back** to single‑read loop with a performance warning. **or**
3. ClickHouse provides a clear diagnostic that ZooKeeper is not a supported coordinator for coordinated Refreshable MVs (and that **ClickHouse Keeper** is required), **or**
3. ClickHouse falls back to a non-batched coordination path when connected to ZooKeeper, with an explicit performance warning.

### Error message and/or stacktrace

2025.09.26 19:59:58.443240 [ 802 ] {c5c2fd39-9ecb-4b5b-9fdd-b4c4e05964c5} executeQuery: Code: 48. DB::Exception: Keeper server doesn't support multi-reads. (NOT_IMPLEMENTED) (version 25.8.7.3 (officia l build)) (from 0.0.0.0:0) (in query: /* ddl_entry=query-0000000016 */ CREATE MATERIALIZED VIEW r.tt UUID '80e70afb-9240-48f0-8f5e-b54354d0acdd' REFRESH EVERY 1 HOUR TO r.a (id UInt64) DEFINER = default SQL SECURITY DEFINER AS SELECT number AS id FROM numbers(10)), Stack trace (when copying this message, always include the lines below): 0. DB::Exception::Exception(DB::Exception::MessageMasked&&, int, bool) @ 0x00000000133ab85f 1. DB::Exception::Exception(String&&, int, String, bool) @ 0x000000000c8569ce 2. DB::Exception::Exception(PreformattedMessage&&, int) @ 0x000000000c856480 3. DB::Exception::Exception<>(int, FormatStringHelperImpl<>) @ 0x000000000c86552b 4. DB::RefreshTask::create(DB::StorageMaterializedView*, std::shared_ptr, DB::ASTRefreshStrategy const&, bool, bool, bool, bool) @ 0x00000000198065b5 5. DB::StorageMaterializedView::StorageMaterializedView(DB::StorageID const&, std::shared_ptr, DB::ASTCreateQuery const&, DB::ColumnsDescription const&, DB::LoadingStrictnessLevel, String c onst&, bool) @ 0x0000000018ab0f19 6. std::shared_ptr std::__function::__policy_invoker (DB::StorageFactory::Arguments const&)>::__call_impl[abi:ne190107] (DB::StorageFactory::Arguments const&)>>(std::__function::__policy_storage const*, DB::StorageFactory::Arguments const&) (.llvm.12559941393787381952) @ 0x0000000018abf89b 7. DB::StorageFactory::get(DB::ASTCreateQuery const&, String const&, std::shared_ptr, std::shared_ptr, DB::ColumnsDescription const&, DB::ConstraintsDescription const&, DB::LoadingSt rictnessLevel, bool) const @ 0x0000000018a0417b 8. DB::InterpreterCreateQuery::doCreateTable(DB::ASTCreateQuery&, DB::InterpreterCreateQuery::TableProperties const&, std::unique_ptr>&, DB::LoadingStrictnessLe vel) @ 0x0000000017ebee95 9. DB::InterpreterCreateQuery::createTable(DB::ASTCreateQuery&) @ 0x0000000017eb36d6 10. DB::InterpreterCreateQuery::execute() @ 0x0000000017ec7778 11. DB::executeQueryImpl(char const*, char const*, std::shared_ptr, DB::QueryFlags, DB::QueryProcessingStage::Enum, std::unique_ptr>&, std::sha red_ptr&, std::shared_ptr) @ 0x0000000018351712 12. DB::executeQuery(std::unique_ptr>, DB::WriteBuffer&, bool, std::shared_ptr, std::function, DB::QueryF lags, std::optional const&, std::function const&, std::optional const&)>, std::function) @ 0x00000000183564ea 13. DB::executeQuery(DB::ReadBuffer&, DB::WriteBuffer&, bool, std::shared_ptr, std::function, DB::QueryFlags, std::optional const&, std:: function const&, std::optional const&)>, std::function) @ 0x0000000018355cf1 14. DB::DDLWorker::tryExecuteQuery(DB::DDLTaskBase&, std::shared_ptr const&, bool) @ 0x0000000017d8cce9 15. DB::DDLWorker::processTask(DB::DDLTaskBase&, std::shared_ptr const&, bool) @ 0x0000000017d8ae49 16. DB::DatabaseReplicatedDDLWorker::tryEnqueueAndExecuteEntry(DB::DDLLogEntry&, std::shared_ptr, bool) @ 0x0000000017098627 17. DB::DatabaseReplicated::tryEnqueueReplicatedDDL(std::shared_ptr const&, std::shared_ptr, DB::QueryFlags) @ 0x0000000017058a5b 18. DB::InterpreterCreateQuery::createTable(DB::ASTCreateQuery&) @ 0x0000000017eb3a27 19. DB::InterpreterCreateQuery::execute() @ 0x0000000017ec7778 20. DB::executeQueryImpl(char const*, char const*, std::shared_ptr, DB::QueryFlags, DB::QueryProcessingStage::Enum, std::unique_ptr>&, std::sha red_ptr&, std::shared_ptr) @ 0x0000000018351712 21. DB::executeQuery(String const&, std::shared_ptr, DB::QueryFlags, DB::QueryProcessingStage::Enum) @ 0x0000000018349a4b 22. DB::TCPHandler::runImpl() @ 0x0000000019a0e954 23. DB::TCPHandler::run() @ 0x0000000019a30518 24. Poco::Net::TCPServerConnection::start() @ 0x000000001ef4b1c7 25. Poco::Net::TCPServerDispatcher::run() @ 0x000000001ef4b659 26. Poco::PooledThread::run() @ 0x000000001ef11c87 27. Poco::ThreadImpl::runnableEntry(void*) @ 0x000000001ef10081 28. ? @ 0x0000000000094ac3 29. ? @ 0x0000000000126850

### Additional context

* In **Replicated** databases, Refreshable MVs coordinate via the keeper client path. The implementation checks for **Keeper feature flags** and uses batched **MultiRead**; when connected to ZooKeeper, the handshake exposes `keeper_api_version = 0` and **no feature flags**, so ClickHouse treats multi-read as unavailable and throws `NOT_IMPLEMENTED` from the RMV coordinator (`RefreshTask`).
* ZooKeeper **does implement** a native `multiRead` op starting from **3.6.0**, but ClickHouse does **not** probe or use ZooKeeper's native capability; it relies on Keeper feature negotiation, which ZooKeeper does not implement.

See Resolved ZK issue:
- Add a multiRead operation [ZOOKEEPER-3402](https://issues.apache.org/jira/browse/ZOOKEEPER-3402)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.