Commit 3efbbfe3 authored by Julien Muchembled's avatar Julien Muchembled

master: automatically discard feeding cells that get out-of-date

This is a follow-up of commit 2ca7c335,
which changed 'tweak' not to discard readable cells too quickly.

The scenario of a storage being lost whereas it has feeding cells was forgotten.
These must be discarded immediately, otherwise we end up with more up-to-date
cells than wanted. Without the change in outdate(), testSafeTweak would end
with: UU.|U.U|UUU

Once replication is optimized not to always restart checking cells from the
beginning:
- Remembering that an out-of-date cell was feeding could be a safer
  option, but it may not be worth the extra complexity.
- Another possibility may be to replace the FEEDING state by an automatic
  partial tweak that only discards up-to-date cells too many whenever a cell
  becomes up-to-date.
parent 3443d483
...@@ -130,7 +130,7 @@ def CellStates(): ...@@ -130,7 +130,7 @@ def CellStates():
OUT_OF_DATE OUT_OF_DATE
# Same as UP_TO_DATE, except that it will be discarded as soon as another # Same as UP_TO_DATE, except that it will be discarded as soon as another
# node finishes to replicate it. It means a partition is moved from 1 node # node finishes to replicate it. It means a partition is moved from 1 node
# to another. # to another. It is also discarded immediately if out-of-date.
FEEDING FEEDING
# Not really a state: only used in network packets to tell storages to drop # Not really a state: only used in network packets to tell storages to drop
# partitions. # partitions.
......
...@@ -384,13 +384,18 @@ class PartitionTable(neo.lib.pt.PartitionTable): ...@@ -384,13 +384,18 @@ class PartitionTable(neo.lib.pt.PartitionTable):
if cell.isReadable(): if cell.isReadable():
if cell.getNode().isRunning(): if cell.getNode().isRunning():
lost = None lost = None
else : else:
cell_list.append(cell) cell_list.append(cell)
for cell in cell_list: for cell in cell_list:
if cell.getNode() is not lost: node = cell.getNode()
cell.setState(CellStates.OUT_OF_DATE) if node is not lost:
change_list.append((offset, cell.getUUID(), if cell.isFeeding():
CellStates.OUT_OF_DATE)) self.removeCell(offset, node)
state = CellStates.DISCARDED
else:
state = CellStates.OUT_OF_DATE
cell.setState(state)
change_list.append((offset, node.getUUID(), state))
if fully_readable and change_list: if fully_readable and change_list:
logging.warning(self._first_outdated_message) logging.warning(self._first_outdated_message)
return change_list return change_list
......
...@@ -409,11 +409,19 @@ class ReplicationTests(NEOThreadedTest): ...@@ -409,11 +409,19 @@ class ReplicationTests(NEOThreadedTest):
self.tic() self.tic()
cluster.enableStorageList([s2]) cluster.enableStorageList([s2])
# 2 UP_TO_DATE cells become FEEDING: # 2 UP_TO_DATE cells become FEEDING:
# they are dropped only when the replication is done, # they are "normally" (see below) dropped only when the replication
# so that 1 storage can still die without data loss. # is done, so that 1 storage can still die without data loss.
with Patch(s0.dm, changePartitionTable=changePartitionTable): with Patch(s0.dm, changePartitionTable=changePartitionTable):
cluster.neoctl.tweakPartitionTable() cluster.neoctl.tweakPartitionTable()
self.tic() self.tic()
self.assertEqual(cluster.neoctl.getClusterState(),
ClusterStates.RUNNING)
# 1 of the FEEDING cells was actually discarded immediately when it got
# out-of-date, so that we don't end up with too many up-to-date cells.
s0.resetNode()
s0.start()
self.tic()
self.assertPartitionTable(cluster, 'UU.|U.U|.UU')
@with_cluster(start_cluster=0, partitions=3, replicas=1, storage_count=3) @with_cluster(start_cluster=0, partitions=3, replicas=1, storage_count=3)
def testReplicationAbortedBySource(self, cluster): def testReplicationAbortedBySource(self, cluster):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment