/** * Find the best editlog input stream to read from txid. If a journal throws an * CorruptionException while reading from a txn id, it means that it has more transactions, but * can't find any from fromTxId. If this is the case and no other journal has transactions, we * should throw an exception as it means more transactions exist, we just can't load them. * * @param fromTxnId Transaction id to start from. * @return A edit log input stream with tranactions fromTxId or null if no more exist */ @Override public EditLogInputStream getInputStream(long fromTxnId) throws IOException { JournalManager bestjm = null; long bestjmNumTxns = 0; CorruptionException corruption = null; for (JournalAndStream jas : journals) { JournalManager candidate = jas.getManager(); long candidateNumTxns = 0; try { candidateNumTxns = candidate.getNumberOfTransactions(fromTxnId); } catch (CorruptionException ce) { corruption = ce; } catch (IOException ioe) { continue; // error reading disk, just skip } if (candidateNumTxns > bestjmNumTxns) { bestjm = candidate; bestjmNumTxns = candidateNumTxns; } } if (bestjm == null) { if (corruption != null) { throw new IOException("No non-corrupt logs for txid " + fromTxnId, corruption); } else { return null; } } return bestjm.getInputStream(fromTxnId); }
List<JournalManager> getJournalManagers() { List<JournalManager> jList = new ArrayList<JournalManager>(); for (JournalAndStream j : journals) { jList.add(j.getManager()); } return jList; }
/** * Returns true if there are no journals or all are disabled. * * @return True if no journals or all are disabled. */ public boolean isEmpty() { for (JournalAndStream jas : journals) { if (!jas.isDisabled()) { return false; } } return true; }
/** * Return a manifest of what finalized edit logs are available. All available edit logs are * returned starting from the transaction id passed. * * @param fromTxId Starting transaction id to read the logs. * @return RemoteEditLogManifest object. */ public synchronized RemoteEditLogManifest getEditLogManifest(long fromTxId) { // Collect RemoteEditLogs available from each FileJournalManager List<RemoteEditLog> allLogs = Lists.newArrayList(); for (JournalAndStream j : journals) { if (j.getManager() instanceof FileJournalManager) { FileJournalManager fjm = (FileJournalManager) j.getManager(); try { allLogs.addAll(fjm.getRemoteEditLogs(fromTxId)); } catch (Throwable t) { LOG.warn("Cannot list edit logs in " + fjm, t); } } } // Group logs by their starting txid ImmutableListMultimap<Long, RemoteEditLog> logsByStartTxId = Multimaps.index(allLogs, RemoteEditLog.GET_START_TXID); long curStartTxId = fromTxId; List<RemoteEditLog> logs = Lists.newArrayList(); while (true) { ImmutableList<RemoteEditLog> logGroup = logsByStartTxId.get(curStartTxId); if (logGroup.isEmpty()) { // we have a gap in logs - for example because we recovered some old // storage directory with ancient logs. Clear out any logs we've // accumulated so far, and then skip to the next segment of logs // after the gap. SortedSet<Long> startTxIds = Sets.newTreeSet(logsByStartTxId.keySet()); startTxIds = startTxIds.tailSet(curStartTxId); if (startTxIds.isEmpty()) { break; } else { if (LOG.isDebugEnabled()) { LOG.debug( "Found gap in logs at " + curStartTxId + ": " + "not returning previous logs in manifest."); } logs.clear(); curStartTxId = startTxIds.first(); continue; } } // Find the one that extends the farthest forward RemoteEditLog bestLog = Collections.max(logGroup); logs.add(bestLog); // And then start looking from after that point curStartTxId = bestLog.getEndTxId() + 1; } RemoteEditLogManifest ret = new RemoteEditLogManifest(logs); if (LOG.isDebugEnabled()) { LOG.debug("Generated manifest for logs since " + fromTxId + ":" + ret); } return ret; }
@Override protected long getNumSync() { for (JournalAndStream jas : journals) { if (jas.isActive()) { return jas.getCurrentStream().getNumSync(); } } return 0; }
@Override public boolean shouldForceSync() { for (JournalAndStream js : journals) { if (js.isActive() && js.getCurrentStream().shouldForceSync()) { return true; } } return false; }
/** Add sync times to the buffer. */ String getSyncTimes() { StringBuilder buf = new StringBuilder(); for (JournalAndStream jas : journals) { if (jas.isActive()) { buf.append(jas.getCurrentStream().getTotalSyncTime()); buf.append(" "); } } return buf.toString(); }
/** Called when some journals experience an error in some operation. */ private void disableAndReportErrorOnJournals(List<JournalAndStream> badJournals) { if (badJournals == null || badJournals.isEmpty()) { return; // nothing to do } for (JournalAndStream j : badJournals) { LOG.error("Disabling journal " + j); j.abort(); j.setDisabled(true); } }
void remove(JournalManager j) { JournalAndStream jasToRemove = null; for (JournalAndStream jas : journals) { if (jas.getManager().equals(j)) { jasToRemove = jas; break; } } if (jasToRemove != null) { jasToRemove.abort(); journals.remove(jasToRemove); } }
@Override public long getNumberOfTransactions(long fromTxnId) throws IOException { long num = 0; for (JournalAndStream jas : journals) { if (jas.isActive()) { long newNum = jas.getManager().getNumberOfTransactions(fromTxnId); if (newNum > num) { num = newNum; } } } return num; }