Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions changelog.d/20251209_162100_whole_object_configurable.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.. A new scriv changelog fragment.

- Make whole object diff configurable (PL-134246)
13 changes: 11 additions & 2 deletions src/backy/sources/ceph/rbd.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@

class RBDClient(object):
log: BoundLogger

def __init__(self, log: BoundLogger):
use_whole_object_diff: bool

def __init__(
self,
log: BoundLogger,
# disable by default due to performance issues when not using exclusive-lock in cluster (PL-134255)
use_whole_object_diff=False,
):
self.log = log.bind(subsystem="rbd")
self.use_whole_object_diff = use_whole_object_diff

def _ceph_cli(self, cmdline, encoding="utf-8") -> str:
# This wrapper function for the `rbd` command is only used for
Expand Down Expand Up @@ -67,6 +74,8 @@ def _rbd_stream(self, cmd: list[str]) -> Iterator[IO[bytes]]:

@functools.cached_property
def _supports_whole_object(self):
if not self.use_whole_object_diff:
return False
return "--whole-object" in self._rbd(["help", "export-diff"])

def exists(self, snapspec):
Expand Down
10 changes: 7 additions & 3 deletions src/backy/sources/ceph/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ def __init__(self, config: dict, log: BoundLogger):
self.image = config["image"]
self.always_full = config.get("full-always", False)
self.log = log.bind(subsystem="ceph")
self.rbd = RBDClient(self.log)
self.rbd = RBDClient(
self.log, config.get("use-full-object-diff", False)
)

def ready(self) -> bool:
"""Check whether the source can be backed up.
Expand Down Expand Up @@ -146,8 +148,10 @@ def verify(self, target) -> bool:
return backy.utils.files_are_roughly_equal(
source,
target,
report=lambda s, t, o: self.revision.backup.quarantine.add_report(
QuarantineReport(s, t, o)
report=(
lambda s, t, o: self.revision.backup.quarantine.add_report(
QuarantineReport(s, t, o)
)
),
)

Expand Down
33 changes: 33 additions & 0 deletions src/backy/sources/ceph/tests/test_rbd.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,36 @@ def test_rbd_export(popen, rbdclient, tmpdir):
),
]
)


RBD_HELP_WHOLE_OBJECT = """\
usage: rbd export-diff [--pool <pool>] [--namespace <namespace>]
[--image <image>] [--snap <snap>] [--path <path>]
[--from-snap <from-snap>] [--whole-object]
[--no-progress]
<source-image-or-snap-spec> <path-name>
"""


def test_rbd_whole_object_support_detection(log):
rbd_mock = mock.Mock()
rbd_mock.return_value = RBD_HELP_WHOLE_OBJECT
client1 = RBDClient(log) # assume default: `use_whole_object_diff=False`
client1._rbd = rbd_mock
assert not client1._supports_whole_object

client2 = RBDClient(log, use_whole_object_diff=False)
client2._rbd = rbd_mock
assert not client2._supports_whole_object

client3 = RBDClient(log, use_whole_object_diff=True)
client3._rbd = rbd_mock
assert client3._supports_whole_object

rbd_mock_no_whole_object = mock.Mock()
rbd_mock_no_whole_object.return_value = (
"usage: rbd export-diff [--pool <pool>] [--namespace <namespace>]"
)
client4 = RBDClient(log, use_whole_object_diff=True)
client4._rbd = rbd_mock_no_whole_object
assert not client4._supports_whole_object
Loading