Example #1
0
  /**
   * Flush a specified ledger
   *
   * @param ledger Ledger Id
   * @throws IOException
   */
  private void flushSpecificLedger(long ledger) throws IOException {
    LinkedList<Long> firstEntryList = pageMapAndList.getFirstEntryListToBeFlushed(ledger);

    // flush ledger index file header if necessary
    indexPersistenceManager.flushLedgerHeader(ledger);

    if (null == firstEntryList || firstEntryList.size() == 0) {
      LOG.debug("Nothing to flush for ledger {}.", ledger);
      // nothing to do
      return;
    }

    // Now flush all the pages of a ledger
    List<LedgerEntryPage> entries = new ArrayList<LedgerEntryPage>(firstEntryList.size());
    try {
      for (Long firstEntry : firstEntryList) {
        LedgerEntryPage lep = getLedgerEntryPage(ledger, firstEntry, true);
        if (lep != null) {
          entries.add(lep);
        }
      }
      indexPersistenceManager.flushLedgerEntries(ledger, entries);
    } finally {
      for (LedgerEntryPage lep : entries) {
        lep.releasePage();
      }
    }
  }
Example #2
0
 /**
  * Grab ledger entry page whose first entry is <code>pageEntry</code>.
  *
  * <p>If the page doesn't existed before, we allocate a memory page. Otherwise, we grab a clean
  * page and read it from disk.
  *
  * @param ledger Ledger Id
  * @param pageEntry Start entry of this entry page.
  */
 private LedgerEntryPage grabLedgerEntryPage(long ledger, long pageEntry) throws IOException {
   LedgerEntryPage lep = grabCleanPage(ledger, pageEntry);
   try {
     // should get the up to date page from the persistence manager
     // before we put it into table otherwise we would put
     // an empty page in it
     indexPersistenceManager.updatePage(lep);
     LedgerEntryPage oldLep;
     if (lep != (oldLep = pageMapAndList.putPage(lep))) {
       lep.releasePage();
       // Decrement the page count because we couldn't put this lep in the page cache.
       pageCount.decrementAndGet();
       // Increment the use count of the old lep because this is unexpected
       oldLep.usePage();
       lep = oldLep;
     }
   } catch (IOException ie) {
     // if we grab a clean page, but failed to update the page
     // we are exhausting the count of ledger entry pages.
     // since this page will be never used, so we need to decrement
     // page count of ledger cache.
     lep.releasePage();
     pageCount.decrementAndGet();
     throw ie;
   }
   return lep;
 }
Example #3
0
  private LedgerEntryPage grabCleanPage(long ledger, long entry) throws IOException {
    if (entry % entriesPerPage != 0) {
      throw new IllegalArgumentException(entry + " is not a multiple of " + entriesPerPage);
    }

    while (true) {
      boolean canAllocate = false;
      if (pageCount.incrementAndGet() <= pageLimit) {
        canAllocate = true;
      } else {
        pageCount.decrementAndGet();
      }

      if (canAllocate) {
        LedgerEntryPage lep = new LedgerEntryPage(pageSize, entriesPerPage, pageMapAndList);
        lep.setLedgerAndFirstEntry(ledger, entry);
        lep.usePage();
        return lep;
      }

      LedgerEntryPage lep = pageMapAndList.grabCleanPage(ledger, entry);
      if (null != lep) {
        return lep;
      }
      LOG.info(
          "Could not grab a clean page for ledger {}, entry {}, force flushing dirty ledgers.",
          ledger,
          entry);
      flushOneOrMoreLedgers(false);
    }
  }
Example #4
0
 /**
  * Add the LedgerEntryPage to the clean page LRU map
  *
  * @param lep Ledger Entry Page object
  */
 private void addToCleanPagesList(LedgerEntryPage lep) {
   synchronized (lruCleanPageMap) {
     if (lep.isClean() && !lep.inUse()) {
       lruCleanPageMap.put(lep.getEntryKey(), lep);
     }
   }
 }
Example #5
0
 /**
  * Remove the LedgerEntryPage from the clean page LRU map
  *
  * @param lep Ledger Entry Page object
  */
 private void removeFromCleanPageList(LedgerEntryPage lep) {
   synchronized (lruCleanPageMap) {
     if (!lep.isClean() || lep.inUse()) {
       lruCleanPageMap.remove(lep.getEntryKey());
     }
   }
 }
