In the previous chapter, Integration Tests, we learned how to spin up a "virtual world" of multiple ClickHouse servers using Python. We ran a basic test to see if data could move from one server to another.
But ClickHouse is more than just moving data. It has complex "Core Features" that define its reliability and security. If 1 + 1 fails, it's a bug. If Backups fail, it's a catastrophe.
This chapter is about Core Feature Tests.
Imagine you are building a bank vault.
The Challenge: ClickHouse relies on five critical pillars to function in production:
We cannot just "hope" these work. We need rigorous scenarios that simulate disasters.
Central Use Case: We want to test a Backup and Restore scenario.
These tests live in tests/integration/ alongside the others, but they focus on specific subsystems.
This feature allows ClickHouse to freeze data and copy it to a safe place (like an S3 bucket or a local disk).
"DDL" means commands like CREATE TABLE or ALTER TABLE. "Distributed" means you type the command on one server, and it magically happens on all servers in the cluster.
This manages who is allowed to do what.
This is a special component (replacing ZooKeeper) that ensures all servers agree on the truth. It prevents "Split Brain" scenarios where two servers think they are the leader.
Let's solve our Central Use Case: Testing the "Undo Button" (Backups).
To test backups, we first need to tell the virtual server where to store them. We do this by injecting a configuration snippet.
import pytest
from helpers.cluster import ClickHouseCluster
# We define a "virtual disk" called 'backups' for the test
config = """
<clickhouse>
<storage_configuration>
<disks>
<backups><type>local</type><path>/backups/</path></backups>
</disks>
</storage_configuration>
</clickhouse>
"""
# Start the cluster with this config
cluster = ClickHouseCluster(__file__)
node = cluster.add_instance('node', main_configs=['configs/backups.xml'])
Explanation: We define a local folder /backups/ as a valid place to store data. We pass this config to the instance.
We use a standard Pytest function to set up our valuable data.
@pytest.fixture(scope="module", autouse=True)
def setup_node():
cluster.start()
# Create a table and insert money
node.query("CREATE TABLE bank (id Int, money Int) ENGINE=MergeTree ORDER BY id")
node.query("INSERT INTO bank VALUES (1, 1000), (2, 2000)")
yield
cluster.shutdown()
Explanation: We have a bank table. User 1 has $1000. User 2 has $2000.
Now, inside the test function, we run the backup command.
def test_backup_and_restore():
# 1. Create a backup named 'snapshot1' on the 'backups' disk
node.query("BACKUP TABLE bank TO Disk('backups', 'snapshot1.zip')")
# Verify the backup file exists (optional check)
assert "snapshot1.zip" in node.query("SELECT name FROM system.backups")
Explanation: The BACKUP SQL command tells ClickHouse to bundle the data into a file named snapshot1.zip.
Now we simulate a developer making a terrible mistake.
# 2. DISASTER! Someone drops the table
node.query("DROP TABLE bank")
# Verify the data is gone (should raise error or be empty)
with pytest.raises(Exception):
node.query("SELECT * FROM bank")
Explanation: The table is gone. If this were a real production environment without backups, the money would be lost.
Finally, we use the restore command to bring the data back.
# 3. RESTORE from the snapshot
node.query("RESTORE TABLE bank FROM Disk('backups', 'snapshot1.zip')")
# 4. Verify the money is back
assert node.query("SELECT sum(money) FROM bank") == "3000\n"
Explanation: We pull the data from the zip file. We check the sum ($1000 + $2000 = $3000). Success!
When you run BACKUP TABLE, ClickHouse performs a complex dance to ensure data consistency without stopping the database.
Here is the flow of the test we just wrote:
Testing Security (RBAC) works similarly but focuses on permission denied errors.
def test_rbac_security():
# Create a user 'intern' who can ONLY read
node.query("CREATE USER intern IDENTIFIED BY 'password'")
node.query("GRANT SELECT ON bank TO intern")
# 1. Intern tries to read (Allowed)
node.query("SELECT * FROM bank", user="intern", password="password")
# 2. Intern tries to delete data (Should be Denied)
error = node.query_and_get_error("DROP TABLE bank", user="intern", password="password")
assert "Not enough privileges" in error
Explanation:
CREATE USER, GRANT) to set up rules.user="intern".Core Feature Tests protect the most vital parts of the system.
If these tests pass, we know the "Skeleton" of the database is strong.
In this chapter, we learned about Core Feature Tests.
ClickHouse is constantly evolving. One of the biggest recent changes is a completely new engine for understanding SQL, called the Analyzer. It needs its own special category of tests.
In the next chapter, we will explore Analyzer Tests.
Generated by Code IQ