snapshots/devmapper: deactivate thin device after committed#3262
snapshots/devmapper: deactivate thin device after committed#3262crosbymichael merged 2 commits intocontainerd:masterfrom
Conversation
|
The following error: "failed to create snapshot device from parent vg0-mythinpool-snap-3" can be reproduced by fault injection in code like this: in snapshots/devmapper/pool_device.go::CreateSnapshotDevice()
// Resume back base thin device on exit
defer func() {
//retErr = multierror.Append(retErr, p.resumeDevice(ctx, baseInfo)).ErrorOrNil()
log.G(context.TODO()).Debugf("In Resume back base thin device")
retErr = multierror.Append(retErr, errors.New("fake resumeDevice failure!")).ErrorOrNil()
}() |
|
@mxpv Hi, please help review, any comments will be appreciated :) |
|
@mxpv Hi, if you don't like the idea to deactivate committed snapshot, another idea I can think up to solve the rollback issue is like this - use one defer call, put the dev resume action first: // CreateSnapshotDevice creates and activates new thin-device from parent thin-device (makes snapshot)
func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string, snapshotName string, virtualSizeBytes uint64) (retErr error) {
var (
devSuspended = false
metaSaved = false
snapCreated = false
)
...
defer func() {
// Resume back base thin device on exit
if devSuspended {
retErr = multierror.Append(retErr, p.resumeDevice(ctx, baseInfo))
}
// Rollback snapshot creation
if snapCreated && retErr != nil {
retErr = multierror.Append(retErr, p.deleteDevice(ctx, snapInfo))
}
// Rollback metadata
if metaSaved && retErr != nil {
retErr = multierror.Append(retErr, p.metadata.RemoveDevice(ctx, snapInfo.Name))
}
}()
... |
|
In general I like the idea to deactivate a device after Some snapshotter tests are failing now, can you have a look on those? |
Thanks.
Sure:) |
|
Hmm strange, the CI failed at this case - FAIL: TestSnapshotterSuite/128LayersMount, |
68a9760 to
8045d2b
Compare
|
Hmm, the AppVeyor failure seems unrelated to this pr: |
|
@renzhengeek the AppVeyor failed case is flaky one. you can repush to make it happy... |
af4daee to
f87245b
Compare
The failure shows that the new snapshot doesn't capture all the data, which seems caused by the umount flags: UMOUNT(2) I am thinking if there is a proper way to flush the data anyway before deactivating the snapshot. |
|
I ran tests on Ubuntu 18. Everything works as expected except func parseDmsetupError(output string) string {
...
line := lines[0]
// Handle output like "Device /dev/mapper/snapshotter-suite-pool-snap-1 not found"
if strings.HasSuffix(line, "not found") {
return unix.ENXIO.Error()
}
const failedSubstr = "failed: "As for Travis, can you try it without |
f36ca76 to
e637578
Compare
|
Hi,
Done and pushed, thanks!
I'm afraid changing testutil.Unmount() may bring effects on other places, instead I add SuspendDevice before removing in Commit() to ensure the IO laid on disk. I think Commit() is a much less frequent operation than Prepare()/View() (creating snapshot), adding a suspend operation is acceptable:) Travis can pass now. Will trigger more times to see if it's stable enough. |
971d292 to
70e0c16
Compare
mxpv
left a comment
There was a problem hiding this comment.
instead I add SuspendDevice before removing in Commit() to ensure the IO laid on disk
Yep, suspend will do the trick too, great idea.
LGTM
1. reason to deactivate committed snapshot The thin device will not be used for IO after committed, and further thin snapshotting is OK using an inactive thin device as origin. The benefits to deactivate are: - device is not unneccesary visible avoiding any unexpected IO; - save useless kernel data structs for maintaining active dm. Quote from kernel doc (Documentation/device-mapper/provisioning.txt): " ii) Using an internal snapshot. Once created, the user doesn't have to worry about any connection between the origin and the snapshot. Indeed the snapshot is no different from any other thinly-provisioned device and can be snapshotted itself via the same method. It's perfectly legal to have only one of them active, and there's no ordering requirement on activating or removing them both. (This differs from conventional device-mapper snapshots.) " 2. an thinpool metadata bug is naturally removed An problem happens when failed to suspend/resume origin thin device when creating snapshot: "failed to create snapshot device from parent vg0-mythinpool-snap-3" error="failed to save initial metadata for snapshot "vg0-mythinpool-snap-19": object already exists" This issue occurs because when failed to create snapshot, the snapshotter.store can be rollbacked, but the thin pool metadata boltdb failed to rollback in PoolDevice.CreateSnapshotDevice(), therefore metadata becomes inconsistent: the snapshotID is not taken in snapshotter.store, but saved in pool metadata boltdb. The cause is, in PoolDevice.CreateSnapshotDevice(), the defer calls are invoked on "first-in-last-out" order. When the error happens on the "resume device" defer call, the metadata is saved and snapshot is created, which has no chance to be rollbacked. Signed-off-by: Eric Ren <[email protected]>
70e0c16 to
d03417d
Compare
|
Travis failed with one failure not related to this pr: Let's trigger CI again. |
d03417d to
c560040
Compare
Codecov Report
@@ Coverage Diff @@
## master #3262 +/- ##
==========================================
- Coverage 44.56% 44.53% -0.03%
==========================================
Files 113 113
Lines 12194 12195 +1
==========================================
- Hits 5434 5431 -3
- Misses 5928 5930 +2
- Partials 832 834 +2
Continue to review full report at Codecov.
|
For block device like devicemapper, umount before committing active snapshot can sync data onto disk. Otherewise: 1. deactivating a block device in use will fail or IO error when forced; 2. new snapshot on it will not catch the data not laid on disk yet. Signed-off-by: Eric Ren <[email protected]>
c560040 to
6f463d3
Compare
fuweid
left a comment
There was a problem hiding this comment.
LGTM
this is good improvement. when we run several instances from same image, it can save time.
|
LGTM |
The thin device will not be used for IO after committed,
and further thin snapshotting is OK using an inactive thin
device as origin. The benefits to deactivate are:
Quote from kernel doc (Documentation/device-mapper/provisioning.txt):
"
ii) Using an internal snapshot.
Once created, the user doesn't have to worry about any connection
between the origin and the snapshot. Indeed the snapshot is no
different from any other thinly-provisioned device and can be
snapshotted itself via the same method. It's perfectly legal to
have only one of them active, and there's no ordering requirement on
activating or removing them both. (This differs from conventional
device-mapper snapshots.)
"
An problem happens when failed to suspend/resume origin thin device
when creating snapshot:
"failed to create snapshot device from parent vg0-mythinpool-snap-3"
error="failed to save initial metadata for snapshot "vg0-mythinpool-snap-19":
object already exists"
This issue occurs because when failed to create snapshot, the
snapshotter.store can be rollbacked, but the thin pool metadata
boltdb failed to rollback in PoolDevice.CreateSnapshotDevice(),
therefore metadata becomes inconsistent: the snapshotID is not
taken in snapshotter.store, but saved in pool metadata boltdb.
The cause is, in PoolDevice.CreateSnapshotDevice(), the defer calls
are invoked on "first-in-last-out" order. When the error happens
on the "resume device" defer call, the metadata is saved and
snapshot is created, which has no chance to be rollbacked.
Signed-off-by: Eric Ren [email protected]