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

Proposed by Guillermo Gonzalez
Status: Merged
Approved by: dobey
Approved revision: 1135
Merged at revision: 1138
Proposed branch: lp:~verterok/ubuntuone-client/fix-776386
Merge into: lp:ubuntuone-client
Diff against target: 156 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
Reviewer Review Type Date Requested Status
Facundo Batista (community) Approve
Review via email: mp+75622@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) wrote :

Nice!

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-08-30 17:16:49 +0000
3+++ tests/syncdaemon/test_tritcask.py 2011-09-15 19:58:33 +0000
4@@ -38,9 +38,11 @@
5 HINT,
6 INACTIVE,
7 DEAD,
8+ BROKEN,
9 BadCrc,
10 BadHeader,
11 DataFile,
12+ ImmutableDataFile,
13 TempDataFile,
14 DeadDataFile,
15 HintFile,
16@@ -869,6 +871,52 @@
17 key=attrgetter('filename'))]
18 self.assertEqual(files, filenames)
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-08-23 15:48:54 +0000
72+++ ubuntuone/syncdaemon/tritcask.py 2011-09-15 19:58:33 +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@@ -172,12 +174,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@@ -681,6 +683,7 @@
104 """Collect the files we need to work with."""
105 logger.debug("lookingup data files")
106 dead_files = 0
107+ broken_files = 0
108 # becuase listdir appends / at the end of the path if there is not
109 # os.path.sep at the end we are going to add it, otherwhise on windows
110 # we will have wrong paths formed
111@@ -689,21 +692,37 @@
112 base_path += os.path.sep
113 files = os.listdir(self.base_path)
114 for filename in files:
115+ # first check for hint and dead
116 if is_hint(filename):
117 continue
118- if is_live(filename):
119- self.live_file = DataFile(self.base_path, filename)
120- elif is_immutable(filename):
121- # it's an immutable file
122- data_file = ImmutableDataFile(self.base_path, filename)
123- self._immutable[data_file.file_id] = data_file
124 elif is_dead(filename):
125 # a dead file...let's remove it
126 dead_files += 1
127 DeadDataFile(self.base_path, filename).delete()
128+ continue
129+ # if it's a live or immutable file try to open it, but if it's
130+ # "broken", just rename it and continue
131+ try:
132+ if is_live(filename):
133+ self.live_file = DataFile(self.base_path, filename)
134+ elif is_immutable(filename):
135+ # it's an immutable file
136+ data_file = ImmutableDataFile(self.base_path, filename)
137+ self._immutable[data_file.file_id] = data_file
138+ except IOError, e:
139+ # oops, failed to open the file..discard it
140+ broken_files += 1
141+ orig = os.path.join(self.base_path, filename)
142+ # get the kind of the file so we can rename it
143+ kind = LIVE if is_live(filename) else INACTIVE
144+ dest = orig.replace(kind, BROKEN)
145+ logger.warning("Failed to open %s, renaming it to: %s - "
146+ "error: %s", orig, dest, e)
147+ # rename it to "broken"
148+ os.rename(orig, dest)
149 # immutable files + live
150- logger.info("found %s data files and %s dead files",
151- len(self._immutable)+1, dead_files)
152+ logger.info("found %s data files, %s dead and %s broken files",
153+ len(self._immutable)+1, dead_files, broken_files)
154
155 def rotate(self, create_file=True):
156 """Rotate the live file only if it already exsits."""

Subscribers

People subscribed via source and target branches