snapshot_create onchange creates a snapshot on every run once the source's last write has been committed
- Dominant language
- Perl
- Stars
- 2.1k
- Forks
- 139
- PR merge metrics
- No merged PRs in 30d
Description
btrbk 0.32.7, btrfs-progs v7.1, kernel 7.1.11-zen1. The same comparison is in current `master`.
Whether `snapshot_create onchange` skips depends on whether the source subvolume's last write was still uncommitted when the snapshot was taken.
- Uncommitted at snapshot time: the snapshot forces a transaction commit, the source's dirty metadata goes into that same transaction, and `gen` and `cgen` come out equal. Later runs skip, correctly.
- Already committed: the snapshot lands in a later transaction than the source's `gen`, which stays at the transaction of its write. No later snapshot's `cgen` can equal it, so every subsequent run creates a snapshot with the source untouched.
So the feature holds on a subvolume that is being written as the snapshot is taken, and stops holding on a quiescent one.
256 MiB loopback image, fresh `mkfs.btrfs`, one subvolume, nothing else on the filesystem, no dedup and no other writer:
```sh
truncate -s 256M img && mkfs.btrfs -q img
sudo mkdir -p /mnt/repro && sudo mount -o loop img /mnt/repro
sudo btrfs subvolume create /mnt/repro/src
sudo mkdir /mnt/repro/snaps
```
```
# ./btrbk.conf
timestamp_format long-iso
snapshot_create onchange
snapshot_preserve_min all
volume /mnt/repro
snapshot_dir snaps
subvolume src
snapshot_name srcsnap
```
Invoke as `sudo btrbk -c ./btrbk.conf ...` so this config is used and not the host's.
```
A. write still uncommitted when the run happens
echo a | sudo tee /mnt/repro/src/a
after run: src.gen=9 latest.cgen=9 snapshots=1
next run: Snapshot creation skipped: snapshot_create=onchange, snapshot is up-to-date: .../srcsnap.20260830T225600+0000
B. write committed before the run
echo b | sudo tee /mnt/repro/src/b ; sudo sync -f /mnt/repro
src.gen after the committed write: 10
run 1: src.gen=10 latest.cgen=11 snapshots=2
run 2: src.gen=10 latest.cgen=12 snapshots=3
run 3: src.gen=10 latest.cgen=13 snapshots=4
next run: Snapshot creation enabled: snapshot_create=onchange, gen=10 > snapshot_cgen=13
```
The `sync -f` in B is there to establish the condition deterministically rather than to cause it; any commit of the filesystem between one run and the next puts a source into the same state, which is where a scheduled backup's sources normally sit.
The skip condition is an equality (0.32.7 line 6890, master line 6649):
```perl
if($latest->{node}{cgen} == $svol->{node}{gen}) {
```
When the source has not changed since the snapshot, the relation that holds is `source gen <= snapshot cgen`; equality holds only in case A.
Two smaller things in passing. The `>` in the DEBUG string on the other branch is literal text rather than a tested relation, so it prints an inequality that is false on its own numbers — `gen=10 > snapshot_cgen=13` above. And the same comparison determines the `up-to-date` column at master line 6422:
```perl
status => ($_->{node}{cgen} == $svol->{node}{gen}) ? "up-to-date" : "",
```
This may also be what #406 is seeing — it opens with `onchange` working on some subvolumes and not others, which is the split between the two cases above.
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce the committed-write case with the loopback btrfs image and configuration shown in the issue. Inspect the snapshot_create=onchange comparison at master line 6649 and the up-to-date status comparison at line 6422, then trace how gen and cgen are obtained. Done means an unchanged source is skipped after its write is committed, and the status output agrees with that result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, perl
- Domain
- operating-systems, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100