Class: LVM::ThinSnapshot

Inherits:
Object
  • Object
show all
Defined in:
lib/lvm/thin_snapshot.rb

Instance Method Summary collapse

Constructor Details

#initialize(vg, lv) ⇒ ThinSnapshot

Returns a new instance of ThinSnapshot.



6
7
8
9
# File 'lib/lvm/thin_snapshot.rb', line 6

def initialize(vg, lv)
	@vg = vg
	@lv = lv
end

Instance Method Details

#differencesObject

Return an array of ranges which are the bytes which are different between the origin and the snapshot.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/lvm/thin_snapshot.rb', line 13

def differences
	# This is a relatively complicated multi-step process.  We have two
	# piles of <lv block> => <pool block> mappings, one for the "origin"
	# (the LV that's changing) and one for the "snapshot" (the LV that
	# represents some past point-in-time).  What we need to get out at the
	# end is an array of (<first byte>..<last byte>) ranges which cover
	# the parts of the volumes which are different (or that at least point
	# to different blocks within the data pool).
	#
	# This is going to take a few steps to accomplish.
	#
	# First, we translate each of the hashes into a list of two-element
	# arrays, expanding out ranges, because it means we don't have to
	# handle ranges differently in later steps (a worthwhile optimisation,
	# in my opinion -- if you think differently, I'd *really* welcome a
	# patch that handles ranges in-place without turning into a complete
	# mind-fuck, because I couldn't manage it).
	#
	# Next, we work out which mappings are "different" in all the possible
	# ways.  There's four cases we might come across:
	#
	# 1. Both origin and snapshot map the same LV block to the same data
	#    block.  This is a mapping we can discard from the set of
	#    differences, because, well, it isn't a difference.
	#
	# 2. Both origin and snapshot map the same LV block, but they point
	#    to different data blocks.  That's the easiest sort of difference
	#    to understand, and we *could* catch that just by comparing all
	#    of the mappings in the origin with the mappings in the snapshot,
	#    and listing those whose value differs.  But that wouldn't catch
	#    these next two cases...
	#
	# 3. The origin maps a particular LV block to a data block, but the
	#    snapshot doesn't have any mapping for that LV block.  This would
	#    occur quite commonly -- whenever a location in the origin LV was
	#    written to for the first time after the snapshot is taken.  You
	#    would catch all these (as well as the previous case) by taking
	#    the origin block map and removing any mappings which were
	#    identical in the snapshot block map.  However, that would fail to
	#    identify...
	#
	# 4. A block in the snapshot is mapped, when the corresponding origin
	#    block is *not* mapped.  Given the assumption that the snapshot
	#    was never written to, how could this possibly happen?  One word:
	#    "discard".  Mappings in the origin block list are removed if
	#    the block to which they refer is discarded.  Finding *these* (and also
	#    all mappings of type 2) by the reverse process to that in case
	#    3 -- simply remove from the snapshot block list all mappings which
	#    appear identically in the origin block list.
	#
	# In order to get all of 2, 3, and 4 together, we can simply do the
	# operations described in steps 3 & 4 and add the results together.  Sure,
	# we'll get two copies of all "type 2" block maps, but #uniq is good at
	# fixing that.
	#
	@differences ||= begin
		diff_maps = ((flat_origin_blocklist - flat_snapshot_blocklist) +
						 (flat_snapshot_blocklist - flat_origin_blocklist)
						).uniq

		# At this point, we're off to a good start -- we've got the mappings
		# that are different.  But we're not actually interested in the
		# mappings themselves -- all we want is "the list of LV blocks which
		# are different" (we'll translate LV blocks into byte ranges next).
		#
		changed_blocks = diff_maps.map { |m| m[0] }.uniq

		# Block-to-byte-range is pretty trivial, and we're done!
		changed_blocks.map do |b|
			((b*chunk_size)..(((b+1)*chunk_size)-1))
		end

		# There is one optimisation we could make here that we haven't --
		# coalescing adjacent byte ranges into single larger ranges.  I haven't
		# done it for two reasons: Firstly, I don't have any idea how much of a
		# real-world benefit it would be, and secondly, I couldn't work out how
		# to do it elegantly.  So I punted.
	end
end

#originObject



93
94
95
# File 'lib/lvm/thin_snapshot.rb', line 93

def origin
	@origin ||= vgcfg.logical_volumes[@lv].origin
end