summaryrefslogtreecommitdiff
path: root/test/unit/account/test_backend.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit/account/test_backend.py')
-rw-r--r--test/unit/account/test_backend.py66
1 files changed, 66 insertions, 0 deletions
diff --git a/test/unit/account/test_backend.py b/test/unit/account/test_backend.py
index 15422bd13..3556a1ad0 100644
--- a/test/unit/account/test_backend.py
+++ b/test/unit/account/test_backend.py
@@ -180,6 +180,72 @@ class TestAccountBroker(unittest.TestCase):
broker.delete_db(Timestamp.now().internal)
broker.reclaim(Timestamp.now().internal, time())
+ def test_batched_reclaim(self):
+ num_of_containers = 60
+ container_specs = []
+ now = time()
+ top_of_the_minute = now - (now % 60)
+ c = itertools.cycle([True, False])
+ for m, is_deleted in six.moves.zip(range(num_of_containers), c):
+ offset = top_of_the_minute - (m * 60)
+ container_specs.append((Timestamp(offset), is_deleted))
+ random.seed(now)
+ random.shuffle(container_specs)
+ policy_indexes = list(p.idx for p in POLICIES)
+ broker = AccountBroker(':memory:', account='test_account')
+ broker.initialize(Timestamp('1').internal)
+ for i, container_spec in enumerate(container_specs):
+ # with container12 before container2 and shuffled ts.internal we
+ # shouldn't be able to accidently rely on any implicit ordering
+ name = 'container%s' % i
+ pidx = random.choice(policy_indexes)
+ ts, is_deleted = container_spec
+ if is_deleted:
+ broker.put_container(name, 0, ts.internal, 0, 0, pidx)
+ else:
+ broker.put_container(name, ts.internal, 0, 0, 0, pidx)
+
+ def count_reclaimable(conn, reclaim_age):
+ return conn.execute(
+ "SELECT count(*) FROM container "
+ "WHERE deleted = 1 AND delete_timestamp < ?", (reclaim_age,)
+ ).fetchone()[0]
+
+ # This is intended to divide the set of timestamps exactly in half
+ # regardless of the value of now
+ reclaim_age = top_of_the_minute + 1 - (num_of_containers / 2 * 60)
+ with broker.get() as conn:
+ self.assertEqual(count_reclaimable(conn, reclaim_age),
+ num_of_containers / 4)
+
+ orig__reclaim = broker._reclaim
+ trace = []
+
+ def tracing_reclaim(conn, age_timestamp, marker):
+ trace.append((age_timestamp, marker,
+ count_reclaimable(conn, age_timestamp)))
+ return orig__reclaim(conn, age_timestamp, marker)
+
+ with mock.patch.object(broker, '_reclaim', new=tracing_reclaim), \
+ mock.patch('swift.common.db.RECLAIM_PAGE_SIZE', 10):
+ broker.reclaim(reclaim_age, reclaim_age)
+ with broker.get() as conn:
+ self.assertEqual(count_reclaimable(conn, reclaim_age), 0)
+ self.assertEqual(3, len(trace), trace)
+ self.assertEqual([age for age, marker, reclaimable in trace],
+ [reclaim_age] * 3)
+ # markers are in-order
+ self.assertLess(trace[0][1], trace[1][1])
+ self.assertLess(trace[1][1], trace[2][1])
+ # reclaimable count gradually decreases
+ # generally, count1 > count2 > count3, but because of the randomness
+ # we may occassionally have count1 == count2 or count2 == count3
+ self.assertGreaterEqual(trace[0][2], trace[1][2])
+ self.assertGreaterEqual(trace[1][2], trace[2][2])
+ # technically, this might happen occasionally, but *really* rarely
+ self.assertTrue(trace[0][2] > trace[1][2] or
+ trace[1][2] > trace[2][2])
+
def test_delete_db_status(self):
start = next(self.ts)
broker = AccountBroker(':memory:', account='a')