blob: 49a9db6a5c98054c224cd4409da6cf880889bfe5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.jackrabbit.oak.plugins.blob;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.System.nanoTime;
import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount;
import static org.apache.jackrabbit.oak.commons.jmx.ManagementOperation.Status.formatTime;
import static org.apache.jackrabbit.oak.commons.jmx.ManagementOperation.done;
import static org.apache.jackrabbit.oak.commons.jmx.ManagementOperation.newManagementOperation;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularData;
import javax.management.openmbean.TabularDataSupport;
import javax.management.openmbean.TabularType;
import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
import org.apache.jackrabbit.oak.commons.jmx.ManagementOperation;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of {@link BlobGCMBean} based on a {@link BlobGarbageCollector}.
*/
public class BlobGC extends AnnotatedStandardMBean implements BlobGCMBean {
private static final Logger log = LoggerFactory.getLogger(BlobGC.class);
public static final String OP_NAME = "Blob garbage collection";
private final BlobGarbageCollector blobGarbageCollector;
private final Executor executor;
private ManagementOperation<String> gcOp = done(OP_NAME, "");
public static final String CONSISTENCY_OP_NAME = "Blob consistency check";
private ManagementOperation<String> consistencyOp = done(CONSISTENCY_OP_NAME, "");
/**
* @param blobGarbageCollector Blob garbage collector
* @param executor executor for running the garbage collection task
*/
public BlobGC(
@NotNull BlobGarbageCollector blobGarbageCollector,
@NotNull Executor executor) {
super(BlobGCMBean.class);
this.blobGarbageCollector = checkNotNull(blobGarbageCollector);
this.executor = checkNotNull(executor);
}
@NotNull
@Override
public CompositeData startBlobGC(final boolean markOnly) {
if (gcOp.isDone()) {
gcOp = newManagementOperation(OP_NAME, new Callable<String>() {
@Override
public String call() throws Exception {
long t0 = nanoTime();
blobGarbageCollector.collectGarbage(markOnly);
return "Blob gc completed in " + formatTime(nanoTime() - t0);
}
});
executor.execute(gcOp);
}
return getBlobGCStatus();
}
@Override
public CompositeData startBlobGC(final boolean markOnly, final boolean forceBlobIdRetrieve) {
if (gcOp.isDone()) {
gcOp = newManagementOperation(OP_NAME, new Callable<String>() {
@Override
public String call() throws Exception {
long t0 = nanoTime();
blobGarbageCollector.collectGarbage(markOnly, forceBlobIdRetrieve);
return "Blob gc completed in " + formatTime(nanoTime() - t0);
}
});
executor.execute(gcOp);
}
return getBlobGCStatus();
}
@NotNull
@Override
public CompositeData getBlobGCStatus() {
return gcOp.getStatus().toCompositeData();
}
@Override
public CompositeData checkConsistency() {
if (consistencyOp.isDone()) {
consistencyOp = newManagementOperation(CONSISTENCY_OP_NAME, new Callable<String>() {
@Override
public String call() throws Exception {
long t0 = nanoTime();
long missing = blobGarbageCollector.checkConsistency();
return "Consistency check completed in "
+ formatTime(nanoTime() - t0) + ". " +
+ missing + " missing blobs found (details in the log).";
}
});
executor.execute(consistencyOp);
}
return getConsistencyCheckStatus();
}
@NotNull
@Override
public CompositeData getConsistencyCheckStatus() {
return consistencyOp.getStatus().toCompositeData();
}
@Override
public TabularData getGlobalMarkStats() {
TabularDataSupport tds;
try {
TabularType tt = new TabularType(BlobGC.class.getName(),
"Garbage collection global mark phase Stats", MARK_TYPE,
new String[] {"repositoryId"});
tds = new TabularDataSupport(tt);
List<GarbageCollectionRepoStats> stats = blobGarbageCollector.getStats();
for (GarbageCollectionRepoStats stat : stats) {
tds.put(toCompositeData(stat));
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return tds;
}
private CompositeDataSupport toCompositeData(GarbageCollectionRepoStats statObj) throws OpenDataException {
Object[] values = new Object[] {
statObj.getRepositoryId() + (statObj.isLocal() ? " *" : ""),
(statObj.getStartTime() == 0 ? "" : (new Date(statObj.getStartTime()))).toString(),
(statObj.getEndTime() == 0 ? "" : (new Date(statObj.getEndTime()))).toString(),
statObj.getLength(),
humanReadableByteCount(statObj.getLength()),
statObj.getNumLines()
};
return new CompositeDataSupport(MARK_TYPE, MARK_FIELD_NAMES, values);
}
private static final String[] MARK_FIELD_NAMES = new String[] {
"repositoryId",
"markStartTime",
"markEndTime",
"referenceFileSizeBytes",
"referencesFileSize",
"numReferences",
};
private static final String[] MARK_FIELD_DESCRIPTIONS = new String[] {
"Repository ID",
"Start time of mark",
"End time of mark",
"References file size in bytes",
"References file size in human readable format",
"Number of references"
};
private static final OpenType[] MARK_FIELD_TYPES = new OpenType[] {
SimpleType.STRING,
SimpleType.STRING,
SimpleType.STRING,
SimpleType.LONG,
SimpleType.STRING,
SimpleType.INTEGER
};
private static final CompositeType MARK_TYPE = createMarkCompositeType();
private static CompositeType createMarkCompositeType() {
try {
return new CompositeType(
GarbageCollectionRepoStats.class.getName(),
"Composite data type for datastore GC statistics", MARK_FIELD_NAMES, MARK_FIELD_DESCRIPTIONS,
MARK_FIELD_TYPES);
} catch (OpenDataException e) {
throw new IllegalStateException(e);
}
}
@Override
public TabularData getOperationStats() {
TabularDataSupport tds;
try {
TabularType tt = new TabularType(BlobGC.class.getName(),
"Garbage Collection Operation Stats", OP_STATS_TYPE,
OP_STATS_FIELD_NAMES);
tds = new TabularDataSupport(tt);
OperationsStatsMBean operationStats = blobGarbageCollector.getOperationStats();
tds.put(toCompositeData(operationStats));
} catch (Exception e) {
throw new IllegalStateException(e);
}
return tds;
}
private CompositeData toCompositeData(OperationsStatsMBean statObj) throws OpenDataException {
Object[] values = new Object[] {
statObj.getStartCount(),
statObj.getFailureCount(),
statObj.duration()
};
return new CompositeDataSupport(OP_STATS_TYPE, OP_STATS_FIELD_NAMES, values);
}
private static final String[] OP_STATS_FIELD_NAMES = new String[] {
"count",
"failureCount",
"duration"
};
private static final String[] OP_STATS_FIELD_DESCRIPTIONS = new String[] {
"Count",
"Failure Count",
"Duration"
};
private static final OpenType[] OP_STATS_FIELD_TYPES = new OpenType[] {
SimpleType.LONG,
SimpleType.LONG,
SimpleType.LONG
};
private static final CompositeType OP_STATS_TYPE = createOpStatsCompositeType();
private static CompositeType createOpStatsCompositeType() {
try {
return new CompositeType(
GarbageCollectionRepoStats.class.getName(),
"Composite data type for datastore GC operation stats", OP_STATS_FIELD_NAMES, OP_STATS_FIELD_DESCRIPTIONS,
OP_STATS_FIELD_TYPES);
} catch (OpenDataException e) {
throw new IllegalStateException(e);
}
}
}