Skip to content

Commit 527db10

Browse files
committed
CP-309718: calculate a moving average of leaf size in GC
While attempting to reduce the size of the VM leaf VDI to a size suitable for performing the last, offline, coalesce step the Vm might write more data than the GC has been able to merge in that cycle. Add a moving average calculation to the progress tracker to allow some tolerance of this and all the GC process to proceed. Signed-off-by: Mark Syms <[email protected]>
1 parent e305c9b commit 527db10

File tree

2 files changed

+80
-39
lines changed

2 files changed

+80
-39
lines changed

libs/sm/cleanup.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,7 +2030,7 @@ def _coalesce(self, vdi):
20302030
class CoalesceTracker:
20312031
GRACE_ITERATIONS = 2
20322032
MAX_ITERATIONS_NO_PROGRESS = 3
2033-
MAX_ITERATIONS = 10
2033+
MAX_ITERATIONS = 20
20342034
MAX_INCREASE_FROM_MINIMUM = 1.2
20352035
HISTORY_STRING = "Iteration: {its} -- Initial size {initSize}" \
20362036
" --> Final size {finSize}"
@@ -2039,18 +2039,37 @@ def __init__(self, sr):
20392039
self.itsNoProgress = 0
20402040
self.its = 0
20412041
self.minSize = float("inf")
2042-
self.history = []
2042+
self._history = []
20432043
self.reason = ""
20442044
self.startSize = None
20452045
self.finishSize = None
20462046
self.sr = sr
20472047
self.grace_remaining = self.GRACE_ITERATIONS
20482048

2049+
@property
2050+
def history(self):
2051+
return [x['msg'] for x in self._history]
2052+
2053+
def moving_average(self):
2054+
"""
2055+
Calculate a three point moving average
2056+
"""
2057+
assert len(self._history) >= 3
2058+
2059+
mv_average = sum([x['finalsize'] for x in self._history]) / len(self._history)
2060+
util.SMlog(f'Calculated moving average as {mv_average}')
2061+
return mv_average
2062+
20492063
def abortCoalesce(self, prevSize, curSize):
20502064
self.its += 1
2051-
self.history.append(self.HISTORY_STRING.format(its=self.its,
2052-
initSize=prevSize,
2053-
finSize=curSize))
2065+
self._history.append(
2066+
{
2067+
'finalsize': curSize,
2068+
'msg': self.HISTORY_STRING.format(its=self.its,
2069+
initSize=prevSize,
2070+
finSize=curSize)
2071+
}
2072+
)
20542073

20552074
self.finishSize = curSize
20562075

@@ -2063,18 +2082,18 @@ def abortCoalesce(self, prevSize, curSize):
20632082
if prevSize < self.minSize:
20642083
self.minSize = prevSize
20652084

2066-
if self.its == 1:
2067-
# Skip evaluating conditions on first iteration
2085+
if self.its < 4:
2086+
# Perform at least three iterations
20682087
return False
20692088

2070-
if prevSize < curSize:
2089+
if prevSize >= curSize or curSize < self.moving_average():
2090+
# We made progress
2091+
return False
2092+
else:
20712093
self.itsNoProgress += 1
20722094
Util.log("No progress, attempt:"
20732095
" {attempt}".format(attempt=self.itsNoProgress))
20742096
util.fistpoint.activate("cleanup_tracker_no_progress", self.sr.uuid)
2075-
else:
2076-
# We made progress
2077-
return False
20782097

20792098
if self.its > self.MAX_ITERATIONS:
20802099
max = self.MAX_ITERATIONS

