Merge lp:~verterok/ubuntuone-client/fix-776386-1-6 into lp:ubuntuone-client/stable-1-6

Proposed by Guillermo Gonzalez
Status: Merged
Approved by: dobey
Approved revision: 968
Merged at revision: 970
Proposed branch: lp:~verterok/ubuntuone-client/fix-776386-1-6
Merge into: lp:ubuntuone-client/stable-1-6
Diff against target: 151 lines (+76/-9)
2 files modified
tests/syncdaemon/test_tritcask.py (+48/-0)
ubuntuone/syncdaemon/tritcask.py (+28/-9)
To merge this branch: bzr merge lp:~verterok/ubuntuone-client/fix-776386-1-6
Reviewer Review Type Date Requested Status
Guillermo Gonzalez Approve
Facundo Batista (community) Approve
Review via email: mp+75623@code.launchpad.net

Commit message

Fix Tritcask._find_data_files method to handle the case of broken/corrupted data files (when opening the file).

Description of the change

This branch fix Tritcask._find_data_files method to handle the case of broken/corrupted data files (when opening the file).
It basically rename any broken "immutable" data file doing: s/".inactive"/".broken"/

To post a comment you must log in.
Revision history for this message
Facundo Batista (facundo) :
review: Approve
Revision history for this message
Guillermo Gonzalez (verterok) wrote :

