RetryPolicy tests, related fixes
This commit is contained in:
@@ -83,15 +83,15 @@ class ResponseFuture(object):
|
|||||||
if isinstance(response, ReadTimeoutErrorMessage):
|
if isinstance(response, ReadTimeoutErrorMessage):
|
||||||
details = response.recv_error_info()
|
details = response.recv_error_info()
|
||||||
retry = retry_policy.on_read_timeout(
|
retry = retry_policy.on_read_timeout(
|
||||||
self.query, attempt_num=self._query_retries, **details)
|
self.query, retry_num=self._query_retries, **details)
|
||||||
elif isinstance(response, WriteTimeoutErrorMessage):
|
elif isinstance(response, WriteTimeoutErrorMessage):
|
||||||
details = response.recv_error_info()
|
details = response.recv_error_info()
|
||||||
retry = retry_policy.on_write_timeout(
|
retry = retry_policy.on_write_timeout(
|
||||||
self.query, attempt_num=self._query_retries, **details)
|
self.query, retry_num=self._query_retries, **details)
|
||||||
elif isinstance(response, UnavailableExceptionErrorMessage):
|
elif isinstance(response, UnavailableExceptionErrorMessage):
|
||||||
details = response.recv_error_info()
|
details = response.recv_error_info()
|
||||||
retry = retry_policy.on_write_timeout(
|
retry = retry_policy.on_write_timeout(
|
||||||
self.query, attempt_num=self._query_retries, **details)
|
self.query, retry_num=self._query_retries, **details)
|
||||||
elif isinstance(response, OverloadedErrorMessage):
|
elif isinstance(response, OverloadedErrorMessage):
|
||||||
# need to retry against a different host here
|
# need to retry against a different host here
|
||||||
self._retry(False, None)
|
self._retry(False, None)
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ class RetryPolicy(object):
|
|||||||
IGNORE = 2
|
IGNORE = 2
|
||||||
|
|
||||||
def on_read_timeout(self, query, consistency, required_responses,
|
def on_read_timeout(self, query, consistency, required_responses,
|
||||||
received_responses, data_retrieved, attempt_num):
|
received_responses, data_retrieved, retry_num):
|
||||||
if attempt_num != 0:
|
if retry_num != 0:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
elif received_responses >= required_responses and not data_retrieved:
|
elif received_responses >= required_responses and not data_retrieved:
|
||||||
return (self.RETRY, consistency)
|
return (self.RETRY, consistency)
|
||||||
@@ -186,15 +186,15 @@ class RetryPolicy(object):
|
|||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
def on_write_timeout(self, query, consistency, write_type,
|
def on_write_timeout(self, query, consistency, write_type,
|
||||||
required_responses, received_responses, attempt_num):
|
required_responses, received_responses, retry_num):
|
||||||
if attempt_num != 0:
|
if retry_num != 0:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
elif write_type == WriteType.BATCH_LOG:
|
elif write_type == WriteType.BATCH_LOG:
|
||||||
return (self.RETRY, consistency)
|
return (self.RETRY, consistency)
|
||||||
else:
|
else:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
def on_unavailable(self, query, consistency, required_replicas, alive_replicas, attempt_num):
|
def on_unavailable(self, query, consistency, required_replicas, alive_replicas, retry_num):
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
|
|
||||||
@@ -210,21 +210,21 @@ class FallthroughRetryPolicy(RetryPolicy):
|
|||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
|
|
||||||
class FallthroughRetryPolicy(RetryPolicy):
|
class DowngradingConsistencyRetryPolicy(RetryPolicy):
|
||||||
|
|
||||||
def _pick_consistency(self, num_responses):
|
def _pick_consistency(self, num_responses):
|
||||||
if num_responses >= 3:
|
if num_responses >= 3:
|
||||||
return (self.RETRY, ConsistencyLevel.name_to_value["THREE"])
|
return (self.RETRY, ConsistencyLevel.THREE)
|
||||||
elif num_responses >= 2:
|
elif num_responses >= 2:
|
||||||
return (self.RETRY, ConsistencyLevel.name_to_value["TWO"])
|
return (self.RETRY, ConsistencyLevel.TWO)
|
||||||
elif num_responses >= 1:
|
elif num_responses >= 1:
|
||||||
return (self.RETRY, ConsistencyLevel.name_to_value["ONE"])
|
return (self.RETRY, ConsistencyLevel.ONE)
|
||||||
else:
|
else:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
def on_read_timeout(self, query, consistency, required_responses,
|
def on_read_timeout(self, query, consistency, required_responses,
|
||||||
received_responses, data_retrieved, attempt_num):
|
received_responses, data_retrieved, retry_num):
|
||||||
if attempt_num != 0:
|
if retry_num != 0:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
elif received_responses < required_responses:
|
elif received_responses < required_responses:
|
||||||
return self._pick_consistency(received_responses)
|
return self._pick_consistency(received_responses)
|
||||||
@@ -234,11 +234,11 @@ class FallthroughRetryPolicy(RetryPolicy):
|
|||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
def on_write_timeout(self, query, consistency, write_type,
|
def on_write_timeout(self, query, consistency, write_type,
|
||||||
required_responses, received_responses, attempt_num):
|
required_responses, received_responses, retry_num):
|
||||||
if attempt_num != 0:
|
if retry_num != 0:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
elif write_type in (WriteType.SIMPLE, WriteType.BATCH, WriteType.COUNTER):
|
elif write_type in (WriteType.SIMPLE, WriteType.BATCH, WriteType.COUNTER):
|
||||||
return (self.IGNORED, None)
|
return (self.IGNORE, None)
|
||||||
elif write_type == WriteType.UNLOGGED_BATCH:
|
elif write_type == WriteType.UNLOGGED_BATCH:
|
||||||
return self._pick_consistency(received_responses)
|
return self._pick_consistency(received_responses)
|
||||||
elif write_type == WriteType.BATCH_LOG:
|
elif write_type == WriteType.BATCH_LOG:
|
||||||
@@ -246,8 +246,8 @@ class FallthroughRetryPolicy(RetryPolicy):
|
|||||||
else:
|
else:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
|
|
||||||
def on_unavailable(self, query, consistency, required_replicas, alive_replicas, attempt_num):
|
def on_unavailable(self, query, consistency, required_replicas, alive_replicas, retry_num):
|
||||||
if attempt_num != 0:
|
if retry_num != 0:
|
||||||
return (self.RETHROW, None)
|
return (self.RETHROW, None)
|
||||||
else:
|
else:
|
||||||
return self._pick_consistency(alive_replicas)
|
return self._pick_consistency(alive_replicas)
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
from cassandra.decoder import ConsistencyLevel
|
||||||
from cassandra.policies import (RoundRobinPolicy, DCAwareRoundRobinPolicy,
|
from cassandra.policies import (RoundRobinPolicy, DCAwareRoundRobinPolicy,
|
||||||
SimpleConvictionPolicy, HostDistance,
|
SimpleConvictionPolicy, HostDistance,
|
||||||
ExponentialReconnectionPolicy)
|
ExponentialReconnectionPolicy, RetryPolicy,
|
||||||
|
WriteType, DowngradingConsistencyRetryPolicy)
|
||||||
from cassandra.pool import Host
|
from cassandra.pool import Host
|
||||||
|
|
||||||
class TestRoundRobinPolicy(unittest.TestCase):
|
class TestRoundRobinPolicy(unittest.TestCase):
|
||||||
@@ -158,3 +160,144 @@ class ExponentialReconnectionPolicyTest(unittest.TestCase):
|
|||||||
self.assertEqual(delay, schedule[i - 1] * 2)
|
self.assertEqual(delay, schedule[i - 1] * 2)
|
||||||
else:
|
else:
|
||||||
self.assertEqual(delay, 100)
|
self.assertEqual(delay, 100)
|
||||||
|
|
||||||
|
|
||||||
|
class RetryPolicyTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_read_timeout(self):
|
||||||
|
policy = RetryPolicy()
|
||||||
|
|
||||||
|
# if this is the second or greater attempt, rethrow
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=1, received_responses=2,
|
||||||
|
data_retrieved=True, retry_num=1)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# if we didn't get enough responses, rethrow
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=2, received_responses=1,
|
||||||
|
data_retrieved=True, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# if we got enough responses, but also got a data response, rethrow
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=2, received_responses=2,
|
||||||
|
data_retrieved=True, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# we got enough reponses but no data response, so retry
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=2, received_responses=2,
|
||||||
|
data_retrieved=False, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, "ONE")
|
||||||
|
|
||||||
|
def test_write_timeout(self):
|
||||||
|
policy = RetryPolicy()
|
||||||
|
|
||||||
|
# if this is the second or greater attempt, rethrow
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=WriteType.SIMPLE,
|
||||||
|
required_responses=1, received_responses=2, retry_num=1)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# if it's not a BATCH_LOG write, don't retry it
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=WriteType.SIMPLE,
|
||||||
|
required_responses=1, received_responses=2, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# retry BATCH_LOG writes regardless of received responses
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=WriteType.BATCH_LOG,
|
||||||
|
required_responses=10000, received_responses=1, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, "ONE")
|
||||||
|
|
||||||
|
|
||||||
|
class DowngradingConsistencyRetryPolicyTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_read_timeout(self):
|
||||||
|
policy = DowngradingConsistencyRetryPolicy()
|
||||||
|
|
||||||
|
# if this is the second or greater attempt, rethrow
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=1, received_responses=2,
|
||||||
|
data_retrieved=True, retry_num=1)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# if we didn't get enough responses, retry at a lower consistency
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=3, received_responses=2,
|
||||||
|
data_retrieved=True, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, ConsistencyLevel.TWO)
|
||||||
|
|
||||||
|
# retry consistency level goes down based on the # of recv'd responses
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=3, received_responses=1,
|
||||||
|
data_retrieved=True, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, ConsistencyLevel.ONE)
|
||||||
|
|
||||||
|
# if we got no responses, rethrow
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=3, received_responses=0,
|
||||||
|
data_retrieved=True, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# if we got enough response but no data, retry
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=3, received_responses=3,
|
||||||
|
data_retrieved=False, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
|
||||||
|
# if we got enough responses, but also got a data response, rethrow
|
||||||
|
retry, consistency = policy.on_read_timeout(
|
||||||
|
query=None, consistency="ONE", required_responses=2, received_responses=2,
|
||||||
|
data_retrieved=True, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
def test_write_timeout(self):
|
||||||
|
policy = DowngradingConsistencyRetryPolicy()
|
||||||
|
|
||||||
|
# if this is the second or greater attempt, rethrow
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=WriteType.SIMPLE,
|
||||||
|
required_responses=1, received_responses=2, retry_num=1)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# ignore failures on these types of writes
|
||||||
|
for write_type in (WriteType.SIMPLE, WriteType.BATCH, WriteType.COUNTER):
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=write_type,
|
||||||
|
required_responses=1, received_responses=2, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.IGNORE)
|
||||||
|
|
||||||
|
# downgrade consistency level on unlogged batch writes
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=WriteType.UNLOGGED_BATCH,
|
||||||
|
required_responses=3, received_responses=1, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, ConsistencyLevel.ONE)
|
||||||
|
|
||||||
|
# retry batch log writes at the same consistency level
|
||||||
|
retry, consistency = policy.on_write_timeout(
|
||||||
|
query=None, consistency="ONE", write_type=WriteType.BATCH_LOG,
|
||||||
|
required_responses=3, received_responses=1, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, "ONE")
|
||||||
|
|
||||||
|
def test_unavailable(self):
|
||||||
|
policy = DowngradingConsistencyRetryPolicy()
|
||||||
|
|
||||||
|
# if this is the second or greater attempt, rethrow
|
||||||
|
retry, consistency = policy.on_unavailable(
|
||||||
|
query=None, consistency="ONE", required_replicas=3, alive_replicas=1, retry_num=1)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETHROW)
|
||||||
|
|
||||||
|
# downgrade consistency on unavailable exceptions
|
||||||
|
retry, consistency = policy.on_unavailable(
|
||||||
|
query=None, consistency="ONE", required_replicas=3, alive_replicas=1, retry_num=0)
|
||||||
|
self.assertEqual(retry, RetryPolicy.RETRY)
|
||||||
|
self.assertEqual(consistency, ConsistencyLevel.ONE)
|
||||||
|
|||||||
Reference in New Issue
Block a user