tests/test_cleanup.py

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ def test_coalesceLeaf_size_bigger(self, mock_log,
724724
vdi_uuid = uuid4()
725725
vdi = cleanup.VDI(sr, str(vdi_uuid), False)
726726

727-
mock_vhdSize.side_effect = iter([1024, 4096, 4096, 8000, 8000, 16000])
727+
mock_vhdSize.side_effect = iter([1024, 4096, 4096, 8000, 8500, 9000, 9500, 10000, 10500, 16000])
728728

729729
sr._snapshotCoalesce = mock.MagicMock(autospec=True)
730730
sr._snapshotCoalesce.return_value = True
@@ -1393,16 +1393,16 @@ def autopsyTracker(self, tracker, finalRes, expectedHistory,
13931393
self.trackerReportOk(tracker, expectedHistory,
13941394
expectedReason, start, finish, minimum)
13951395

1396-
def exerciseTracker(self, tracker, sizes1, sizes2, its, expectedHistory,
1396+
def exerciseTracker(self, tracker, start_sizes, end_sizes, its, expectedHistory,
13971397
expectedReason, start, finish, minimum):
13981398
for x in range(its):
1399-
size1 = sizes1.pop(0)
1400-
size2 = sizes2.pop(0)
1399+
size1 = start_sizes.pop(0)
1400+
size2 = end_sizes.pop(0)
14011401
res = tracker.abortCoalesce(size1, size2)
14021402
self.assertFalse(res)
14031403

1404-
size1 = sizes1.pop(0)
1405-
size2 = sizes2.pop(0)
1404+
size1 = start_sizes.pop(0)
1405+
size2 = end_sizes.pop(0)
14061406
res = tracker.abortCoalesce(size1, size2)
14071407
self.autopsyTracker(tracker, res, expectedHistory, expectedReason,
14081408
start, finish, minimum)
@@ -1425,6 +1425,8 @@ def test_leafCoalesceTracker(self):
14251425
"Iteration: 1 -- Initial size 100 --> Final size 100",
14261426
"Iteration: 2 -- Initial size 100 --> Final size 121",
14271427
"Iteration: 3 -- Initial size 100 --> Final size 141",
1428+
"Iteration: 4 -- Initial size 100 --> Final size 150",
1429+
"Iteration: 5 -- Initial size 100 --> Final size 160",
14281430
]
14291431
expectedReason = "Unexpected bump in size," \
14301432
" compared to minimum achieved"
@@ -1433,35 +1435,53 @@ def test_leafCoalesceTracker(self):
14331435
res = tracker.abortCoalesce(100, 121)
14341436
self.assertFalse(res)
14351437
res = tracker.abortCoalesce(100, 141)
1438+
self.assertFalse(res)
1439+
res = tracker.abortCoalesce(100, 150)
1440+
self.assertFalse(res)
1441+
res = tracker.abortCoalesce(100, 160)
14361442
self.autopsyTracker(tracker, res, expectedHistory,
1437-
expectedReason, 100, 141, 100)
1443+
expectedReason, 100, 160, 100)
14381444

14391445
def test_leafCoaleesceTracker_too_many_iterations(self):
1446+
"""
1447+
Make the GC fail after max iterations
1448+
"""
1449+
# Note this test is rather brittle if the criteria change
14401450
sr = create_cleanup_sr(self.xapi_mock)
14411451

14421452
# Test initialization
14431453
tracker = cleanup.SR.CoalesceTracker(sr)
14441454

14451455
# 10 iterations no progress 11th fails.
14461456
expectedHistory = [
1447-
"Iteration: 1 -- Initial size 20 --> Final size 19",
1448-
"Iteration: 2 -- Initial size 19 --> Final size 18",
1449-
"Iteration: 3 -- Initial size 18 --> Final size 17",
1450-
"Iteration: 4 -- Initial size 17 --> Final size 16",
1451-
"Iteration: 5 -- Initial size 16 --> Final size 15",
1452-
"Iteration: 6 -- Initial size 15 --> Final size 14",
1453-
"Iteration: 7 -- Initial size 14 --> Final size 13",
1454-
"Iteration: 8 -- Initial size 13 --> Final size 12",
1455-
"Iteration: 9 -- Initial size 12 --> Final size 11",
1456-
"Iteration: 10 -- Initial size 11 --> Final size 10",
1457-
"Iteration: 11 -- Initial size 10 --> Final size 12"
1457+
"Iteration: 1 -- Initial size 20 --> Final size 20",
1458+
"Iteration: 2 -- Initial size 20 --> Final size 20",
1459+
"Iteration: 3 -- Initial size 20 --> Final size 20",
1460+
"Iteration: 4 -- Initial size 20 --> Final size 20",
1461+
"Iteration: 5 -- Initial size 20 --> Final size 20",
1462+
"Iteration: 6 -- Initial size 20 --> Final size 20",
1463+
"Iteration: 7 -- Initial size 20 --> Final size 20",
1464+
"Iteration: 8 -- Initial size 20 --> Final size 20",
1465+
"Iteration: 9 -- Initial size 20 --> Final size 20",
1466+
"Iteration: 10 -- Initial size 20 --> Final size 20",
1467+
"Iteration: 11 -- Initial size 20 --> Final size 20",
1468+
"Iteration: 12 -- Initial size 20 --> Final size 20",
1469+
"Iteration: 13 -- Initial size 20 --> Final size 20",
1470+
"Iteration: 14 -- Initial size 20 --> Final size 20",
1471+
"Iteration: 15 -- Initial size 20 --> Final size 20",
1472+
"Iteration: 16 -- Initial size 20 --> Final size 20",
1473+
"Iteration: 17 -- Initial size 20 --> Final size 20",
1474+
"Iteration: 18 -- Initial size 20 --> Final size 21",
1475+
"Iteration: 19 -- Initial size 21 --> Final size 22",
1476+
"Iteration: 20 -- Initial size 22 --> Final size 23",
1477+
"Iteration: 21 -- Initial size 23 --> Final size 24",
14581478
]
1459-
expectedReason = "Max iterations (10) exceeded"
1479+
expectedReason = "Max iterations (20) exceeded"
14601480
self.exerciseTracker(tracker,
1461-
[20,19,18,17,16,15,14,13,12,11,10],
1462-
[19,18,17,16,15,14,13,12,11,10,12],
1463-
10, expectedHistory,
1464-
expectedReason, 20, 12, 10)
1481+
[20] * 18 + [21, 22, 23],
1482+
[20] * 17 + [21, 22, 23, 24],
1483+
20, expectedHistory,
1484+
expectedReason, 20, 24, 20)
14651485

