Skip to content

Releases: simplyblock/sbcli

26.3.0.8

Choose a tag to compare

@Hamdy-khader Hamdy-khader released this 10 Sep 20:49
cb169ef

What's Changed

  • Revert "fix: preserve original source lvolID through failback cutover" (#1276) by @wmousa in #1340

Full Changelog: 26.3.0.7...26.3.0.8

26.3.0.7

Choose a tag to compare

@Hamdy-khader Hamdy-khader released this 10 Sep 11:06
8f78cdc

What's Changed

Full Changelog: 26.3.0.6...26.3.0.7

26.3.0.6

Choose a tag to compare

@schmidt-scaled schmidt-scaled released this 09 Sep 13:41
680b163

Hotfix on top of 26.3.0.5.

fix(lvol-delete): self-heal delete status 3 (async done, leadership moved) (138719a)

bdev_lvol_get_lvol_delete_status returns 3 when the async (data-plane) delete
finished but LVS leadership then moved, so the follow-up per-node sync deletes
are blocked. The data clusters are already freed; only the per-node record /
subsystem / bdev cleanup remains, and that does not depend on who holds
leadership now.

The ret == 3 branch in lvol_monitor only logged and fell through — the sole
delete status that took no action (ret == 4 and ret == -35, both also
leadership situations, recover). So after leadership left the async node,
delete_lvol_from_node hit check_non_leader_for_operation and skipped/queued
the leader-side sync delete forever: the blob + bdev survived and the record
was never removed, while retried client DELETEs short-circuited on in_deletion
and returned success in ms.

Production run n_plus_k_failover_multi_client_ha_all_nodes-20260908-174343: a
supported 2-host outage moved LVS_1's primary+secondary together, the clone's
async delete completed on the tertiary, leadership moved back to the primary
44s later, and the monitor logged "error code: 3" 2250x for one lvol, still
going 14 min after the test gave up.

Fix: ret == 3 now completes the delete leader-independently — sync deletes in
the fixed sequence (async node first, then primary → secondary → tertiary,
online-only, skipping already-done), forcing the async-node sync delete past
the leadership gate. The normal path (ret 0/2) is unchanged.

🤖 Generated with Claude Code

fix(restart): block non-leader ports before the leader, demote only once all blocked (#1327)

On a primary restart the leader path blocked the leader's LVS port first,
suspended its replication, then blocked the non-leaders — leaving a window in
which a non-leader (typically the tertiary) kept serving client IO and
redirected it through the hublvol to a leader mid-transition, producing
writer_conflict on the journal. Section 3 is reordered so non-leader ports are
blocked FIRST, then the leader's, and the demote (replication suspend →
leadership drop) starts only once every port is blocked.

26.3.0.5

Choose a tag to compare

@schmidt-scaled schmidt-scaled released this 08 Sep 19:04
db7aa87

Hotfix on top of 26.3.0.4.

fix(jm): pin JM RAID geometry so upgrades never rebuild journals under a new layout (#1322)

The per-node JM device is a RAID created with superblock=False, so its
geometry is not recorded on disk. Older releases built it as one N-way RAID1
mirror across every JM partition; commit 41dea94 ("RAID 0+1 journal layout")
changed the planner to a mirror of two RAID0 legs, which stripes each leg 4 KiB
across its drives.

On upgrade of a cluster created under the old code, the new planner rebuilt each
JM raid under RAID 0+1. Because the same on-disk bytes are then read through a
different geometry, the alceml PBA header / journal / distrib output come back
scrambled and the blobstore superblock on raid0_<lvs> fails to parse
("unsupported version on super block") — sn restart and cluster activate
fail on every LVS. No data is written (a fresh RAID1 create with both legs
present neither resyncs nor writes; the alceml opens pba_init_mode=1 and the JM
appends nothing), so it is recoverable by reproducing the original geometry.

Fix: Cluster.jm_raid_layout pins the geometry once and reproduces it forever —
fresh clusters are pinned raid01 at create; an upgrade pins it from the
pre-restart JMDevice records before restarting any node; _create_jm_stack_on_raid
builds the pinned geometry (legacy => one N-way RAID1 across all partitions).
An existing cluster can never have its journals rebuilt under a different
geometry than they were written with.

Recovery runbook for an already-broken cluster:
docs/runbooks/jm-raid-geometry-recovery.md.

Reproducible on any cluster with >= 3 devices per node (1-2 devices produce
byte-identical geometries under both layouts).

Also included since 26.3.0.4:

  • fix(nvme): detect an SPDK running without the control plane's nvme options (e6082b0)

🤖 Generated with Claude Code

26.3.0.4

Choose a tag to compare

@Hamdy-khader Hamdy-khader released this 07 Sep 17:35
833dfaf

What's Changed

Full Changelog: 26.3.0.2...26.3.0.4

26.3.0.2

Choose a tag to compare

@schmidt-scaled schmidt-scaled released this 04 Sep 22:29

Hotfix on top of 26.3.0.1.

fix(capacity): snapshot admission charges the source's utilized size, at pool AND cluster level (9c152f6)

A snapshot immediately inherits the source volume's utilized bytes, so
creating one is now blocked when

provisioned-capacity limit
  - sum of provisioned volume sizes
  - actual utilization by existing snapshots
< utilized size of the source volume
  • Pool level: pool_max_size admission now charges the snapshot at the
    source's UTILIZED size; the former extra check at the source's full
    provisioned size (which made large thin volumes un-snapshottable
    regardless of utilization) is removed.
  • Cluster level (new): the same rule against prov_cap_crit percent of
    effective cluster capacity — snapshot creation previously had no
    cluster-level admission at all.
  • Applies to all snapshot sources (user, backup policies, internal
    replication snapshots). A missing stats record falls back to the
    provisioned size; unlimited pools/clusters skip the scans.

fix: hublvol redirect invariant + lift stale SPDK port fences (e87a088)

A non-leader peer left with a single hublvol path loses its redirect outright
when the primary dies, promotes itself on the next write, and produces a writer
conflict whose fence is then never lifted. Two changes:

  • The tertiary's hublvol failover path is repaired on the repair_paths
    cadence rather than auto_fix alone (a controller holding one of its two
    paths PASSES the coarse check, so the branch was unreachable in exactly the
    state it repairs), and is no longer gated on the primary being ready — the
    path targets the SECONDARY, and the primary dying is what makes it
    load-bearing.
  • storage_node_monitor now lifts a client port that SPDK fenced and nothing
    released, after STALE_PORT_BLOCK_SEC = 25s, gated on the node being ONLINE,
    no restart owning that LVS, and the hublvol being healthy and connected.
    Unblocking a peer with no redirect would only reopen the loop.

feat(device): bounded, differential self-repair of an unavailable device (6872469)

An unavailable verdict is reached by CONSENSUS — more than half the nodes
failing to reach the device over NVMe-oF — and is then persisted, so a
transient cause left the device unavailable indefinitely with nothing trying to
bring it back. Nothing repaired it, for two independent reasons: a
consensus-unavailable device carries io_error == False and the auto-restart
branch required it, and that branch sat in an elif under
cluster.status == ACTIVE so it never ran on a healthy cluster.

device_repair() probes the stack bottom-up (one bounded RPC per layer) and
rebuilds only what is missing via the additive _def_create_device_stack()
deliberately not the destructive restart_device(). Five attempts on
[0, 10, 60, 180, 600] seconds; an attempt is counted only when there was
something local to rebuild, and reaching ONLINE by any route clears the budget.

feat(device): self-repair a removed device too, unless the operator removed it (d996595)

STATUS_REMOVED means SPDK unregistered the bdev and the control plane tore
the stack down after it — not proof the hardware is gone. It was previously a
dead end: device_monitor's loop filters to online/unavailable/readonly/
cannot-allocate, so a removed device hit continue before any repair could see
it, leaving only "Device status is not recognised, status: removed" every 10s.

Unsolicited SPDK removals now join STATUS_UNAVAILABLE in
DEVICE_REPAIRABLE_STATES, on the same bounded backoff. The one removal never
undone is the one a human asked for: CAUSE_ADMIN_REMOVE is passed explicitly
by the CLI sn remove-device and the v1/v2 API remove endpoints, recorded on
the device as admin_removed atomically before teardown, and honoured in
both device_repair_due() and device_repair() — not overridable by force.
It clears when the device next reaches ONLINE, alongside deleted.

fix(proxy): stop the stats thread before interpreter shutdown (f017b98)

The integration tier aborted with _enter_buffered_busy: could not acquire lock for <_io.BufferedWriter name='<stderr>'> after all 1213 tests had passed —
pytest exit -6, tox 250, so a green run reported as failure. print_stats() is
a daemon thread Python never joins; once logging's stream closed, each emit
raised ValueError and the handler logged that to the same dead stream and
looped. Now an atexit-set threading.Event drives the sleep, and
ValueError/OSError return instead of being logged.

26.3.0.1

Choose a tag to compare

@schmidt-scaled schmidt-scaled released this 04 Sep 17:49

Hotfix on top of 26.3.0.

fix(upgrade): heal legacy lvstore_stack_secondary/_tertiary values on load (07447ec)

Clusters created before 2026-04 carry the pre-refactor list value in these
node-record fields; 26.3.0's str annotation coerced it into the truthy string
"[]" at load time, aborting the first post-upgrade storage-node restart with
KeyError: 'StorageNode [] not found' during LVS recreation.

  • StorageNode.from_dict now normalizes legacy list / "[]" values to "" on
    every read — stored records heal on load, no migration step required (also
    repairs records already re-persisted with the poison string by a 26.3.0
    control plane).
  • The JM-connect path degrades a dangling primary reference to a warning +
    skip instead of aborting the restart.
  • Annotation audit R26.2-PRE → 26.3.0 confirmed no other field carries a
    load-semantics-changing type switch.

Upgrade path for affected clusters: update the control plane to 26.3.0.1,
then re-run the rolling storage-node restarts with the R26.3 SPDK image.

🤖 Generated with Claude Code

26.3.0

Choose a tag to compare

@schmidt-scaled schmidt-scaled released this 04 Sep 16:06
01613c8

What's Changed

Read more

26.2.12-PRE

Choose a tag to compare

@Hamdy-khader Hamdy-khader released this 03 Sep 15:15

What's Changed

  • fix(metrics): stop the v1 /cluster/metrics series leak by @wmousa in #1281
  • fix(pool): set_pool crashed on an omitted size argument by @wmousa in #1295
  • Fix/thanos query sidecar discovery by @wmousa in #1292

Full Changelog: 26.2.10-PRE...26.2.12-PRE

26.2.11-PRE

Choose a tag to compare

@mxsrc mxsrc released this 23 Aug 23:03

Add performance improvement in pool capacity reporting.

Full Changelog: 26.2.10-PRE...26.2.11-PRE