Commit d3cb8888 authored by Julien Muchembled's avatar Julien Muchembled

qa: rewrite testReplicationBlockedByUnfinished as a threaded test

It is extended to check that the storage is only notified about the
transactions that existed at the time it asked for them. Otherwise,
Replicator.transactionFinished would be called more than once, and
`self.ttid_set.remove(ttid)` would raise KeyError.

The functional version also contained an annoying 'sleep(10)'.
parent 5b66a6a7
...@@ -54,8 +54,8 @@ class StorageServiceHandler(BaseServiceHandler): ...@@ -54,8 +54,8 @@ class StorageServiceHandler(BaseServiceHandler):
pending_list = () pending_list = ()
else: else:
# This can't be app.tm.getLastTID() for imported transactions, # This can't be app.tm.getLastTID() for imported transactions,
# because outdated cells must at least wait that they're locked. # because outdated cells must at least wait that they're locked
# For normal transactions, it would not matter. # at source side. For normal transactions, it would not matter.
last_tid = app.getLastTransaction() last_tid = app.getLastTransaction()
pending_list = app.tm.registerForNotification(conn.getUUID()) pending_list = app.tm.registerForNotification(conn.getUUID())
p = Packets.AnswerUnfinishedTransactions(last_tid, pending_list) p = Packets.AnswerUnfinishedTransactions(last_tid, pending_list)
......
...@@ -14,14 +14,12 @@ ...@@ -14,14 +14,12 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import time
import unittest import unittest
import transaction import transaction
from persistent import Persistent from persistent import Persistent
from . import NEOCluster, NEOFunctionalTest from . import NEOCluster, NEOFunctionalTest
from neo.lib.protocol import ClusterStates, NodeStates from neo.lib.protocol import ClusterStates, NodeStates
from ZODB.tests.StorageTestBase import zodb_pickle
class PObject(Persistent): class PObject(Persistent):
...@@ -421,47 +419,5 @@ class StorageTests(NEOFunctionalTest): ...@@ -421,47 +419,5 @@ class StorageTests(NEOFunctionalTest):
self.neo.expectClusterRunning() self.neo.expectClusterRunning()
self.neo.expectOudatedCells(number=0) self.neo.expectOudatedCells(number=0)
def testReplicationBlockedByUnfinished(self):
# start a cluster with 1 of 2 storages and a replica
(started, stopped) = self.__setup(storage_number=2, replicas=1,
pending_number=1, partitions=10)
self.neo.expectRunning(started[0])
self.neo.expectStorageNotKnown(stopped[0])
self.neo.expectOudatedCells(number=0)
self.neo.expectClusterRunning()
self.__populate()
self.neo.expectOudatedCells(number=0)
# start a transaction that will block the end of the replication
db, conn = self.neo.getZODBConnection()
st = conn._storage
t = transaction.Transaction()
t.user = 'user'
t.description = 'desc'
oid = st.new_oid()
rev = '\0' * 8
data = zodb_pickle(PObject(42))
st.tpc_begin(t)
st.store(oid, rev, data, '', t)
# start the outdated storage
stopped[0].start()
self.neo.expectPending(stopped[0])
self.neo.neoctl.enableStorageList([stopped[0].getUUID()])
self.neo.neoctl.tweakPartitionTable()
self.neo.expectRunning(stopped[0])
self.neo.expectClusterRunning()
self.neo.expectAssignedCells(started[0], 10)
self.neo.expectAssignedCells(stopped[0], 10)
# wait a bit, replication must not happen. This hack is required
# because we cannot gather informations directly from the storages
time.sleep(10)
self.neo.expectOudatedCells(number=10)
# finish the transaction, the replication must happen and finish
st.tpc_vote(t)
st.tpc_finish(t)
self.neo.expectOudatedCells(number=0, timeout=10)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -494,6 +494,40 @@ class ReplicationTests(NEOThreadedTest): ...@@ -494,6 +494,40 @@ class ReplicationTests(NEOThreadedTest):
finally: finally:
cluster.stop() cluster.stop()
def testReplicationBlockedByUnfinished(self):
cluster = NEOCluster(replicas=1)
try:
s0, s1 = cluster.storage_list
cluster.start(storage_list=(s0,))
storage = cluster.getZODBStorage()
oid = storage.new_oid()
tid = None
for n in 1, 0:
# On first iteration, the transaction will block replication
# until tpc_finish.
# We do a second iteration as a quick check that the cluster
# remains functional after such a scenario.
txn = transaction.Transaction()
storage.tpc_begin(txn)
tid = storage.store(oid, tid, 'foo', '', txn)
if n:
# Start the outdated storage.
s1.start()
self.tic()
cluster.enableStorageList((s1,))
cluster.neoctl.tweakPartitionTable()
self.tic()
self.assertEqual(n, len(cluster.getOutdatedCells()))
storage.tpc_vote(txn)
self.assertEqual(n, len(cluster.getOutdatedCells()))
tid = storage.tpc_finish(txn)
self.tic() # replication resumes and ends
self.assertFalse(cluster.getOutdatedCells())
self.assertEqual(cluster.neoctl.getClusterState(),
ClusterStates.RUNNING)
finally:
cluster.stop()
def testCheckReplicas(self): def testCheckReplicas(self):
from neo.storage import checker from neo.storage import checker
def corrupt(offset): def corrupt(offset):
......
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