14661486
def test_leafCoalesceTracker_getting_bigger(self):
14671487
sr = create_cleanup_sr(self.xapi_mock)
@@ -1475,14 +1495,16 @@ def test_leafCoalesceTracker_getting_bigger(self):
14751495
"Iteration: 2 -- Initial size 101 --> Final size 102",
14761496
"Iteration: 3 -- Initial size 102 --> Final size 103",
14771497
"Iteration: 4 -- Initial size 103 --> Final size 104",
1478-
"Iteration: 5 -- Initial size 104 --> Final size 105"
1498+
"Iteration: 5 -- Initial size 104 --> Final size 105",
1499+
"Iteration: 6 -- Initial size 105 --> Final size 106",
1500+
"Iteration: 7 -- Initial size 106 --> Final size 107",
14791501
]
14801502
expectedReason = "No progress made for 3 iterations"
14811503
self.exerciseTracker(tracker,
1482-
[100,101,102,103,104],
1483-
[101,102,103,104,105],
1484-
4, expectedHistory,
1485-
expectedReason, 100, 105, 100)
1504+
[100,101,102,103,104,105,106],
1505+
[101,102,103,104,105,106,107],
1506+
6, expectedHistory,
1507+
expectedReason, 100, 107, 100)
14861508

14871509
def runAbortable(self, func, ret, ns, abortTest, pollInterval, timeOut):
14881510
return func()

0 commit comments

Comments
 (0)