Example #6
0
 LedgerEntryPage getLedgerEntryPage(Long ledger, Long firstEntry, boolean onlyDirty) {
   LedgerEntryPage lep = pageMapAndList.getPage(ledger, firstEntry);
   if (onlyDirty && null != lep && lep.isClean()) {
     return null;
   }
   if (null != lep) {
     lep.usePage();
   }
   return lep;
 }
  void flushLedgerEntries(long l, List<LedgerEntryPage> entries) throws IOException {
    FileInfo fi = null;
    try {
      Collections.sort(
          entries,
          new Comparator<LedgerEntryPage>() {
            @Override
            public int compare(LedgerEntryPage o1, LedgerEntryPage o2) {
              return (int) (o1.getFirstEntry() - o2.getFirstEntry());
            }
          });
      int[] versions = new int[entries.size()];
      try {
        fi = getFileInfo(l, null);
      } catch (Bookie.NoLedgerException nle) {
        // ledger has been deleted
        LOG.info("No ledger {} found when flushing entries.", l);
        return;
      }

      // flush the header if necessary
      relocateIndexFileAndFlushHeader(l, fi);
      int start = 0;
      long lastOffset = -1;
      for (int i = 0; i < entries.size(); i++) {
        versions[i] = entries.get(i).getVersion();
        if (lastOffset != -1 && (entries.get(i).getFirstEntry() - lastOffset) != entriesPerPage) {
          // send up a sequential list
          int count = i - start;
          if (count == 0) {
            LOG.warn("Count cannot possibly be zero!");
          }
          writeBuffers(l, entries, fi, start, count);
          start = i;
        }
        lastOffset = entries.get(i).getFirstEntry();
      }
      if (entries.size() - start == 0 && entries.size() != 0) {
        LOG.warn("Nothing to write, but there were entries!");
      }
      writeBuffers(l, entries, fi, start, entries.size() - start);
      for (int i = 0; i < entries.size(); i++) {
        LedgerEntryPage lep = entries.get(i);
        lep.setClean(versions[i]);
      }
      if (LOG.isDebugEnabled()) {
        LOG.debug("Flushed ledger {} with {} pages.", l, entries.size());
      }
    } finally {
      if (fi != null) {
        fi.release();
      }
    }
  }
Example #8
0
 void putEntryOffset(long ledger, long entry, long offset) throws IOException {
   int offsetInPage = (int) (entry % entriesPerPage);
   // find the id of the first entry of the page that has the entry
   // we are looking for
   long pageEntry = entry - offsetInPage;
   LedgerEntryPage lep = getLedgerEntryPage(ledger, pageEntry, false);
   if (lep == null) {
     lep = grabLedgerEntryPage(ledger, pageEntry);
   }
   assert lep != null;
   lep.setOffset(offset, offsetInPage * LedgerEntryPage.getIndexEntrySize());
   lep.releasePage();
 }
Example #9
0
 long getEntryOffset(long ledger, long entry) throws IOException {
   int offsetInPage = (int) (entry % entriesPerPage);
   // find the id of the first entry of the page that has the entry
   // we are looking for
   long pageEntry = entry - offsetInPage;
   LedgerEntryPage lep = getLedgerEntryPage(ledger, pageEntry, false);
   try {
     if (lep == null) {
       lep = grabLedgerEntryPage(ledger, pageEntry);
     }
     return lep.getOffset(offsetInPage * LedgerEntryPage.getIndexEntrySize());
   } finally {
     if (lep != null) {
       lep.releasePage();
     }
   }
 }
Example #10
0
    /**
     * Get a clean page and provision it for the specified ledger and firstEntry within the ledger
     *
     * @param ledgerId Ledger id
     * @param firstEntry Id of the first entry in the page
     * @returns LedgerEntryPage if present
     */
    LedgerEntryPage grabCleanPage(long ledgerId, long firstEntry) {
      LedgerEntryPage lep = null;
      while (lruCleanPageMap.size() > 0) {
        lep = null;
        synchronized (lruCleanPageMap) {
          Iterator<Map.Entry<EntryKey, LedgerEntryPage>> iterator =
              lruCleanPageMap.entrySet().iterator();

          Map.Entry<EntryKey, LedgerEntryPage> entry = null;
          while (iterator.hasNext()) {
            entry = iterator.next();
            iterator.remove();
            if (entry.getValue().isClean() && !entry.getValue().inUse()) {
              lep = entry.getValue();
              break;
            }
          }

          if (null == lep) {
            LOG.debug("Did not find eligible page in the first pass");
            return null;
          }
        }

        // We found a candidate page, lets see if we can reclaim it before its re-used
        ConcurrentMap<Long, LedgerEntryPage> pageMap = pages.get(lep.getLedger());
        // Remove from map only if nothing has changed since we checked this lep.
        // Its possible for the ledger to have been deleted or the page to have already
        // been reclaimed. The page map is the definitive source of information, if anything
        // has changed we should leave this page along and continue iterating to find
        // another suitable page.
        if ((null != pageMap) && (pageMap.remove(lep.getFirstEntry(), lep))) {
          if (!lep.isClean()) {
            // Someone wrote to this page while we were reclaiming it.
            pageMap.put(lep.getFirstEntry(), lep);
            lep = null;
          } else {
            // Do some bookkeeping on the page table
            pages.remove(lep.getLedger(), EMPTY_PAGE_MAP);
            // We can now safely reset this lep and return it.
            lep.usePage();
            lep.zeroPage();
            lep.setLedgerAndFirstEntry(ledgerId, firstEntry);
            return lep;
          }
        } else {
          lep = null;
        }
      }
      return lep;
    }
