Skip to main content
RunBook Academy

CephLVIII · RecoveryRecovery

What recovery does and how it knows what to copy

Intermediate⏱ ~17 minceph

What you'll learn

  • Describe the recovery mechanism
  • Explain how the PG log identifies missing objects
  • Distinguish recovery from backfill
  • Predict recovery volume for a given event

Prerequisites

None — start here.

Verified against Ceph Tentacle 20.2.x · Ceph Squid 19.2.x (supported previous) · cephadm matches the verified Ceph release · podman 4.x · csi-rbd and csi-cephfs current · RBD / CephFS / RGW current (matches Ceph release) · Linux kernel 5.15+ (5.10 minimum) · Ubuntu 24.04 LTS (Ceph host baseline) · Debian 12 (Bookworm) (Ceph host baseline) · Rocky Linux / RHEL / AlmaLinux 9.x (Ceph host baseline) · Proxmox VE 9.x (cross-course integration) · Kubernetes 1.31+ (cross-course integration) · 2026-08-18

Not yet marked complete on this device.

Why this matters in production

Recovery is targeted: it copies exactly the objects that are missing and nothing else. That precision is what makes a brief OSD absence cheap, and understanding its limit explains why a long absence is not.

The mechanism

1. A PG has fewer current copies than size
2. Peering establishes which objects each peer holds and at what version
3. The primary computes the missing set from the PG logs
4. It copies those objects to the peers that lack them
5. The PG returns to active+clean
ceph pg 7.3d query | jq -r '.info.stats.stat_sum.num_objects_recovered'
ceph -s | grep recovery

How the missing set is computed

Every write is appended to the PG log with a version:

41207'8823910  MODIFY  rbd_data.1f2a3b.0000000000000c17
41207'8823911  MODIFY  rbd_data.1f2a3b.0000000000000c18
41207'8823912  DELETE  rbd_data.1f2a3b.0000000000000a02

A returning peer reports its last_update. The primary compares it against its own and the difference is exactly the set of objects to copy.

ceph pg 7.3d query | jq -r '.peer_info[] | {peer, last_update: .last_update}'

The limit

The log is bounded:

ceph config get osd osd_min_pg_log_entries      # 100
ceph config get osd osd_max_pg_log_entries      # 10000

If the peer’s position has been trimmed off the log, the delta cannot be computed and the PG falls back to backfill — a full scan and copy.

RecoveryBackfill
Determines what to copyfrom the PG logby scanning the PG
Volumeonly changed objectsthe whole PG
Triggerlog covers the gapit does not
Typical causebrief restartlong outage, new OSD, rebalance

Predicting the volume

A 30-second OSD restart: the writes that occurred in 30 seconds. Trivial.

A one-hour outage on a busy pool: an hour of writes, if the log spans it. Otherwise backfill of the whole OSD.

A new OSD: always backfill — there is no log position to compare.

# how much changed while an OSD was absent
ceph pg dump | awk '{print $1, $2}' | head
ceph -s | grep -E 'degraded|misplaced'

Quiz

Knowledge check · 4 questions

  1. Q1. How does the primary determine which objects a returning peer is missing?

  2. Q2. Recovery produces an object's current state directly rather than replaying the writes that changed it.

  3. Q3. Explain why two similar outages had very different recovery costs.

    A 20-second OSD restart last week recovered in under a minute. A 90-minute OSD outage this week triggered several hours of backfill. Both involved the same OSD on the same pool.

  4. Q4. Why does a newly-deployed OSD always backfill rather than recover?

Passing score: 75%. Answers are checked in this browser.

Production discipline

Consider raising osd_max_pg_log_entries on clusters with frequent short OSD restarts — rolling upgrades, flapping hardware — since it keeps those absences on the cheap recovery path. Weigh it against OSD memory, which the log consumes per PG.

Cross-course references

  • Kubernetes: incremental resync versus full relist follows the same bounded-history logic
  • Linux: a bounded journal determining incremental versus full recovery is a recurring design