Merge lp:~jderose/dmedia/downgrade-unknown into lp:dmedia

Proposed by Jason Gerard DeRose
Status: Merged
Merged at revision: 589
Proposed branch: lp:~jderose/dmedia/downgrade-unknown
Merge into: lp:dmedia
Diff against target: 256 lines (+143/-57)
2 files modified
dmedia/metastore.py (+26/-14)
dmedia/tests/test_metastore.py (+117/-43)
To merge this branch: bzr merge lp:~jderose/dmedia/downgrade-unknown
Reviewer Review Type Date Requested Status
xzcvczx (community) Approve
dmedia Dev Pending
Review via email: mp+150052@code.launchpad.net

Description of the change

For more info, see this bug:
https://bugs.launchpad.net/dmedia/+bug/1131742

This reworks MetaStore.downgrade_by_store_atime() to be driven by the "file/stored" view rather than the "store/atime" view.

Importantly, this change means copies in a store will always be downgraded when the store lacks a corresponding doc in CouchDB. Previously, a missing dmedia/store doc meant these copies never got downgraded by the store atime (although they would eventually get downgraded when the DOWNGRADE_BY_LAST_VERIFIED threshold was reached).

To be a bit more conservative as we test and tune the remaining automation behaviours, I also lowered DOWNGRADE_BY_LAST_VERIFIED from 4 weeks to 2 weeks.

Lastly, the threshold for when files would start to be re-verified was previous hard-coded using the ONE_WEEK constant. Instead, it's now using new VERIFY_THRESHOLD constant (also in the tests), making this parameter easy to tune.

To post a comment you must log in.
Revision history for this message
xzcvczx (xzcvczx) wrote :

