fix "check" for repos that have incomplete chunks, fixes #364

added try/finally (the code in between was just indented, no
other code changes) to make sure it sets self.index back to None,
even if the code crashes e.g. due to an IntegrityError caused
by an incomplete segment caused by a disk full condition.

also, in prepare_txn, create an empty in-memory index if transaction_id
is None, which is required by the Repository.check code to work correctly.
If the index is not empty there, it will miscalculate segment usage
(self.segments).
This commit is contained in:
Thomas Waldmann 2015-11-03 13:46:00 +01:00
parent 1f271821b8
commit 98f3b8d937
1 changed files with 36 additions and 33 deletions

View File

@ -175,7 +175,7 @@ class Repository:
# the repository instance lives on - even if exceptions happened. # the repository instance lives on - even if exceptions happened.
self._active_txn = False self._active_txn = False
raise raise
if not self.index: if not self.index or transaction_id is None:
self.index = self.open_index(transaction_id) self.index = self.open_index(transaction_id)
if transaction_id is None: if transaction_id is None:
self.segments = {} self.segments = {}
@ -237,38 +237,41 @@ class Repository:
def replay_segments(self, index_transaction_id, segments_transaction_id): def replay_segments(self, index_transaction_id, segments_transaction_id):
self.prepare_txn(index_transaction_id, do_cleanup=False) self.prepare_txn(index_transaction_id, do_cleanup=False)
for segment, filename in self.io.segment_iterator(): try:
if index_transaction_id is not None and segment <= index_transaction_id: for segment, filename in self.io.segment_iterator():
continue if index_transaction_id is not None and segment <= index_transaction_id:
if segment > segments_transaction_id:
break
self.segments[segment] = 0
for tag, key, offset in self.io.iter_objects(segment):
if tag == TAG_PUT:
try:
s, _ = self.index[key]
self.compact.add(s)
self.segments[s] -= 1
except KeyError:
pass
self.index[key] = segment, offset
self.segments[segment] += 1
elif tag == TAG_DELETE:
try:
s, _ = self.index.pop(key)
self.segments[s] -= 1
self.compact.add(s)
except KeyError:
pass
self.compact.add(segment)
elif tag == TAG_COMMIT:
continue continue
else: if segment > segments_transaction_id:
raise self.CheckNeeded(self.path) break
if self.segments[segment] == 0: # code duplication below?? vvv (see similar code in check())
self.compact.add(segment) self.segments[segment] = 0
self.write_index() for tag, key, offset in self.io.iter_objects(segment):
self.rollback() if tag == TAG_PUT:
try:
s, _ = self.index[key]
self.compact.add(s)
self.segments[s] -= 1
except KeyError:
pass
self.index[key] = segment, offset
self.segments[segment] += 1
elif tag == TAG_DELETE:
try:
s, _ = self.index.pop(key)
self.segments[s] -= 1
self.compact.add(s)
except KeyError:
pass
self.compact.add(segment)
elif tag == TAG_COMMIT:
continue
else:
raise self.CheckNeeded(self.path)
if self.segments[segment] == 0:
self.compact.add(segment)
self.write_index()
finally:
self.rollback()
def check(self, repair=False): def check(self, repair=False):
"""Check repository consistency """Check repository consistency
@ -297,7 +300,7 @@ class Repository:
if repair: if repair:
self.io.cleanup(transaction_id) self.io.cleanup(transaction_id)
segments_transaction_id = self.io.get_segments_transaction_id() segments_transaction_id = self.io.get_segments_transaction_id()
self.prepare_txn(None) self.prepare_txn(None) # self.index, self.compact, self.segments all empty now!
for segment, filename in self.io.segment_iterator(): for segment, filename in self.io.segment_iterator():
if segment > transaction_id: if segment > transaction_id:
continue continue