@Internal
  public void compact() throws IOException {
    if (!isCompactionSupported()) throw new IncorrectOperationException();
    getWriteLock().lock();
    try {
      force();
      LOG.info("Compacting " + myEnumerator.myFile);
      LOG.info("Live keys:" + (int)(myLiveAndGarbageKeysCounter / LIVE_KEY_MASK) +
               ", dead keys:" + (int)(myLiveAndGarbageKeysCounter & DEAD_KEY_NUMBER_MASK) +
               ", read compaction size:" + myReadCompactionGarbageSize);

      final long now = System.currentTimeMillis();

      Path oldDataFile = getDataFile(myEnumerator.myFile);
      final File[] oldFiles = getFilesInDirectoryWithNameStartingWith(oldDataFile);

      final Path newPath = oldDataFile.resolveSibling(oldDataFile.getFileName() + ".new");
      CreationTimeOptions options = myValueStorage.getOptions();
      final PersistentHashMapValueStorage newStorage = new PersistentHashMapValueStorage(newPath, options);
      myValueStorage.switchToCompactionMode();
      myEnumerator.markDirty(true);
      long sizeBefore = myValueStorage.getSize();

      myLiveAndGarbageKeysCounter = 0;
      myReadCompactionGarbageSize = 0;

      try {
        if (doNewCompact()) {
          newCompact(newStorage);
        }
        else {
          myEnumerator.traverseAllRecords(new PersistentEnumeratorBase.RecordsProcessor() {
            @Override
            public boolean process(final int keyId) throws IOException {
              final long record = readValueId(keyId);
              if (record != NULL_ADDR) {
                PersistentHashMapValueStorage.ReadResult readResult = myValueStorage.readBytes(record);
                long value = newStorage.appendBytes(readResult.buffer, 0, readResult.buffer.length, 0);
                updateValueId(keyId, value, record, null, getCurrentKey());
                myLiveAndGarbageKeysCounter += LIVE_KEY_MASK;
              }
              return true;
            }
          });
        }
      }
      finally {
        newStorage.dispose();
      }

      myValueStorage.dispose();

      for (File f : oldFiles) {
        assert FileUtil.deleteWithRenaming(f);
      }

      final long newSize = newStorage.getSize();

      final File[] newFiles = getFilesInDirectoryWithNameStartingWith(newPath);

      // newFiles should get the same names as oldDataFiles
      File parentFile = newPath.getParent().toFile();
      final String newBaseName = newPath.getFileName().toString();
      final String oldDataFileBaseName = oldDataFile.getFileName().toString();
      for (File f : newFiles) {
        String nameAfterRename = StringUtil.replace(f.getName(), newBaseName, oldDataFileBaseName);
        FileUtil.rename(f, new File(parentFile, nameAfterRename));
      }

      myValueStorage = new PersistentHashMapValueStorage(oldDataFile, options);
      LOG.info("Compacted " + myEnumerator.myFile + ":" + sizeBefore + " bytes into " +
               newSize + " bytes in " + (System.currentTimeMillis() - now) + "ms.");
      myEnumerator.putMetaData(myLiveAndGarbageKeysCounter);
      myEnumerator.putMetaData2(myLargeIndexWatermarkId);
      if (myDoTrace) LOG.assertTrue(myEnumerator.isDirty());
    }
    finally {
      getWriteLock().unlock();
    }
  }
@Internal
  public void compact()