Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cb0a40a
fix(node-removal): enforce full pairwise FD diversity in replica relo…
wmousa Aug 27, 2026
6e5e062
diag(node-removal): log remote_jm_devices state when jc_replace_jm ha…
wmousa Aug 27, 2026
af94ba9
fix(node-removal): prefer splice edges that leave P's other role diverse
wmousa Aug 27, 2026
1bdf79e
fix(node-removal): actively repair a splice occupant's other-role col…
wmousa Aug 28, 2026
cf1ff5f
fix(node-removal): exclude the in-flight primary from nested vacate s…
wmousa Aug 28, 2026
cfb40fb
fix(node-removal): hard-exclude a splice edge where P shares stranded…
wmousa Aug 28, 2026
5616316
fix(node-removal): solve FD diversity globally instead of one replica…
wmousa Sep 1, 2026
1a91a4e
fix(node-removal): don't tear down a replica stack the other role sti…
wmousa Sep 1, 2026
78b8cdc
fix(node-removal): release the JM back to JC before deleting its bdev
wmousa Sep 2, 2026
d91fd4a
fix(node-removal): cover the removed node's own vuid on its replica p…
wmousa Sep 2, 2026
91c9148
fix(node-removal): don't let the leftover replacement abort phase 2
wmousa Sep 2, 2026
3ec3723
fix(node-removal): treat jc_remove_jm -13 as already-released, not as…
wmousa Sep 2, 2026
cff21b0
fix(node-removal): release the JM on peers that have no vuid to replace
wmousa Sep 2, 2026
3d470ea
fix(node-removal): make jc_replace_jm and jc_remove_jm exclusive per …
wmousa Sep 2, 2026
67af167
fix(monitoring): stop polling and probing storage nodes that were rem…
wmousa Sep 2, 2026
222436f
fix(node-removal): retry without the blind vuid if jc_replace_jm reje…
wmousa Sep 2, 2026
7182c28
fix(node-removal): never make the removed primary's group a replace t…
wmousa Sep 2, 2026
c5589a1
fix(node-removal): apply the JM decommission rule per node and per group
wmousa Sep 2, 2026
65f4cd9
fix(removal): treat a node in_removal as a disconnected peer
wmousa Sep 3, 2026
89f7433
fix(removal): exclude the node being removed from phase 2's JC sweep
wmousa Sep 3, 2026
f4d961a
fix(health): stop probing devices whose owning node has departed
wmousa Sep 3, 2026
70f0e8a
fix(types): annotate the node-add in-flight sets
wmousa Sep 4, 2026
0782ef0
fix(node-removal): plan replica relocation when failure domains are off
wmousa Sep 11, 2026
a52984a
fix(node-removal): prove the relocation is planable before tearing an…
wmousa Sep 11, 2026
39e61be
fix(node-add): tear down the SPDK pod when add_node bails out after s…
wmousa Sep 11, 2026
e5a5226
fix(node-add): an unreachable node is not a dead SPDK
wmousa Sep 11, 2026
74c0608
fix(node-add): tear down the abandoned SPDK pod via the API server
wmousa Sep 11, 2026
1e4d8e8
fix(node-removal): include the stack being built in lvstore_ports
wmousa Sep 11, 2026
0a733df
fix(migration): compare online_since against an aware now
wmousa Sep 11, 2026
50060b4
fix(node-removal): explain the KeyError fallback in relocation placement
wmousa Sep 14, 2026
8d6d5b0
fix(node-removal): drop the unused RPCClient import from storage_node…
wmousa Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions simplyblock_core/controllers/health_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,20 +958,30 @@ def check_node(node_id, with_devices=True):
for remote_device in snode.remote_jm_devices:

name = remote_device.remote_bdev
bdev_info = rpc_client.get_bdevs(name)
logger.log(INFO if bdev_info else ERROR,
f"Checking bdev: {name} ... " + ('ok' if bdev_info else 'failed'))
# Owner resolved BEFORE the probe, not after. Previously the
# RPC went out unconditionally and the ERROR line was logged
# before anything knew the owner was gone -- so a removed
# node's stale entry cost one RPC per cycle and left an ERROR
# in the log that the very next line classified as expected,
# and never retracted. Live 2026-09-02: 1797 such hits on one
# removed node's JM.
try:
jm_owner = db_controller.get_storage_node_by_id(remote_device.node_id)
except KeyError:
jm_owner = None
if _peer_connections_relevant(jm_owner):
node_remote_devices_check &= bool(bdev_info)
elif not bdev_info:
owner_relevant = _peer_connections_relevant(jm_owner)
if not owner_relevant:
logger.info(
"Remote JM %s missing, but owning node %s is %s — expected, "
"not failing health", name, remote_device.node_id,
"Remote JM %s belongs to node %s (%s); not probing and not "
"failing health", name, remote_device.node_id,
jm_owner.status if jm_owner else "not-found")
connected_jms.append(remote_device.get_id())
continue

bdev_info = rpc_client.get_bdevs(name)
logger.log(INFO if bdev_info else ERROR,
f"Checking bdev: {name} ... " + ('ok' if bdev_info else 'failed'))
node_remote_devices_check &= bool(bdev_info)
connected_jms.append(remote_device.get_id())

controller_info = rpc_client.bdev_nvme_controller_list(f'remote_{remote_device.jm_bdev}')
Expand Down Expand Up @@ -1126,6 +1136,26 @@ def check_remote_device(device_id, target_node=None):
logger.exception("node not found")
return False

# The device's OWNER decides whether a remote connection to it is even
# expected. Skip the probe entirely when it is not -- same rule, and the
# same reason, as the remote-JM loop above: a missing connection to a
# departed owner is the expected consequence of its teardown.
#
# Gating only the verdict is not enough. The caller already discards the
# result for an irrelevant owner, but it calls this function first, so the
# two RPCs below still went out on every cycle for every surviving node.
# For a REMOVED node's devices that never stops: each miss makes SPDK log
# `*ERROR*: ctrlr 'remote_alceml_<uuid>' does not exist`, measured at
# 3-15 errors/min still climbing 35 minutes after the removal that made
# those devices failed_and_migrated (2026-09-03, devices 04fce724 /
# b0ada39d / ddf660f5 of the removed 2vk79, probed by 9 surviving nodes).
# Real faults then drown in a permanent error stream.
if not _peer_connections_relevant(snode):
logger.info(
"Remote device %s belongs to node %s (%s); not probing and not "
"failing health", device_id, device.node_id, snode.status)
return True

result = True
if target_node:
nodes = [target_node]
Expand Down
Loading
Loading