Nice

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'dmedia/metastore.py'
--- dmedia/metastore.py 2013-02-21 13:38:52 +0000
+++ dmedia/metastore.py 2013-02-22 14:17:19 +0000
@@ -48,12 +48,13 @@
4848
4949
50log = logging.getLogger()50log = logging.getLogger()
51
51DAY = 24 * 60 * 6052DAY = 24 * 60 * 60
52ONE_WEEK = 7 * DAY53WEEK = 7 * DAY
5354DOWNGRADE_BY_NEVER_VERIFIED = 2 * DAY
54DOWNGRADE_BY_STORE_ATIME = 7 * DAY # 1 week55VERIFY_THRESHOLD = WEEK
55DOWNGRADE_BY_NEVER_VERIFIED = 2 * DAY # 48 hours56DOWNGRADE_BY_STORE_ATIME = WEEK
56DOWNGRADE_BY_LAST_VERIFIED = 28 * DAY # 4 weeks57DOWNGRADE_BY_LAST_VERIFIED = 2 * WEEK
5758
5859
59class MTimeMismatch(Exception):60class MTimeMismatch(Exception):
@@ -71,7 +72,7 @@
71 return time.perf_counter() - self.start72 return time.perf_counter() - self.start
7273
73 def log(self, msg, *args):74 def log(self, msg, *args):
74 log.info('[%.3fs] ' + msg, self.delta, *args)75 log.info('[%.3f] ' + msg, self.delta, *args)
7576
7677
77def get_dict(d, key):78def get_dict(d, key):
@@ -361,13 +362,24 @@
361 if curtime is None:362 if curtime is None:
362 curtime = int(time.time())363 curtime = int(time.time())
363 assert isinstance(curtime, int) and curtime >= 0364 assert isinstance(curtime, int) and curtime >= 0
364 rows = self.db.view('store', 'atime',365 threshold = curtime - DOWNGRADE_BY_STORE_ATIME
365 endkey=(curtime - DOWNGRADE_BY_STORE_ATIME)366 t = TimeDelta()
366 )['rows']367 result = {}
367 ids = [row['id'] for row in rows]368 rows = self.db.view('file', 'stored', reduce=True, group=True)['rows']
368 for store_id in ids:369 for row in rows:
369 self.downgrade_store(store_id)370 store_id = row['key']
370 return ids371 try:
372 doc = self.db.get(store_id)
373 atime = doc.get('atime')
374 if isinstance(atime, int) and atime > threshold:
375 log.info('Store %s okay at atime %s', store_id, atime)
376 continue
377 except NotFound:
378 log.warning('No doc found for store %s, forcing downgrade')
379 result[store_id] = self.downgrade_store(store_id)
380 total = sum(result.values())
381 t.log('downgraded %d total copies in %d stores', total, len(result))
382 return result
371383
372 def downgrade_store(self, store_id):384 def downgrade_store(self, store_id):
373 t = TimeDelta()385 t = TimeDelta()
@@ -545,7 +557,7 @@
545557
546 def verify_all(self, fs):558 def verify_all(self, fs):
547 start = [fs.id, None]559 start = [fs.id, None]
548 end = [fs.id, int(time.time()) - ONE_WEEK]560 end = [fs.id, int(time.time()) - VERIFY_THRESHOLD]
549 count = 0561 count = 0
550 t = TimeDelta()562 t = TimeDelta()
551 log.info('verifying %r', fs)563 log.info('verifying %r', fs)
552564
=== modified file 'dmedia/tests/test_metastore.py'
--- dmedia/tests/test_metastore.py 2013-02-21 09:31:46 +0000
+++ dmedia/tests/test_metastore.py 2013-02-22 14:17:19 +0000
@@ -1019,54 +1019,128 @@
1019 for (old, new) in zip(docs, db.get_many(ids)):1019 for (old, new) in zip(docs, db.get_many(ids)):
1020 self.assertEqual(old, new)1020 self.assertEqual(old, new)
10211021
1022 def test_downgrade_by_store_atime(self): 1022 def test_downgrade_by_store_atime(self):
1023 db = util.get_db(self.env, True)1023 class PassThrough(metastore.MetaStore):
1024
1025 class Dummy(metastore.MetaStore):
1026 def __init__(self, db):
1027 super().__init__(db)
1028 self._calls = []
1029
1030 def downgrade_store(self, store_id):1024 def downgrade_store(self, store_id):
1031 self._calls.append(store_id)1025 self._calls.append(store_id)
1026 return super().downgrade_store(store_id)
10321027
1033 # Test when empty1028 db = util.get_db(self.env, True)
1034 ms = Dummy(db)1029 ms = PassThrough(db)
1035 self.assertEqual(ms.downgrade_by_store_atime(), [])
1036 self.assertEqual(ms._calls, [])
1037 curtime = int(time.time())1030 curtime = int(time.time())
1038 self.assertEqual(ms.downgrade_by_store_atime(curtime), [])
1039 self.assertEqual(ms._calls, [])
1040
1041 # Test when some need to be downgraded
1042 base = curtime - metastore.DOWNGRADE_BY_STORE_ATIME1031 base = curtime - metastore.DOWNGRADE_BY_STORE_ATIME
1043 docs = []1032
1044 for i in range(8):1033 # Test when empty:
1045 doc = {1034 ms._calls = []
1046 '_id': random_id(),1035 self.assertEqual(ms.downgrade_by_store_atime(), {})
1047 'type': 'dmedia/store',1036 self.assertEqual(ms.downgrade_by_store_atime(curtime), {})
1048 'atime': base + i,
1049 }
1050 docs.append(doc)
1051 db.save_many(docs)
1052 ids = [doc['_id'] for doc in docs]
1053 self.assertEqual(ms.downgrade_by_store_atime(curtime - 1), [])
1054 self.assertEqual(ms._calls, [])1037 self.assertEqual(ms._calls, [])
1055 for i in range(8):1038
1056 expected = ids[:i+1]1039 # One store that's missing its doc:
1057 self.assertEqual(1040 store_id1 = random_id()
1058 ms.downgrade_by_store_atime(curtime + i),1041 ids1 = tuple(random_id() for i in range(17))
1059 expected1042 docs = [
1060 )1043 {
1061 self.assertEqual(ms._calls, expected)1044 '_id': _id,
1062 ms._calls = []1045 'type': 'dmedia/file',
10631046 'stored': {store_id1: {'copies': 1}},
1064 # Once more with feeling1047 }
1065 self.assertEqual(1048 for _id in ids1
1066 ms.downgrade_by_store_atime(curtime),1049 ]
1067 [ids[0]]1050 db.save_many(docs)
1068 )1051
1069 self.assertEqual(ms._calls, [ids[0]])1052 # Another store with an atime old enough to trigger a downgrade:
1053 store_id2 = random_id()
1054 doc2 = {'_id': store_id2, 'atime': base}
1055 db.save(doc2)
1056 ids2 = tuple(random_id() for i in range(18))
1057 docs = [
1058 {
1059 '_id': _id,
1060 'type': 'dmedia/file',
1061 'stored': {store_id2: {'copies': 1}},
1062 }
1063 for _id in ids2
1064 ]
1065 db.save_many(docs)
1066
1067 # A store with an atime new enough to be okay:
1068 store_id3 = random_id()
1069 doc3 = {'_id': store_id3, 'atime': base + 1}
1070 db.save(doc3)
1071 ids3 = tuple(random_id() for i in range(19))
1072 docs = [
1073 {
1074 '_id': _id,
1075 'type': 'dmedia/file',
1076 'stored': {store_id3: {'copies': 1}},
1077 }
1078 for _id in ids3
1079 ]
1080 db.save_many(docs)
1081
1082 # And finally a store missing its doc['atime']:
1083 store_id4 = random_id()
1084 doc4 = {'_id': store_id4}
1085 db.save(doc4)
1086 ids4 = tuple(random_id() for i in range(20))
1087 docs = [
1088 {
1089 '_id': _id,
1090 'type': 'dmedia/file',
1091 'stored': {store_id4: {'copies': 1}},
1092 }
1093 for _id in ids4
1094 ]
1095 db.save_many(docs)
1096
1097 # Test at curtime:
1098 self.assertEqual(ms.downgrade_by_store_atime(curtime),
1099 {store_id1: 17, store_id2: 18, store_id4: 20}
1100 )
1101 self.assertEqual(ms._calls,
1102 sorted([store_id1, store_id2, store_id4])
1103 )
1104 for doc in db.get_many(ids1):
1105 self.assertTrue(doc['_rev'].startswith('2-'))
1106 self.assertEqual(doc['stored'], {store_id1: {'copies': 0}})
1107 for doc in db.get_many(ids2):
1108 self.assertTrue(doc['_rev'].startswith('2-'))
1109 self.assertEqual(doc['stored'], {store_id2: {'copies': 0}})
1110 for doc in db.get_many(ids3):
1111 self.assertTrue(doc['_rev'].startswith('1-'))
1112 self.assertEqual(doc['stored'], {store_id3: {'copies': 1}})
1113 for doc in db.get_many(ids4):
1114 self.assertTrue(doc['_rev'].startswith('2-'))
1115 self.assertEqual(doc['stored'], {store_id4: {'copies': 0}})
1116
1117 # Test at curtime + 1:
1118 ms._calls = []
1119 self.assertEqual(ms.downgrade_by_store_atime(curtime + 1),
1120 {store_id1: 0, store_id2: 0, store_id3: 19, store_id4: 0}
1121 )
1122 self.assertEqual(ms._calls,
1123 sorted([store_id1, store_id2, store_id3, store_id4])
1124 )
1125 for doc in db.get_many(ids1):
1126 self.assertTrue(doc['_rev'].startswith('2-'))
1127 self.assertEqual(doc['stored'], {store_id1: {'copies': 0}})
1128 for doc in db.get_many(ids2):
1129 self.assertTrue(doc['_rev'].startswith('2-'))
1130 self.assertEqual(doc['stored'], {store_id2: {'copies': 0}})
1131 for doc in db.get_many(ids3):
1132 self.assertTrue(doc['_rev'].startswith('2-'))
1133 self.assertEqual(doc['stored'], {store_id3: {'copies': 0}})
1134 for doc in db.get_many(ids4):
1135 self.assertTrue(doc['_rev'].startswith('2-'))
1136 self.assertEqual(doc['stored'], {store_id4: {'copies': 0}})
1137
1138 # Make sure the dmedia/store docs aren't modified:
1139 with self.assertRaises(microfiber.NotFound) as cm:
1140 db.get(store_id1)
1141 self.assertEqual(db.get(store_id2), doc2)
1142 self.assertEqual(db.get(store_id3), doc3)
1143 self.assertEqual(db.get(store_id4), doc4)
10701144
1071 def test_downgrade_store(self): 1145 def test_downgrade_store(self):
1072 db = util.get_db(self.env, True)1146 db = util.get_db(self.env, True)
@@ -1554,7 +1628,7 @@
1554 again = docs2[:4]1628 again = docs2[:4]
1555 assert len(again) == 41629 assert len(again) == 4
1556 for doc in again:1630 for doc in again:
1557 doc['stored'][fs.id]['verified'] -= (metastore.ONE_WEEK + 1)1631 doc['stored'][fs.id]['verified'] -= (metastore.VERIFY_THRESHOLD + 1)
1558 db.save(doc)1632 db.save(doc)
1559 self.assertEqual(ms.verify_all(fs), 4)1633 self.assertEqual(ms.verify_all(fs), 4)
1560 self.assertEqual(ms.verify_all(fs), 0)1634 self.assertEqual(ms.verify_all(fs), 0)

Subscribers

People subscribed via source and target branches