def commit(self, mergeFn=None): if self._status & RepositoryView.COMMITTING == 0: try: self._exclusive.acquire() self._status |= RepositoryView.COMMITTING store = self.repository.store before = time() size = 0L txnStatus = 0 lock = None def finish(lock, txnStatus, commit): if commit: self._commitTransaction(txnStatus) else: self._abortTransaction(txnStatus) if lock is not None: lock = store.releaseLock(lock) return lock, 0 notifications = RepositoryNotifications() while True: try: while True: self.refresh(mergeFn) lock = store.acquireLock() newVersion = store.getVersion() if newVersion > self._version: lock = store.releaseLock(lock) else: break count = len(self._log) + len(self._deletedRegistry) if count > 1000: self.logger.info('%s committing %d items...', self, count) txnStatus = self._startTransaction() if count > 0: newVersion += 1 store._values.setVersion(newVersion) itemWriter = DBItemWriter(store) for item in self._log: size += self._saveItem(item, newVersion, itemWriter, notifications) for item in self._deletedRegistry.itervalues(): size += self._saveItem(item, newVersion, itemWriter, notifications) if self.isDirty(): size += self._roots._saveValues(newVersion) lock, txnStatus = finish(lock, txnStatus, True) break except DBLockDeadlockError: self.logger.info('retrying commit aborted by deadlock') lock, txnStatus = finish(lock, txnStatus, False) continue except: if txnStatus: self.logger.exception('aborting transaction (%d kb)', size >> 10) lock, txnStatus = finish(lock, txnStatus, False) raise self._version = newVersion if self._log: for item in self._log: item._version = newVersion item.setDirty(0, None) item._status &= ~(Item.NEW | Item.MERGED) self._log.clear() if self.isDirty(): self._roots._clearDirties() self.setDirty(0) if self._deletedRegistry: self._deletedRegistry.clear() after = time() if count > 0: duration = after - before try: iSpeed = "%d items/s" % round(count / duration) dSpeed = "%d kbytes/s" % round((size >> 10) / duration) except ZeroDivisionError: iSpeed = dSpeed = 'speed could not be measured' self.logger.info('%s committed %d items (%d kbytes) in %s, %s (%s)', self, count, size >> 10, timedelta(seconds=duration), iSpeed, dSpeed) if len(notifications) > 0: notifications.dispatchHistory(self) finally: self._status &= ~RepositoryView.COMMITTING self._exclusive.release()
def refresh(self, mergeFn=None, version=None): store = self.repository.store newVersion = version or store.getVersion() if newVersion > self._version: histNotifications = RepositoryNotifications() unloads = {} also = set() _log = self._log try: self._log = set() try: self._mergeItems(self._version, newVersion, histNotifications, unloads, also, mergeFn) except: for item in self._log: item.setDirty(0) item._unloadItem(True, self) raise else: # unload items unchanged until changed by merging for item in self._log: item.setDirty(0) unloads[item._uuid] = item finally: self._log = _log # unload items changed only in the other view whose older version # got loaded as a side-effect of merging for uuid in also: item = self.find(uuid, False) if item is not None: unloads[uuid] = item self.logger.debug('refreshing view from version %d to %d', self._version, newVersion) self._version = newVersion refCounted = self.isRefCounted() for item in unloads.itervalues(): self.logger.debug('unloading version %d of %s', item._version, item) item._unloadItem(refCounted or item.isPinned(), self) for item in unloads.itervalues(): if refCounted or item.isPinned(): self.logger.debug('reloading version %d of %s', newVersion, item) self.find(item._uuid) before = time() count = len(histNotifications) histNotifications.dispatchHistory(self) duration = time() - before if duration > 1.0: self.logger.warning('%s %d notifications ran in %s', self, count, timedelta(seconds=duration)) self.prune(10000) elif newVersion < self._version: self.cancel() for item in [item for item in self._registry.itervalues() if item._version > newVersion]: item._unloadItem(True, self) self._version = newVersion