Approving with one review

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/syncdaemon/test_tritcask.py'
2--- tests/syncdaemon/test_tritcask.py 2011-04-05 16:50:31 +0000
3+++ tests/syncdaemon/test_tritcask.py 2011-09-15 19:58:34 +0000
4@@ -37,9 +37,11 @@
5 HINT,
6 INACTIVE,
7 DEAD,
8+ BROKEN,
9 BadCrc,
10 BadHeader,
11 DataFile,
12+ ImmutableDataFile,
13 TempDataFile,
14 DeadDataFile,
15 HintFile,
16@@ -841,6 +843,52 @@
17 self.assertEqual(files, filenames)
18 db.shutdown()
19
20+ def test_find_data_files_immutable_open_error(self):
21+ """Test the _find_data_files method failing to open a file."""
22+ data_file = DataFile(self.base_dir)
23+ data_file.write(0, 'foo_0', 'bar_0')
24+ immutable_file = data_file.make_immutable()
25+ immutable_file.close()
26+ # patch the open call and make it fail
27+ def fail_open(filename, *a):
28+ """Always fail."""
29+ raise IOError("I'm a broken file.")
30+ self.patch(ImmutableDataFile, '_open', fail_open)
31+ db = Tritcask(self.base_dir)
32+ self.addCleanup(db.shutdown)
33+ files = [fn.filename for fn in sorted(db._immutable.values(),
34+ key=attrgetter('filename'))]
35+ self.assertNotIn(immutable_file.filename, files)
36+ # check the logs
37+ msg = ("Failed to open %s, renaming it to: %s - error: %s" % \
38+ (immutable_file.filename,
39+ immutable_file.filename.replace(INACTIVE, BROKEN),
40+ IOError("I'm a broken file")))
41+ self.assertTrue(self.memento.check_warning(msg))
42+
43+ def test_find_data_files_live_open_error(self):
44+ """Test the _find_data_files method failing to open a file."""
45+ data_file = DataFile(self.base_dir)
46+ data_file.write(0, 'foo_0', 'bar_0')
47+ data_file.close()
48+ # patch the open call and make it fail
49+ orig_open = DataFile._open
50+ def fail_open(filename, *a):
51+ """Always fail only once."""
52+ self.patch(DataFile, '_open', orig_open)
53+ raise IOError("I'm a broken file.")
54+ self.patch(DataFile, '_open', fail_open)
55+ db = Tritcask(self.base_dir)
56+ self.addCleanup(db.shutdown)
57+ self.assertNotEqual(data_file.filename, db.live_file.filename)
58+ # check the logs
59+ self.memento.debug = True
60+ msg = ("Failed to open %s, renaming it to: %s - error: %s" % \
61+ (data_file.filename,
62+ data_file.filename.replace(LIVE, BROKEN),
63+ IOError("I'm a broken file")))
64+ self.assertTrue(self.memento.check_warning(msg))
65+
66 def test_build_keydir_on_init(self):
67 """Test _build_keydir method."""
68 db = Tritcask(self.base_dir)
69
70=== modified file 'ubuntuone/syncdaemon/tritcask.py'
71--- ubuntuone/syncdaemon/tritcask.py 2011-04-05 16:50:31 +0000
72+++ ubuntuone/syncdaemon/tritcask.py 2011-09-15 19:58:34 +0000
73@@ -55,6 +55,7 @@
74 INACTIVE = '.inactive'
75 HINT = '.hint'
76 DEAD = '.dead'
77+BROKEN = '.broken'
78
79 VERSION = 'v1'
80 FILE_SUFFIX = '.tritcask-%s.data' % VERSION
81@@ -73,6 +74,7 @@
82 class BadHeader(Exception):
83 """A Exception for Bad header value."""
84
85+
86 TritcaskEntry = namedtuple('TritcaskEntry', ['crc32', 'tstamp', 'key_sz',
87 'value_sz', 'row_type', 'key', 'value', 'value_pos'])
88
89@@ -148,12 +150,12 @@
90 """
91 self.has_bad_crc = False
92 self.has_bad_data = False
93+ self.fd = None
94 if filename is None:
95 filename = self._get_next_file_id() + LIVE + FILE_SUFFIX
96 self.filename = os.path.join(base_path, filename)
97 self.file_id = _get_file_id(filename)
98 self.hint_filename = os.path.join(base_path, filename + HINT)
99- self.fd = None
100 self._open()
101
102 @property
103@@ -645,23 +647,40 @@
104 """Collect the files we need to work with."""
105 logger.debug("lookingup data files")
106 dead_files = 0
107+ broken_files = 0
108 files = os.listdir(self.base_path)
109 for filename in files:
110+ # first check for hint and dead
111 if is_hint(filename):
112 continue
113- if is_live(filename):
114- self.live_file = DataFile(self.base_path, filename)
115- elif is_immutable(filename):
116- # it's an immutable file
117- data_file = ImmutableDataFile(self.base_path, filename)
118- self._immutable[data_file.file_id] = data_file
119 elif is_dead(filename):
120 # a dead file...let's remove it
121 dead_files += 1
122 DeadDataFile(self.base_path, filename).delete()
123+ continue
124+ # if it's a live or immutable file try to open it, but if it's
125+ # "broken", just rename it and continue
126+ try:
127+ if is_live(filename):
128+ self.live_file = DataFile(self.base_path, filename)
129+ elif is_immutable(filename):
130+ # it's an immutable file
131+ data_file = ImmutableDataFile(self.base_path, filename)
132+ self._immutable[data_file.file_id] = data_file
133+ except IOError, e:
134+ # oops, failed to open the file..discard it
135+ broken_files += 1
136+ orig = os.path.join(self.base_path, filename)
137+ # get the kind of the file so we can rename it
138+ kind = LIVE if is_live(filename) else INACTIVE
139+ dest = orig.replace(kind, BROKEN)
140+ logger.warning("Failed to open %s, renaming it to: %s - "
141+ "error: %s", orig, dest, e)
142+ # rename it to "broken"
143+ os.rename(orig, dest)
144 # immutable files + live
145- logger.info("found %s data files and %s dead files",
146- len(self._immutable)+1, dead_files)
147+ logger.info("found %s data files, %s dead and %s broken files",
148+ len(self._immutable)+1, dead_files, broken_files)
149
150 def rotate(self, create_file=True):
151 """Rotate the live file only if it already exsits."""

Subscribers

People subscribed via source and target branches