Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java (revision 1616440) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java (working copy) @@ -422,6 +422,27 @@ MILLISECONDS.convert(System.nanoTime() - start, NANOSECONDS)); } + private boolean compactNeeded() { + Set ids = newHashSet(); + for (SegmentId id : tracker.getReferencedSegmentIds()) { + ids.add(new UUID(id.getMostSignificantBits(), id + .getLeastSignificantBits())); + } + try { + writer.cleanup(ids); + for (TarReader reader : readers) { + if (reader.cleanupNeeded(ids)) { + log.info("{} needs compaction.", reader); + return true; + } + } + } catch (IOException e) { + log.error("Compaction triggered by {}", e.getMessage(), e); + return true; + } + return false; + } + /** * Copy every referenced record in data (non-bulk) segments. Bulk segments * are fully kept (they are only removed in cleanup, if there is no @@ -431,6 +452,11 @@ long start = System.nanoTime(); log.info("TarMK compaction started"); + if (!compactNeeded()) { + log.info("TarMK compaction not needed."); + return; + } + SegmentWriter writer = new SegmentWriter(this, tracker); Compactor compactor = new Compactor(writer); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarReader.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarReader.java (revision 1616440) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarReader.java (working copy) @@ -690,6 +690,73 @@ } } + synchronized boolean cleanupNeeded(Set referencedIds) throws IOException { + Map> graph = null; + if (this.graph != null) { + graph = parseGraph(); + } + + TarEntry[] sorted = new TarEntry[index.remaining() / 24]; + int position = index.position(); + for (int i = 0; position < index.limit(); i++) { + sorted[i] = new TarEntry( + index.getLong(position), + index.getLong(position + 8), + index.getInt(position + 16), + index.getInt(position + 20)); + position += 24; + } + Arrays.sort(sorted, TarEntry.OFFSET_ORDER); + + int size = 0; + int count = 0; + for (int i = sorted.length - 1; i >= 0; i--) { + TarEntry entry = sorted[i]; + UUID id = new UUID(entry.msb(), entry.lsb()); + if (!referencedIds.remove(id)) { + // this segment is not referenced anywhere + sorted[i] = null; + } else { + size += getEntrySize(entry.size()); + count += 1; + + if (isDataSegmentId(entry.lsb())) { + // this is a referenced data segment, so follow the graph + if (graph != null) { + List refids = graph.get( + new UUID(entry.msb(), entry.lsb())); + if (refids != null) { + referencedIds.addAll(refids); + } + } else { + // a pre-compiled graph is not available, so read the + // references directly from this segment + ByteBuffer segment = access.read( + entry.offset(), + Math.min(entry.size(), 16 * 256)); + int pos = segment.position(); + int refcount = segment.get(pos + REF_COUNT_OFFSET) & 0xff; + int refend = pos + 16 * (refcount + 1); + for (int refpos = pos + 16; refpos < refend; refpos += 16) { + referencedIds.add(new UUID( + segment.getLong(refpos), + segment.getLong(refpos + 8))); + } + } + } + } + } + size += getEntrySize(24 * count + 16); + size += 2 * BLOCK_SIZE; + if (count == 0) { + // none of the entries within this tar file are referenceable + return true; + } else if (size >= access.length() * 3 / 4 && graph != null) { + return false; + } + return true; + } + File close() throws IOException { access.close(); return file;