A decorator which wraps HistoryAccessor method calls to catch errors from a corrupt SQLite database, move the old database out of the way, and create a new one. We avoid clobbering larger databases because this may be triggered due to filesystem issues, not just a corrupt file.
(f, self, *a, **kw)
| 76 | |
| 77 | @decorator |
| 78 | def catch_corrupt_db(f, self, *a, **kw): |
| 79 | """A decorator which wraps HistoryAccessor method calls to catch errors from |
| 80 | a corrupt SQLite database, move the old database out of the way, and create |
| 81 | a new one. |
| 82 | |
| 83 | We avoid clobbering larger databases because this may be triggered due to filesystem issues, |
| 84 | not just a corrupt file. |
| 85 | """ |
| 86 | try: |
| 87 | return f(self, *a, **kw) |
| 88 | except (DatabaseError, OperationalError) as e: |
| 89 | self._corrupt_db_counter += 1 |
| 90 | self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e) |
| 91 | if self.hist_file != ':memory:': |
| 92 | if self._corrupt_db_counter > self._corrupt_db_limit: |
| 93 | self.hist_file = ':memory:' |
| 94 | self.log.error("Failed to load history too many times, history will not be saved.") |
| 95 | elif os.path.isfile(self.hist_file): |
| 96 | # move the file out of the way |
| 97 | base, ext = os.path.splitext(self.hist_file) |
| 98 | size = os.stat(self.hist_file).st_size |
| 99 | if size >= _SAVE_DB_SIZE: |
| 100 | # if there's significant content, avoid clobbering |
| 101 | now = datetime.datetime.now().isoformat().replace(':', '.') |
| 102 | newpath = base + '-corrupt-' + now + ext |
| 103 | # don't clobber previous corrupt backups |
| 104 | for i in range(100): |
| 105 | if not os.path.isfile(newpath): |
| 106 | break |
| 107 | else: |
| 108 | newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext |
| 109 | else: |
| 110 | # not much content, possibly empty; don't worry about clobbering |
| 111 | # maybe we should just delete it? |
| 112 | newpath = base + '-corrupt' + ext |
| 113 | os.rename(self.hist_file, newpath) |
| 114 | self.log.error("History file was moved to %s and a new file created.", newpath) |
| 115 | self.init_db() |
| 116 | return [] |
| 117 | else: |
| 118 | # Failed with :memory:, something serious is wrong |
| 119 | raise |
| 120 | |
| 121 | class HistoryAccessorBase(LoggingConfigurable): |
| 122 | """An abstract class for History Accessors """ |