/** * Returns the key bounds for the provided log file. * * @return the pair of (lowest key, highest key) that correspond to records stored in the * corresponding log file. * @throws ChangelogException if an error occurs while retrieving the keys */ private Pair<K, K> getKeyBounds(final LogFile<K, V> logFile) throws ChangelogException { try { final String name = logFile.getFile().getName(); final String[] keys = name.substring(0, name.length() - Log.LOG_FILE_SUFFIX.length()) .split(LOG_FILE_NAME_SEPARATOR); return Pair.of( recordParser.decodeKeyFromString(keys[0]), recordParser.decodeKeyFromString(keys[1])); } catch (Exception e) { throw new ChangelogException( ERR_CHANGELOG_UNABLE_TO_RETRIEVE_KEY_BOUNDS_FROM_FILE.get(logFile.getFile().getPath()), e); } }
/** * Purge the log up to and excluding the provided key. * * @param purgeKey the key up to which purging must happen * @return the oldest non purged record, or {@code null} if no record was purged * @throws ChangelogException if a database problem occurs. */ public Record<K, V> purgeUpTo(final K purgeKey) throws ChangelogException { exclusiveLock.lock(); try { if (isClosed) { return null; } final SortedMap<K, LogFile<K, V>> logFilesToPurge = logFiles.headMap(purgeKey); if (logFilesToPurge.isEmpty()) { return null; } final List<String> undeletableFiles = new ArrayList<>(); final Iterator<LogFile<K, V>> entriesToPurge = logFilesToPurge.values().iterator(); while (entriesToPurge.hasNext()) { final LogFile<K, V> logFile = entriesToPurge.next(); try { abortCursorsOpenOnLogFile(logFile); logFile.close(); logFile.delete(); entriesToPurge.remove(); } catch (ChangelogException e) { // The deletion of log file on file system has failed undeletableFiles.add(logFile.getFile().getPath()); } } if (!undeletableFiles.isEmpty()) { throw new ChangelogException( ERR_CHANGELOG_UNABLE_TO_DELETE_LOG_FILE_WHILE_PURGING.get( Utils.joinAsString(", ", undeletableFiles))); } return getOldestRecord(); } finally { exclusiveLock.unlock(); } }
/** * Empties the log, discarding all records it contains. * * <p>All cursors open on the log are aborted. * * @throws ChangelogException If cursors are opened on this log, or if a problem occurs during * clearing operation. */ public void clear() throws ChangelogException { exclusiveLock.lock(); try { if (isClosed) { return; } if (!openCursors.isEmpty()) { // All open cursors are aborted, which means the change number indexer thread // should manage AbortedChangelogCursorException specifically to avoid being // stopped abortAllOpenCursors(); } // delete all log files final List<String> undeletableFiles = new ArrayList<>(); for (LogFile<K, V> logFile : logFiles.values()) { try { logFile.close(); logFile.delete(); } catch (ChangelogException e) { undeletableFiles.add(logFile.getFile().getPath()); } } if (!undeletableFiles.isEmpty()) { throw new ChangelogException( ERR_CHANGELOG_UNABLE_TO_DELETE_LOG_FILE.get( Utils.joinAsString(", ", undeletableFiles))); } logFiles.clear(); // recreate an empty head log file openHeadLogFile(); } catch (Exception e) { throw new ChangelogException( ERR_ERROR_CLEARING_DB.get(logPath.getPath(), stackTraceToSingleLineString(e))); } finally { exclusiveLock.unlock(); } }
private boolean isHeadLogFile(final LogFile<K, V> logFile) { return logFile.getFile().getName().equals(Log.HEAD_LOG_FILE_NAME); }
/** * Dump this log as a text files, intended for debugging purpose only. * * @param dumpDirectory Directory that will contains log files with text format and ".txt" * extensions * @throws ChangelogException If an error occurs during dump */ void dumpAsTextFile(File dumpDirectory) throws ChangelogException { for (LogFile<K, V> logFile : logFiles.values()) { logFile.dumpAsTextFile(new File(dumpDirectory, logFile.getFile().getName() + ".txt")); } }
@Override public String toString() { return String.format( "Cursor on log : %s, current log file: %s, current cursor: %s", log.logPath, currentLogFile.getFile().getName(), currentCursor); }