Example #11
0
 /**
  * Traverse the pages for a given ledger in memory and find the highest entry amongst these
  * pages
  *
  * @param ledgerId Ledger id
  * @returns last entry in the in memory pages
  */
 private long getLastEntryInMem(long ledgerId) {
   long lastEntry = 0;
   // Find the last entry in the cache
   ConcurrentMap<Long, LedgerEntryPage> map = pages.get(ledgerId);
   if (map != null) {
     for (LedgerEntryPage lep : map.values()) {
       if (lep.getMaxPossibleEntry() < lastEntry) {
         continue;
       }
       lep.usePage();
       long highest = lep.getLastEntry();
       if (highest > lastEntry) {
         lastEntry = highest;
       }
       lep.releasePage();
     }
   }
   return lastEntry;
 }
Example #12
0
 void updatePage(LedgerEntryPage lep) throws IOException {
   if (!lep.isClean()) {
     throw new IOException("Trying to update a dirty page");
   }
   FileInfo fi = null;
   try {
     fi = getFileInfo(lep.getLedger(), null);
     long pos = lep.getFirstEntryPosition();
     if (pos >= fi.size()) {
       lep.zeroPage();
     } else {
       lep.readPage(fi);
     }
   } finally {
     if (fi != null) {
       fi.release();
     }
   }
 }
Example #13
0
 /**
  * Add a LedgerEntryPage to the page map
  *
  * @param lep Ledger Entry Page object
  */
 private LedgerEntryPage putPage(LedgerEntryPage lep) {
   // Do a get here to avoid too many new ConcurrentHashMaps() as putIntoTable is called
   // frequently.
   ConcurrentMap<Long, LedgerEntryPage> map = pages.get(lep.getLedger());
   if (null == map) {
     ConcurrentMap<Long, LedgerEntryPage> mapToPut =
         new ConcurrentHashMap<Long, LedgerEntryPage>();
     map = pages.putIfAbsent(lep.getLedger(), mapToPut);
     if (null == map) {
       map = mapToPut;
     }
   }
   LedgerEntryPage oldPage = map.putIfAbsent(lep.getFirstEntry(), lep);
   if (null == oldPage) {
     oldPage = lep;
     // Also include this in the clean page map if it qualifies.
     // Note: This is done for symmetry and correctness, however it should never
     // get exercised since we shouldn't attempt a put without the page being in use
     addToCleanPagesList(lep);
   }
   return oldPage;
 }
Example #14
0
    /**
     * Gets the list of pages in memory that have been changed and hence need to be written as a
     * part of the flush operation that is being issued
     *
     * @param ledgerId Ledger id
     * @returns last entry in the in memory pages.
     */
    private LinkedList<Long> getFirstEntryListToBeFlushed(long ledgerId) {
      ConcurrentMap<Long, LedgerEntryPage> pageMap = pages.get(ledgerId);
      if (pageMap == null || pageMap.isEmpty()) {
        return null;
      }

      LinkedList<Long> firstEntryList = new LinkedList<Long>();
      for (ConcurrentMap.Entry<Long, LedgerEntryPage> entry : pageMap.entrySet()) {
        LedgerEntryPage lep = entry.getValue();
        if (lep.isClean()) {
          if (!lep.inUse()) {
            addToCleanPagesList(lep);
          }
          if (LOG.isTraceEnabled()) {
            LOG.trace("Page is clean " + lep);
          }
        } else {
          firstEntryList.add(lep.getFirstEntry());
        }
      }
      return firstEntryList;
    }
Example #15
0
 long getPersistEntryBeyondInMem(long ledgerId, long lastEntryInMem) throws IOException {
   FileInfo fi = null;
   long lastEntry = lastEntryInMem;
   try {
     fi = getFileInfo(ledgerId, null);
     long size = fi.size();
     // make sure the file size is aligned with index entry size
     // otherwise we may read incorret data
     if (0 != size % LedgerEntryPage.getIndexEntrySize()) {
       LOG.warn("Index file of ledger {} is not aligned with index entry size.", ledgerId);
       size = size - size % LedgerEntryPage.getIndexEntrySize();
     }
     // we may not have the last entry in the cache
     if (size > lastEntry * LedgerEntryPage.getIndexEntrySize()) {
       ByteBuffer bb = ByteBuffer.allocate(pageSize);
       long position = size - pageSize;
       if (position < 0) {
         position = 0;
       }
       fi.read(bb, position);
       bb.flip();
       long startingEntryId = position / LedgerEntryPage.getIndexEntrySize();
       for (int i = entriesPerPage - 1; i >= 0; i--) {
         if (bb.getLong(i * LedgerEntryPage.getIndexEntrySize()) != 0) {
           if (lastEntry < startingEntryId + i) {
             lastEntry = startingEntryId + i;
           }
           break;
         }
       }
     }
   } finally {
     if (fi != null) {
       fi.release();
     }
   }
   return lastEntry;
 }