mirror of
https://github.com/hanzoai/datastore.git
synced 2026-09-26 13:55:07 +00:00
PrometheusMetricsWriter emits DatastoreProfileEvents_, DatastoreMetrics_, DatastoreAsyncMetrics_, DatastoreErrorMetric_ and Datastore_Info, while four consumers still asked for the ClickHouse spelling, so each was reading a string its own server had stopped writing. The three integration tests failed outright, the prometheus endpoint one twice over, since its allowed-prefix assertion rejects every metric line before the query counter is ever looked up. The keeper stress harness was quieter and worse: an allowlist matching nothing reads as a run that produced no metrics rather than as an error. The keeper-stress-analysis skill keeps the ClickHouse spelling on purpose. It queries keeper_stress_tests.keeper_metrics_ts on play.clickhouse.com, where those names are stored data written by upstream nightlies rather than anything this server emits, and that table holds no Datastore rows at all. Renaming there would turn a query that matches into one that matches nothing. Co-authored-by: Hanzo Dev <[email protected]>
164 lines
4.9 KiB
Python
164 lines
4.9 KiB
Python
import pytest
|
|
import requests
|
|
from helpers import keeper_utils
|
|
from helpers.cluster import ClickHouseCluster
|
|
from helpers.test_tools import assert_eq_with_retry
|
|
|
|
cluster = ClickHouseCluster(__file__)
|
|
|
|
node = cluster.add_instance(
|
|
"node",
|
|
main_configs=["configs/config.xml"],
|
|
)
|
|
|
|
cluster_node1 = cluster.add_instance(
|
|
"cluster_node1",
|
|
main_configs=["configs/cluster_keeper1.xml"],
|
|
stay_alive=True,
|
|
with_zookeeper=False,
|
|
)
|
|
cluster_node2 = cluster.add_instance(
|
|
"cluster_node2",
|
|
main_configs=["configs/cluster_keeper2.xml"],
|
|
stay_alive=True,
|
|
with_zookeeper=False,
|
|
)
|
|
cluster_node3 = cluster.add_instance(
|
|
"cluster_node3",
|
|
main_configs=["configs/cluster_keeper3.xml"],
|
|
stay_alive=True,
|
|
with_zookeeper=False,
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def start_cluster():
|
|
try:
|
|
cluster.start()
|
|
yield cluster
|
|
finally:
|
|
cluster.shutdown()
|
|
|
|
|
|
def test_prometheus_keeper_metrics_only(start_cluster):
|
|
keeper_only_async_metric_exists = int(
|
|
node.query("SELECT count() > 0 FROM system.asynchronous_metrics WHERE metric = 'KeeperIsLeader'").strip()
|
|
)
|
|
assert keeper_only_async_metric_exists
|
|
|
|
prometheus_handler_response = requests.get(
|
|
f"http://{node.ip_address}:8001/metrics",
|
|
timeout=5,
|
|
)
|
|
assert prometheus_handler_response.status_code == 200
|
|
assert "DatastoreAsyncMetrics_KeeperIsStandalone" in prometheus_handler_response.text
|
|
assert "PolygonDictionaryThreads" not in prometheus_handler_response.text
|
|
|
|
|
|
def test_asynchronous_metric_log(start_cluster):
|
|
assert_eq_with_retry(
|
|
node,
|
|
"SELECT count() > 0 FROM system.asynchronous_metric_log WHERE metric = 'KeeperIsLeader'",
|
|
"1",
|
|
)
|
|
|
|
|
|
def test_skip_alias_columns(start_cluster):
|
|
node.query("SYSTEM FLUSH LOGS")
|
|
# build_id is an ALIAS column on trace_log. With skip_alias_columns=true,
|
|
# it should be absent from the table schema.
|
|
error = node.query_and_get_error("SELECT build_id FROM system.trace_log LIMIT 0")
|
|
assert "UNKNOWN_IDENTIFIER" in error
|
|
|
|
|
|
def test_keeper_cluster_invariants(start_cluster):
|
|
nodes = (cluster_node1, cluster_node2, cluster_node3)
|
|
for n in nodes:
|
|
keeper_utils.wait_until_connected(cluster, n)
|
|
|
|
invariants_query = (
|
|
"""
|
|
SELECT
|
|
count(),
|
|
uniqExact(server_id),
|
|
countIf(is_self),
|
|
countIf(is_leader),
|
|
countIf(last_log_index IS NOT NULL),
|
|
countIf(last_log_index IS NOT NULL AND is_self)
|
|
FROM system.keeper_cluster
|
|
"""
|
|
)
|
|
for keeper_node in nodes:
|
|
assert_eq_with_retry(keeper_node, invariants_query, "3\t3\t1\t1\t1\t1")
|
|
|
|
|
|
def test_system_keeper_changelogs(start_cluster):
|
|
keeper_utils.wait_until_connected(cluster, node)
|
|
|
|
# Force one entry into the active changelog so its size/last_entry_index are more deterministic.
|
|
zk = keeper_utils.get_fake_zk(cluster, "node")
|
|
try:
|
|
zk.create("/test_system_keeper_changelogs", b"data")
|
|
finally:
|
|
zk.stop()
|
|
zk.close()
|
|
|
|
assert_eq_with_retry(
|
|
node,
|
|
"SELECT count() FROM system.keeper_changelogs WHERE active",
|
|
"1",
|
|
)
|
|
|
|
row = node.query(
|
|
"SELECT from_log_index, to_log_index, last_entry_index, entries, path, disk_name, "
|
|
"size_bytes, toUnixTimestamp(modification_time), is_compressed, is_broken "
|
|
"FROM system.keeper_changelogs WHERE active LIMIT 1 FORMAT TSV"
|
|
).strip().split("\t")
|
|
|
|
(
|
|
from_log_index,
|
|
to_log_index,
|
|
last_entry_index,
|
|
entries,
|
|
path,
|
|
disk_name,
|
|
size_bytes,
|
|
modification_time,
|
|
is_compressed,
|
|
is_broken,
|
|
) = row
|
|
|
|
assert int(from_log_index) >= 1
|
|
assert int(entries) >= 1
|
|
assert int(last_entry_index) == int(from_log_index) + int(entries) - 1
|
|
assert path.startswith("changelog_") and "bin" in path
|
|
assert disk_name != ""
|
|
assert int(size_bytes) > 0
|
|
assert int(modification_time) > 0
|
|
assert is_broken == "false"
|
|
|
|
|
|
def test_system_keeper_snapshots(start_cluster):
|
|
keeper_utils.wait_until_connected(cluster, node)
|
|
response = keeper_utils.send_4lw_cmd(cluster, node, cmd="csnp")
|
|
assert response.strip().isdigit(), f"csnp did not return a log index: {response!r}"
|
|
|
|
assert_eq_with_retry(
|
|
node,
|
|
"SELECT count() >= 1 FROM system.keeper_snapshots WHERE NOT is_received",
|
|
"1",
|
|
)
|
|
|
|
row = node.query(
|
|
"SELECT last_log_index, path, disk_name, size_bytes, toUnixTimestamp(last_modified_at) "
|
|
"FROM system.keeper_snapshots "
|
|
"WHERE NOT is_received LIMIT 1 FORMAT TSV"
|
|
).strip().split("\t")
|
|
|
|
last_log_index, path, disk_name, size_bytes, last_modified_at = row
|
|
assert int(last_log_index) > 0
|
|
assert path.startswith("snapshot_") and (path.endswith(".bin") or path.endswith(".bin.zstd"))
|
|
assert disk_name
|
|
assert int(size_bytes) > 0
|
|
assert int(last_modified_at) > 0
|