CephLVIII · RecoveryRecovery
What recovery does and how it knows what to copy
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
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.
| Recovery | Backfill | |
|---|---|---|
| Determines what to copy | from the PG log | by scanning the PG |
| Volume | only changed objects | the whole PG |
| Trigger | log covers the gap | it does not |
| Typical cause | brief restart | long 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
Q1. How does the primary determine which objects a returning peer is missing?
Q2. Recovery produces an object's current state directly rather than replaying the writes that changed it.
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.
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