Skip to content

Commit 21217da

Browse files
add tests for host_id consistency
1 parent 5fd2c34 commit 21217da

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

system_keyspaces_test.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import pytest
22
import logging
3+
import re
4+
import time
35

46
from cassandra import Unauthorized
57
from dtest import Tester
@@ -12,6 +14,121 @@
1214

1315
class TestSystemKeyspaces(Tester):
1416

17+
@since('3.0')
18+
def test_host_id_is_set(self):
19+
"""
20+
@jira_ticket CASSANDRA-18153
21+
22+
Test that the host ID in system.local is consistent across restarts.
23+
"""
24+
cluster = self.cluster
25+
cluster.populate(1).start()
26+
node = cluster.nodelist()[0]
27+
node.nodetool("disableautocompaction")
28+
29+
session = self.patient_cql_connection(node)
30+
host_id_in_system_local = str(session.execute("SELECT host_id FROM system.local")[0].host_id)
31+
32+
session.execute("CREATE KEYSPACE ks WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}")
33+
session.execute("CREATE TABLE ks.cf (k int PRIMARY KEY, v int)")
34+
node.nodetool("disableautocompaction ks cf")
35+
36+
session.execute("INSERT INTO ks.cf (k, v) VALUES (0, 0)")
37+
node.flush()
38+
39+
self.assert_host_id_in_all_sstables(host_id_in_system_local, node)
40+
41+
# let's have something in the commitlog which was not flushed to sstable
42+
session.execute("INSERT INTO ks.cf (k, v) VALUES (1, 1)")
43+
cluster.stop(gently=False)
44+
45+
cluster.start(wait_for_binary_proto=True)
46+
session = self.patient_cql_connection(node)
47+
assert host_id_in_system_local == str(session.execute("SELECT host_id FROM system.local")[0].host_id), "Host ID in system.local changed after restart"
48+
49+
node.flush()
50+
self.assert_host_id_in_all_sstables(host_id_in_system_local, node)
51+
52+
@since('3.0')
53+
def test_consistent_host_id(self):
54+
"""
55+
@jira_ticket CASSANDRA-18153
56+
57+
Test that the host ID in system.local is consistent across restarts.
58+
"""
59+
cluster = self.cluster
60+
cluster.set_configuration_options(values={'commitlog_sync_period_in_ms': 500})
61+
cluster.populate(1).start()
62+
node = cluster.nodelist()[0]
63+
64+
session = self.patient_cql_connection(node)
65+
host_id_in_system_local = str(session.execute("SELECT host_id FROM system.local")[0].host_id)
66+
logger.info("Host ID in system.local: {}".format(host_id_in_system_local))
67+
68+
session.execute("CREATE KEYSPACE ks WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}")
69+
session.execute("CREATE TABLE ks.cf (k int PRIMARY KEY, v int)")
70+
71+
session.execute("INSERT INTO ks.cf (k, v) VALUES (0, 0)")
72+
node.flush()
73+
74+
# let's do something nasty - system.local is flushed without host ID
75+
session.execute("UPDATE system.local SET host_id = NULL WHERE key = 'local'")
76+
node.flush()
77+
node.compact()
78+
79+
# let's generate some random host ID and leave it only in the commitlog
80+
random_host_id = "12345678-1234-1234-1234-123456789012"
81+
session.execute("UPDATE system.local SET host_id = {} WHERE key = 'local'".format(random_host_id))
82+
# wait for the commitlog to be synced
83+
time.sleep(1)
84+
85+
# restart in a bad way
86+
cluster.stop(gently=False)
87+
cluster.start(wait_for_binary_proto=True)
88+
session = self.patient_cql_connection(node)
89+
90+
host_id_in_system_local_after_restart = str(session.execute("SELECT host_id FROM system.local")[0].host_id)
91+
logger.info("Host ID in system.local after restart: {}".format(host_id_in_system_local_after_restart))
92+
# now we expect that although system.local has no host ID, it wasn't generated at startup because there was something in the commitlog
93+
# eventually, we should read that new host ID from the commitlog
94+
assert host_id_in_system_local_after_restart == random_host_id, "Host ID in system.local changed after restart: {} != {}".format(host_id_in_system_local_after_restart, random_host_id)
95+
96+
def get_host_id_from_sstable_metadata(self, node, sstable_paths):
97+
host_ids = {}
98+
for sstable_path in sstable_paths:
99+
(out, err, rc) = node.run_sstablemetadata(datafiles=[sstable_path])
100+
assert rc == 0, "sstablemetadata failed with error: {}".format(err)
101+
# extract host id from out using "Originating host id: (\\S*)" regex
102+
host_id = re.search("Originating host id: (\\S*)", out)
103+
if host_id is None:
104+
logger.info("Could not find host ID in sstablemetadata output: {}".format(out))
105+
else:
106+
if sstable_path in host_ids:
107+
logger.info("Duplicate sstable path: {}".format(sstable_path))
108+
host_ids[sstable_path] = host_id.group(1)
109+
110+
return host_ids
111+
112+
def assert_host_id_in_all_sstables(self, expected_host_id, node):
113+
keyspaces = node.list_keyspaces()
114+
# we need to explicitly add local system keyspace to the list
115+
keyspaces.append("system")
116+
117+
sstable_paths = []
118+
for keyspace in keyspaces:
119+
sstable_paths.extend(node.get_sstables(keyspace, ""))
120+
121+
host_ids = self.get_host_id_from_sstable_metadata(node, sstable_paths)
122+
assert len(host_ids) == len(sstable_paths), "Expected to find host ID in all sstables: {} != {}".format(
123+
len(host_ids), len(sstable_paths))
124+
125+
# assert that host IDs in system.local and sstables are the same
126+
for sstable_path in sstable_paths:
127+
assert expected_host_id == host_ids[sstable_path], \
128+
"Host ID in system.local and sstable {} are different: {} != {}".format(sstable_path,
129+
expected_host_id,
130+
host_ids[sstable_path])
131+
15132
@since('3.0')
16133
def test_local_system_keyspaces(self):
17134
cluster = self.cluster

0 commit comments

Comments
 (0)