diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
index 1809a8c0c6..7cb17dd959 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
@@ -16,40 +16,61 @@
*/
package org.apache.jackrabbit.oak.segment;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework;
+import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_STRING_CACHE_MB;
+import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_TEMPLATE_CACHE_MB;
+import static org.apache.jackrabbit.oak.segment.SegmentCache.DEFAULT_SEGMENT_CACHE_MB;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_NODE_CACHE_SIZE_OSGi;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_STRING_CACHE_SIZE_OSGi;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_TEMPLATE_CACHE_SIZE_OSGi;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.DISABLE_ESTIMATION_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.FORCE_TIMEOUT_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GC_PROGRESS_LOG_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.MEMORY_THRESHOLD_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.PAUSE_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETAINED_GENERATIONS_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETRY_COUNT_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.SIZE_DELTA_ESTIMATION_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.DEFAULT_MAX_FILE_SIZE;
import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET;
import java.io.Closeable;
+import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.google.common.io.Closer;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.ReferencePolicy;
-import org.apache.felix.scr.annotations.ReferencePolicyOption;
import org.apache.jackrabbit.api.stats.RepositoryStatistics;
import org.apache.jackrabbit.api.stats.TimeSeries;
-import org.apache.jackrabbit.oak.commons.IOUtils;
+import org.apache.jackrabbit.oak.osgi.OsgiUtil;
import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
import org.apache.jackrabbit.oak.spi.blob.BlobStore;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.oak.spi.state.NodeStoreProvider;
import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
import org.apache.jackrabbit.oak.stats.CounterStats;
import org.apache.jackrabbit.oak.stats.HistogramStats;
import org.apache.jackrabbit.oak.stats.MeterStats;
import org.apache.jackrabbit.oak.stats.StatisticsProvider;
import org.apache.jackrabbit.oak.stats.StatsOptions;
import org.apache.jackrabbit.oak.stats.TimerStats;
+import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -58,111 +79,272 @@ import org.slf4j.LoggerFactory;
*
* The different secondaries are distinguished by their role attribute.
*/
-@Component(policy = ConfigurationPolicy.REQUIRE,
- name="org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory",
- configurationFactory=true,
- metatype = true,
- label = "Apache Jackrabbit Oak Segment-Tar NodeStore Factory",
- description = "Factory allowing configuration of adjacent instances of " +
- "NodeStore implementation based on Segment model besides a default SegmentNodeStore in same setup."
+@Component(
+ configurationPolicy = ConfigurationPolicy.REQUIRE,
+ factory = "org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory"
)
+@Designate(factory = true, ocd = SegmentNodeStoreFactory.Configuration.class)
public class SegmentNodeStoreFactory {
- @Property(
- label = "Role",
- description="As multiple SegmentNodeStores can be configured, this parameter defines the role " +
- "of 'this' SegmentNodeStore."
- )
- public static final String ROLE = "role";
-
- @Property(boolValue = false,
- label = "Custom BlobStore",
- description = "Boolean value indicating that a custom BlobStore is to be used. " +
- "By default large binary content would be stored within segment tar files"
- )
- public static final String CUSTOM_BLOB_STORE = "customBlobStore";
+ private final Logger log = LoggerFactory.getLogger(getClass());
- @Property(boolValue = false,
- label = "Custom segment store",
- description = "Boolean value indicating that a custom (non-tar) segment store is used"
- )
- public static final String CUSTOM_SEGMENT_STORE = "customSegmentStore";
+ private static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60;
- @Property(boolValue = false,
- label = "Register JCR descriptors as OSGi services",
- description="Should only be done for one factory instance")
- public static final String REGISTER_DESCRIPTORS = "registerDescriptors";
+ private static final long DEFAULT_BLOB_GC_MAX_AGE = 24 * 60 * 60;
- private final Logger log = LoggerFactory.getLogger(getClass());
+ @ObjectClassDefinition(
+ name = "Apache Jackrabbit Oak Segment-Tar NodeStore Factory",
+ description = "Factory allowing configuration of adjacent instances of " +
+ "NodeStore implementation based on Segment model besides a default SegmentNodeStore in same setup."
+ )
+ @interface Configuration {
+
+ @AttributeDefinition(
+ name = "Repository Home Directory",
+ description = "Path on the file system where repository data will be stored. "
+ + "Defaults to the value of the framework property 'repository.home' or to 'repository' "
+ + "if that is neither specified."
+ )
+ String repository_home() default "";
+
+ @AttributeDefinition(
+ name = "Mode",
+ description = "TarMK mode (64 for memory mapped file access, 32 for normal file access). " +
+ "Default value is taken from the 'sun.arch.data.model' system property."
+ )
+ String tarmk_mode() default "";
+
+ @AttributeDefinition(
+ name = "Maximum tar file size (MB)",
+ description = "The maximum size of the tar files in megabytes. " +
+ "Default value is '" + DEFAULT_MAX_FILE_SIZE + "'."
+ )
+ int tarmk_size() default DEFAULT_MAX_FILE_SIZE;
+
+ @AttributeDefinition(
+ name = "Segment cache size (MB)",
+ description = "Cache size for storing most recently used segments in megabytes. " +
+ "Default value is '" + DEFAULT_SEGMENT_CACHE_MB + "'."
+ )
+ int segmentCache_size() default DEFAULT_SEGMENT_CACHE_MB;
+
+ @AttributeDefinition(
+ name = "String cache size (MB)",
+ description = "Cache size for storing most recently used strings in megabytes. " +
+ "Default value is '" + DEFAULT_STRING_CACHE_MB + "'."
+ )
+ int stringCache_size() default DEFAULT_STRING_CACHE_MB;
+
+ @AttributeDefinition(
+ name = "Template cache size (MB)",
+ description = "Cache size for storing most recently used templates in megabytes. " +
+ "Default value is '" + DEFAULT_TEMPLATE_CACHE_MB + "'."
+ )
+ int templateCache_size() default DEFAULT_TEMPLATE_CACHE_MB;
+
+ @AttributeDefinition(
+ name = "String deduplication cache size (#items)",
+ description = "Maximum number of strings to keep in the deduplication cache. " +
+ "Default value is '" + DEFAULT_STRING_CACHE_SIZE_OSGi + "'."
+ )
+ int stringDeduplicationCache_size() default DEFAULT_STRING_CACHE_SIZE_OSGi;
+
+ @AttributeDefinition(
+ name = "Template deduplication cache size (#items)",
+ description = "Maximum number of templates to keep in the deduplication cache. " +
+ "Default value is '" + DEFAULT_TEMPLATE_CACHE_SIZE_OSGi + "'."
+ )
+ int templateDeduplicationCache_size() default DEFAULT_TEMPLATE_CACHE_SIZE_OSGi;
+
+ @AttributeDefinition(
+ name = "Node deduplication cache size (#items)",
+ description = "Maximum number of node to keep in the deduplication cache. If the supplied " +
+ "value is not a power of 2, it will be rounded up to the next power of 2. " +
+ "Default value is '" + DEFAULT_NODE_CACHE_SIZE_OSGi + "'."
+ )
+ int nodeDeduplicationCache_size() default DEFAULT_NODE_CACHE_SIZE_OSGi;
+
+ @AttributeDefinition(
+ name = "Pause compaction",
+ description = "When set to true the compaction phase is skipped during garbage collection. " +
+ "Default value is '" + PAUSE_DEFAULT + "'."
+ )
+ boolean pauseCompaction() default PAUSE_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Compaction retries",
+ description = "Number of tries to compact concurrent commits on top of already " +
+ "compacted commits. " +
+ "Default value is '" + RETRY_COUNT_DEFAULT + "'."
+ )
+ int compaction_retryCount() default RETRY_COUNT_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Force compaction timeout",
+ description = "Number of seconds to attempt to force compact concurrent commits on top " +
+ "of already compacted commits after the maximum number of retries has been " +
+ "reached. Forced compaction tries to acquire an exclusive write lock on the " +
+ "node store, blocking concurrent write access as long as the lock is held. " +
+ "Default value is '" + FORCE_TIMEOUT_DEFAULT + "'."
+ )
+ int compaction_force_timeout() default FORCE_TIMEOUT_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Garbage collection repository size threshold",
+ description = "Garbage collection will be skipped unless the repository grew at least by " +
+ "the number of bytes specified. " +
+ "Default value is '" + SIZE_DELTA_ESTIMATION_DEFAULT + "'."
+ )
+ long compaction_sizeDeltaEstimation() default SIZE_DELTA_ESTIMATION_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Disable estimation phase",
+ description = "Disables the estimation phase allowing garbage collection to run unconditionally. " +
+ "Default value is '" + DISABLE_ESTIMATION_DEFAULT + "'."
+ )
+ boolean compaction_disableEstimation() default DISABLE_ESTIMATION_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Compaction retained generations",
+ description = "Number of segment generations to retain during garbage collection. " +
+ "The number of generations defaults to " + RETAINED_GENERATIONS_DEFAULT + " and " +
+ "can't be changed. This configuration option is considered deprecated " +
+ "and will be removed in the future."
+ )
+ int compaction_retainedGenerations() default RETAINED_GENERATIONS_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Compaction memory threshold",
+ description = "Threshold of available heap memory in percent of total heap memory below " +
+ "which the compaction phase is canceled. 0 disables heap memory monitoring. " +
+ "Default value is '" + MEMORY_THRESHOLD_DEFAULT + "'."
+ )
+ int compaction_memoryThreshold() default MEMORY_THRESHOLD_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Compaction progress log",
+ description = "The number of nodes compacted after which a status message is logged. " +
+ "-1 disables progress logging. " +
+ "Default value is '" + GC_PROGRESS_LOG_DEFAULT + "'."
+ )
+ long compaction_progressLog() default GC_PROGRESS_LOG_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Standby mode",
+ description = "Flag indicating this component will not register as a NodeStore but as a " +
+ "NodeStoreProvider instead. " +
+ "Default value is 'false'."
+ )
+ boolean standby() default false;
+
+ @AttributeDefinition(
+ name = "Custom blob store",
+ description = "Boolean value indicating that a custom BlobStore is used for storing " +
+ "large binary values."
+ )
+ boolean customBlobStore() default false;
+
+ @AttributeDefinition(
+ name = "Custom segment store",
+ description = "Boolean value indicating that a custom (non-tar) segment store is used"
+ )
+ boolean customSegmentStore() default false;
+
+ @AttributeDefinition(
+ name = "Backup directory",
+ description = "Directory (relative to current working directory) for storing repository backups. " +
+ "Defaults to 'repository.home/segmentstore-backup'."
+ )
+ String repository_backup_dir() default "";
+
+ @AttributeDefinition(
+ name = "Blob gc max age (in secs)",
+ description = "The blob garbage collection logic will only consider those blobs which " +
+ "are not accessed recently (currentTime - lastModifiedTime > blobGcMaxAgeInSecs). " +
+ "For example with the default setting only those blobs which have been created " +
+ "at least 24 hours ago will be considered for garbage collection. " +
+ "Default value is '" + DEFAULT_BLOB_GC_MAX_AGE + "'."
+ )
+ long blobGcMaxAgeInSecs() default DEFAULT_BLOB_GC_MAX_AGE;
+
+ @AttributeDefinition(
+ name = "Blob tracking snapshot interval",
+ description = "Interval in seconds in which snapshots of locally tracked blob ids are " +
+ "taken and synchronized with the blob store. This should be configured to be " +
+ "less than the frequency of blob garbage collection so that deletions during blob " +
+ "garbage collection can be accounted for in the next garbage collection execution. " +
+ "Default value is '" + DEFAULT_BLOB_SNAPSHOT_INTERVAL + "'."
+ )
+ long blobTrackSnapshotIntervalInSecs() default DEFAULT_BLOB_SNAPSHOT_INTERVAL;
+
+ @AttributeDefinition(
+ name = "Role",
+ description = "As multiple SegmentNodeStores can be configured, this parameter defines the role " +
+ "of 'this' SegmentNodeStore."
+ )
+ String role() default "";
+
+ @AttributeDefinition(
+ name = "Register JCR descriptors as OSGi services",
+ description = "Should only be done for one factory instance"
+ )
+ boolean registerDescriptors() default false;
+ }
@Reference(
- cardinality = ReferenceCardinality.OPTIONAL_UNARY,
- policy = ReferencePolicy.STATIC,
- policyOption = ReferencePolicyOption.GREEDY,
- target = ONLY_STANDALONE_TARGET
+ cardinality = ReferenceCardinality.OPTIONAL,
+ policy = ReferencePolicy.STATIC,
+ policyOption = ReferencePolicyOption.GREEDY,
+ target = ONLY_STANDALONE_TARGET
)
private volatile BlobStore blobStore;
@Reference(
- cardinality = ReferenceCardinality.OPTIONAL_UNARY,
- policy = ReferencePolicy.STATIC,
- policyOption = ReferencePolicyOption.GREEDY
+ cardinality = ReferenceCardinality.OPTIONAL,
+ policy = ReferencePolicy.STATIC,
+ policyOption = ReferencePolicyOption.GREEDY
)
private volatile SegmentNodeStorePersistence segmentStore;
@Reference
private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
- private Closer registrations = Closer.create();
+ private final Closer registrations = Closer.create();
@Activate
- public void activate(ComponentContext context) throws IOException {
- String role = property(ROLE, context);
- // In secondaryNodeStore mode customBlobStore is always enabled
- boolean isSecondaryStoreMode = "secondary".equals(role);
- boolean customBlobStore = Boolean.parseBoolean(property(CUSTOM_BLOB_STORE, context)) || isSecondaryStoreMode;
- boolean customSegmentStore = Boolean.parseBoolean(property(CUSTOM_SEGMENT_STORE, context));
- boolean registerRepositoryDescriptors = Boolean.parseBoolean(property(REGISTER_DESCRIPTORS, context));
- log.info("activate: SegmentNodeStore '" + role + "' starting.");
-
- if (blobStore == null && customBlobStore) {
- log.info("BlobStore use enabled. SegmentNodeStore would be initialized when BlobStore would be available");
+ public void activate(ComponentContext context, Configuration configuration) throws IOException {
+ log.info("activate: SegmentNodeStore '" + configuration.role() + "' starting.");
+
+ if (configuration.role().isEmpty()) {
return;
}
- if (segmentStore == null && customSegmentStore) {
- log.info("customSegmentStore enabled. SegmentNodeStore will be initialized once the custom segment store becomes available");
+ OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext());
+ SegmentNodeStore store = registerSegmentStore(
+ context,
+ configuration,
+ blobStore,
+ segmentStore,
+ getRoleStatisticsProvider(statisticsProvider, configuration.role()),
+ registrations,
+ whiteboard,
+ configuration.role(),
+ log
+ );
+
+ if (store == null) {
return;
}
- if (role != null) {
- registrations = Closer.create();
- OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext());
- final SegmentNodeStore store = SegmentNodeStoreService.registerSegmentStore(context, blobStore, segmentStore,
- getRoleStatisticsProvider(statisticsProvider, role), registrations, whiteboard, role, registerRepositoryDescriptors);
- if (store != null) {
- Map props = new HashMap();
- props.put(NodeStoreProvider.ROLE, role);
-
- registrations
- .register(asCloseable(whiteboard.register(NodeStoreProvider.class, new NodeStoreProvider() {
-
- @Override
- public NodeStore getNodeStore() {
- return store;
- }
- }, props)));
- log.info("Registered NodeStoreProvider backed by SegmentNodeStore of type '{}'", role);
- }
- }
+ Map props = new HashMap();
+ props.put(NodeStoreProvider.ROLE, configuration.role());
+ registrations.register(asCloseable(whiteboard.register(NodeStoreProvider.class, () -> store, props)));
+ log.info("Registered NodeStoreProvider backed by SegmentNodeStore of type '{}'", configuration.role());
}
@Deactivate
public void deactivate() {
- if (registrations != null) {
- IOUtils.closeQuietly(registrations);
- registrations = null;
- }
+ closeQuietly(registrations);
}
private static Closeable asCloseable(final Registration r) {
@@ -180,20 +362,250 @@ public class SegmentNodeStoreFactory {
return lookupConfigurationThenFramework(context, name);
}
- private static StatisticsProvider getRoleStatisticsProvider(StatisticsProvider delegate, String role) {
- RepositoryStatistics repositoryStatistics = new RepositoryStatistics() {
- @Override
- public TimeSeries getTimeSeries(Type type) {
- return getTimeSeries(type.name(), type.isResetValueEachSecond());
+ private static SegmentNodeStore registerSegmentStore(
+ ComponentContext context,
+ Configuration configuration,
+ BlobStore blobStore,
+ SegmentNodeStorePersistence segmentStore,
+ StatisticsProvider statisticsProvider,
+ Closer closer,
+ Whiteboard whiteboard,
+ String role,
+ Logger logger
+ ) throws IOException {
+ return SegmentNodeStoreRegistrar.registerSegmentNodeStore(new SegmentNodeStoreRegistrar.Configuration() {
+
+ String appendRole(String name) {
+ return name + "-" + role;
+ }
+
+ int roundToNextPowerOfTwo(int size) {
+ return 1 << (32 - Integer.numberOfLeadingZeros(Math.max(0, size - 1)));
+ }
+
+ String getMode() {
+ String mode = configuration.tarmk_mode();
+ if (isNullOrEmpty(mode)) {
+ return System.getProperty("tarmk.mode", System.getProperty("sun.arch.data.model", "32"));
+ }
+ return mode;
+ }
+
+ int getCacheSize(String name, int otherwise) {
+ Integer size = Integer.getInteger(name);
+ if (size != null) {
+ return size;
+ }
+ return otherwise;
+ }
+
+ @Override
+ public boolean isPrimarySegmentStore() {
+ return false;
+ }
+
+ @Override
+ public boolean isSecondarySegmentStore() {
+ return "secondary".equals(role);
+ }
+
+ @Override
+ public boolean isStandbyInstance() {
+ return configuration.standby();
+ }
+
+ @Override
+ public String getRole() {
+ return role;
+ }
+
+ @Override
+ public int getRetainedGenerations() {
+ return configuration.compaction_retainedGenerations();
+ }
+
+ @Override
+ public int getDefaultRetainedGenerations() {
+ return RETAINED_GENERATIONS_DEFAULT;
+ }
+
+ @Override
+ public boolean getPauseCompaction() {
+ return configuration.pauseCompaction();
+ }
+
+ @Override
+ public int getRetryCount() {
+ return configuration.compaction_retryCount();
+ }
+
+ @Override
+ public int getForceCompactionTimeout() {
+ return configuration.compaction_force_timeout();
+ }
+
+ @Override
+ public long getSizeDeltaEstimation() {
+ return configuration.compaction_sizeDeltaEstimation();
+ }
+
+ @Override
+ public int getMemoryThreshold() {
+ return configuration.compaction_memoryThreshold();
+ }
+
+ @Override
+ public boolean getDisableEstimation() {
+ return configuration.compaction_disableEstimation();
+ }
+
+ @Override
+ public long getGCProcessLog() {
+ return configuration.compaction_progressLog();
+ }
+
+ @Override
+ public File getSegmentDirectory() {
+ return new File(getRepositoryHome(), appendRole("segmentstore"));
+ }
+
+ @Override
+ public int getSegmentCacheSize() {
+ return getCacheSize("segmentCache.size", configuration.segmentCache_size());
+ }
+
+ @Override
+ public int getStringCacheSize() {
+ return getCacheSize("stringCache.size", configuration.stringCache_size());
+ }
+
+ @Override
+ public int getTemplateCacheSize() {
+ return getCacheSize("templateCache.size", configuration.templateCache_size());
+ }
+
+ @Override
+ public int getStringDeduplicationCacheSize() {
+ return getCacheSize("stringDeduplicationCache.size", configuration.stringDeduplicationCache_size());
+ }
+
+ @Override
+ public int getTemplateDeduplicationCacheSize() {
+ return getCacheSize("templateDeduplicationCache.size", configuration.templateDeduplicationCache_size());
+ }
+
+ @Override
+ public int getNodeDeduplicationCacheSize() {
+ return roundToNextPowerOfTwo(getCacheSize("nodeDeduplicationCache.size", configuration.nodeDeduplicationCache_size()));
+ }
+
+ @Override
+ public int getMaxFileSize() {
+ return configuration.tarmk_size();
+ }
+
+ @Override
+ public boolean getMemoryMapping() {
+ return getMode().equals("64");
+ }
+
+ @Override
+ public boolean hasCustomBlobStore() {
+ return configuration.customBlobStore();
+ }
+
+ @Override
+ public boolean hasCustomSegmentStore() {
+ return configuration.customSegmentStore();
+ }
+
+ @Override
+ public boolean registerDescriptors() {
+ return configuration.registerDescriptors();
+ }
+
+ @Override
+ public String getRepositoryHome() {
+ String repositoryHome = OsgiUtil.lookupConfigurationThenFramework(context, "repository.home");
+ if (isNullOrEmpty(repositoryHome)) {
+ return "repository";
}
+ return repositoryHome;
+ }
+
+ @Override
+ public long getBlobSnapshotInterval() {
+ return configuration.blobTrackSnapshotIntervalInSecs();
+ }
+
+ @Override
+ public long getBlobGcMaxAge() {
+ return configuration.blobGcMaxAgeInSecs();
+ }
- @Override
- public TimeSeries getTimeSeries(String type, boolean resetValueEachSecond) {
- return delegate.getStats().getTimeSeries(addRoleToName(type, role), resetValueEachSecond);
+ @Override
+ public File getBackupDirectory() {
+ String backupDirectory = configuration.repository_backup_dir();
+ if (isNullOrEmpty(backupDirectory)) {
+ return new File(getRepositoryHome(), appendRole("segmentstore-backup"));
}
+ return new File(backupDirectory);
+ }
+
+ @Override
+ public Whiteboard getWhiteboard() {
+ return whiteboard;
+ }
+
+ @Override
+ public Closer getCloser() {
+ return closer;
+ }
+
+ @Override
+ public Logger getLogger() {
+ return logger;
+ }
+
+ @Override
+ public StatisticsProvider getStatisticsProvider() {
+ return statisticsProvider;
+ }
+
+ @Override
+ public BlobStore getBlobStore() {
+ return blobStore;
+ }
+
+ @Override
+ public SegmentNodeStorePersistence getSegmentNodeStorePersistence() {
+ return segmentStore;
+ }
+
+ @Override
+ public BundleContext getBundleContext() {
+ return context.getBundleContext();
+ }
+
+ });
+ }
+
+ private static StatisticsProvider getRoleStatisticsProvider(StatisticsProvider delegate, String role) {
+ RepositoryStatistics repositoryStatistics = new RepositoryStatistics() {
+
+ @Override
+ public TimeSeries getTimeSeries(Type type) {
+ return getTimeSeries(type.name(), type.isResetValueEachSecond());
+ }
+
+ @Override
+ public TimeSeries getTimeSeries(String type, boolean resetValueEachSecond) {
+ return delegate.getStats().getTimeSeries(addRoleToName(type, role), resetValueEachSecond);
+ }
};
return new StatisticsProvider() {
+
@Override
public RepositoryStatistics getStats() {
return repositoryStatistics;
@@ -218,12 +630,12 @@ public class SegmentNodeStoreFactory {
public HistogramStats getHistogram(String name, StatsOptions options) {
return delegate.getHistogram(addRoleToName(name, role), options);
}
+
};
}
private static String addRoleToName(String name, String role) {
- return new StringBuilder(role).append('.').append(name).toString();
+ return role + '.' + name;
}
-
}
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java
index dcc2130df2..50cff1d93c 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java
@@ -19,45 +19,44 @@
package org.apache.jackrabbit.oak.segment;
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.PropertyUnbounded;
-import org.apache.felix.scr.annotations.Reference;
import org.apache.jackrabbit.oak.commons.PropertiesUtil;
-import org.osgi.service.component.ComponentContext;
+import org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService.Configuration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
/**
* An OSGi wrapper for segment node store monitoring configurations.
*/
-@Component(policy = ConfigurationPolicy.REQUIRE,
- metatype = true,
- label = "Oak Segment Tar Monitoring service",
- description = "This service is responsible for different configurations related to " +
- "Oak Segment Tar read/write monitoring."
-)
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = Configuration.class)
public class SegmentNodeStoreMonitorService {
-
- @Property(label = "Writer groups",
- unbounded = PropertyUnbounded.ARRAY,
- description = "Writer groups for which commits are tracked individually"
+
+ @ObjectClassDefinition(
+ name = "Oak Segment Tar Monitoring service",
+ description = "This service is responsible for different configurations related to " +
+ "Oak Segment Tar read/write monitoring."
)
- private static final String COMMITS_TRACKER_WRITER_GROUPS = "commitsTrackerWriterGroups";
+ @interface Configuration {
+
+ @AttributeDefinition(
+ name = "Writer groups",
+ description = "Writer groups for which commits are tracked individually"
+ )
+ String[] commitsTrackerWriterGroups() default {};
+
+ }
@Reference
private SegmentNodeStoreStatsMBean snsStatsMBean;
-
+
@Activate
- public void activate(ComponentContext context, Map config) throws IOException {
- augmentSegmentNodeStoreStatsMBean(config);
+ public void activate(Configuration config) {
+ snsStatsMBean.setWriterGroupsForLastMinuteCounts(PropertiesUtil.toStringArray(config.commitsTrackerWriterGroups(), null));
}
- private void augmentSegmentNodeStoreStatsMBean(Map config) {
- snsStatsMBean.setWriterGroupsForLastMinuteCounts(
- PropertiesUtil.toStringArray(config.get(COMMITS_TRACKER_WRITER_GROUPS), null));
- }
}
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java
new file mode 100644
index 0000000000..5901ec94f7
--- /dev/null
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java
@@ -0,0 +1,525 @@
+/*
+ * 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.segment;
+
+import static org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils.isShared;
+import static org.apache.jackrabbit.oak.segment.SegmentNotFoundExceptionListener.IGNORE_SNFE;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETAINED_GENERATIONS_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+import static org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo.getOrCreateId;
+
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.io.Closer;
+import org.apache.jackrabbit.commons.SimpleValueFactory;
+import org.apache.jackrabbit.oak.api.Descriptors;
+import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
+import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean;
+import org.apache.jackrabbit.oak.api.jmx.FileStoreBackupRestoreMBean;
+import org.apache.jackrabbit.oak.backup.impl.FileStoreBackupRestoreImpl;
+import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.plugins.blob.BlobGC;
+import org.apache.jackrabbit.oak.plugins.blob.BlobGCMBean;
+import org.apache.jackrabbit.oak.plugins.blob.BlobGarbageCollector;
+import org.apache.jackrabbit.oak.plugins.blob.BlobTrackingStore;
+import org.apache.jackrabbit.oak.plugins.blob.MarkSweepGarbageCollector;
+import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.BlobIdTracker;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGC;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGCMBean;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder;
+import org.apache.jackrabbit.oak.segment.file.FileStoreGCMonitor;
+import org.apache.jackrabbit.oak.segment.file.FileStoreStatsMBean;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.file.MetricsIOMonitor;
+import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
+import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo;
+import org.apache.jackrabbit.oak.spi.commit.ObserverTracker;
+import org.apache.jackrabbit.oak.spi.descriptors.GenericDescriptors;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitorTracker;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.RevisionGC;
+import org.apache.jackrabbit.oak.spi.state.RevisionGCMBean;
+import org.apache.jackrabbit.oak.spi.whiteboard.AbstractServiceTracker;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.apache.jackrabbit.oak.stats.Clock;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+
+class SegmentNodeStoreRegistrar {
+
+ static SegmentNodeStore registerSegmentNodeStore(Configuration cfg) throws IOException {
+ return new SegmentNodeStoreRegistrar(cfg).register();
+ }
+
+ interface Configuration {
+
+ boolean isPrimarySegmentStore();
+
+ boolean isSecondarySegmentStore();
+
+ boolean isStandbyInstance();
+
+ String getRole();
+
+ int getRetainedGenerations();
+
+ int getDefaultRetainedGenerations();
+
+ boolean getPauseCompaction();
+
+ int getRetryCount();
+
+ int getForceCompactionTimeout();
+
+ long getSizeDeltaEstimation();
+
+ int getMemoryThreshold();
+
+ boolean getDisableEstimation();
+
+ long getGCProcessLog();
+
+ File getSegmentDirectory();
+
+ int getSegmentCacheSize();
+
+ int getStringCacheSize();
+
+ int getTemplateCacheSize();
+
+ int getStringDeduplicationCacheSize();
+
+ int getTemplateDeduplicationCacheSize();
+
+ int getNodeDeduplicationCacheSize();
+
+ int getMaxFileSize();
+
+ boolean getMemoryMapping();
+
+ boolean hasCustomBlobStore();
+
+ boolean hasCustomSegmentStore();
+
+ boolean registerDescriptors();
+
+ String getRepositoryHome();
+
+ long getBlobSnapshotInterval();
+
+ long getBlobGcMaxAge();
+
+ File getBackupDirectory();
+
+ Whiteboard getWhiteboard();
+
+ Closer getCloser();
+
+ Logger getLogger();
+
+ StatisticsProvider getStatisticsProvider();
+
+ BlobStore getBlobStore();
+
+ SegmentNodeStorePersistence getSegmentNodeStorePersistence();
+
+ BundleContext getBundleContext();
+
+ }
+
+ private final Configuration cfg;
+
+ private SegmentNodeStoreRegistrar(Configuration cfg) {
+ this.cfg = cfg;
+ }
+
+ private SegmentNodeStore register() throws IOException {
+ if (cfg.getBlobStore() == null && (cfg.hasCustomBlobStore() || cfg.isSecondarySegmentStore())) {
+ cfg.getLogger().info("BlobStore enabled. SegmentNodeStore will be initialized once the blob store becomes available");
+ return null;
+ }
+
+ if (cfg.getSegmentNodeStorePersistence() == null && cfg.hasCustomSegmentStore()) {
+ cfg.getLogger().info("customSegmentStore enabled. SegmentNodeStore will be initialized once the custom segment store becomes available");
+ return null;
+ }
+
+ // Listen for GCMonitor services
+ GCMonitor gcMonitor = GCMonitor.EMPTY;
+
+ if (cfg.isPrimarySegmentStore()) {
+ GCMonitorTracker tracker = new GCMonitorTracker();
+ tracker.start(cfg.getWhiteboard());
+ registerCloseable(tracker);
+ gcMonitor = tracker;
+ }
+
+ // Create the gc options
+ if (cfg.getRetainedGenerations() != cfg.getDefaultRetainedGenerations()) {
+ cfg.getLogger().warn(
+ "The number of retained generations defaults to {} and can't be " +
+ "changed. This configuration option is considered deprecated " +
+ "and will be removed in the future.",
+ RETAINED_GENERATIONS_DEFAULT
+ );
+ }
+ SegmentGCOptions gcOptions = new SegmentGCOptions(cfg.getPauseCompaction(), cfg.getRetryCount(), cfg.getForceCompactionTimeout())
+ .setGcSizeDeltaEstimation(cfg.getSizeDeltaEstimation())
+ .setMemoryThreshold(cfg.getMemoryThreshold())
+ .setEstimationDisabled(cfg.getDisableEstimation())
+ .setGCLogInterval(cfg.getGCProcessLog());
+ if (cfg.isStandbyInstance()) {
+ gcOptions.setRetainedGenerations(1);
+ }
+
+ // Build the FileStore
+ FileStoreBuilder builder = fileStoreBuilder(cfg.getSegmentDirectory())
+ .withSegmentCacheSize(cfg.getSegmentCacheSize())
+ .withStringCacheSize(cfg.getStringCacheSize())
+ .withTemplateCacheSize(cfg.getTemplateCacheSize())
+ .withStringDeduplicationCacheSize(cfg.getStringDeduplicationCacheSize())
+ .withTemplateDeduplicationCacheSize(cfg.getTemplateDeduplicationCacheSize())
+ .withNodeDeduplicationCacheSize(cfg.getNodeDeduplicationCacheSize())
+ .withMaxFileSize(cfg.getMaxFileSize())
+ .withMemoryMapping(cfg.getMemoryMapping())
+ .withGCMonitor(gcMonitor)
+ .withIOMonitor(new MetricsIOMonitor(cfg.getStatisticsProvider()))
+ .withStatisticsProvider(cfg.getStatisticsProvider())
+ .withGCOptions(gcOptions);
+
+ if (cfg.hasCustomBlobStore() && cfg.getBlobStore() != null) {
+ cfg.getLogger().info("Initializing SegmentNodeStore with BlobStore [{}]", cfg.getBlobStore());
+ builder.withBlobStore(cfg.getBlobStore());
+ }
+
+ if (cfg.hasCustomSegmentStore() && cfg.getSegmentNodeStorePersistence() != null) {
+ cfg.getLogger().info("Initializing SegmentNodeStore with custom persistence [{}]", cfg.getSegmentNodeStorePersistence());
+ builder.withCustomPersistence(cfg.getSegmentNodeStorePersistence());
+ }
+
+ if (cfg.isStandbyInstance()) {
+ builder.withSnfeListener(IGNORE_SNFE);
+ }
+
+ FileStore store;
+ try {
+ store = builder.build();
+ } catch (InvalidFileStoreVersionException e) {
+ cfg.getLogger().error("The storage format is not compatible with this version of Oak Segment Tar", e);
+ return null;
+ }
+ registerCloseable(store);
+
+ // Listen for Executor services on the whiteboard
+
+ WhiteboardExecutor executor = new WhiteboardExecutor();
+ executor.start(cfg.getWhiteboard());
+ registerCloseable(executor);
+
+ // Expose stats about the segment cache
+
+ CacheStatsMBean segmentCacheStats = store.getSegmentCacheStats();
+ registerCloseable(registerMBean(
+ CacheStatsMBean.class,
+ segmentCacheStats,
+ CacheStats.TYPE,
+ segmentCacheStats.getName()
+ ));
+
+ // Expose stats about the string and template caches
+
+ CacheStatsMBean stringCacheStats = store.getStringCacheStats();
+ registerCloseable(registerMBean(
+ CacheStatsMBean.class,
+ stringCacheStats,
+ CacheStats.TYPE,
+ stringCacheStats.getName()
+ ));
+
+ CacheStatsMBean templateCacheStats = store.getTemplateCacheStats();
+ registerCloseable(registerMBean(
+ CacheStatsMBean.class,
+ templateCacheStats,
+ CacheStats.TYPE,
+ templateCacheStats.getName()
+ ));
+
+ WriterCacheManager cacheManager = builder.getCacheManager();
+ CacheStatsMBean stringDeduplicationCacheStats = cacheManager.getStringCacheStats();
+ if (stringDeduplicationCacheStats != null) {
+ registerCloseable(registerMBean(
+ CacheStatsMBean.class,
+ stringDeduplicationCacheStats,
+ CacheStats.TYPE,
+ stringDeduplicationCacheStats.getName()
+ ));
+ }
+
+ CacheStatsMBean templateDeduplicationCacheStats = cacheManager.getTemplateCacheStats();
+ if (templateDeduplicationCacheStats != null) {
+ registerCloseable(registerMBean(
+ CacheStatsMBean.class,
+ templateDeduplicationCacheStats,
+ CacheStats.TYPE,
+ templateDeduplicationCacheStats.getName()
+ ));
+ }
+
+ CacheStatsMBean nodeDeduplicationCacheStats = cacheManager.getNodeCacheStats();
+ if (nodeDeduplicationCacheStats != null) {
+ registerCloseable(registerMBean(
+ CacheStatsMBean.class,
+ nodeDeduplicationCacheStats,
+ CacheStats.TYPE,
+ nodeDeduplicationCacheStats.getName()
+ ));
+ }
+
+ // Expose an MBean to managing and monitoring garbage collection
+
+ FileStoreGCMonitor monitor = new FileStoreGCMonitor(Clock.SIMPLE);
+ registerCloseable(register(
+ GCMonitor.class,
+ monitor
+ ));
+ if (!cfg.isStandbyInstance()) {
+ registerCloseable(registerMBean(
+ SegmentRevisionGC.class,
+ new SegmentRevisionGCMBean(store, gcOptions, monitor),
+ SegmentRevisionGC.TYPE,
+ "Segment node store revision garbage collection"
+ ));
+ }
+
+ registerCloseable(registerMBean(
+ RevisionGCMBean.class,
+ new RevisionGC(store.getGCRunner(), store::cancelGC, monitor::getStatus, executor),
+ RevisionGCMBean.TYPE,
+ "Revision garbage collection"
+ ));
+
+ // Expose statistics about the FileStore
+
+ registerCloseable(registerMBean(
+ FileStoreStatsMBean.class,
+ store.getStats(),
+ FileStoreStatsMBean.TYPE,
+ "FileStore statistics"
+ ));
+
+ // register segment node store
+
+ SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = SegmentNodeStoreBuilders.builder(store).withStatisticsProvider(cfg.getStatisticsProvider());
+ if (cfg.isStandbyInstance() || !cfg.isPrimarySegmentStore()) {
+ segmentNodeStoreBuilder.dispatchChanges(false);
+ }
+ SegmentNodeStore segmentNodeStore = segmentNodeStoreBuilder.build();
+
+ if (cfg.isPrimarySegmentStore()) {
+ ObserverTracker observerTracker = new ObserverTracker(segmentNodeStore);
+ observerTracker.start(cfg.getBundleContext());
+ registerCloseable(observerTracker);
+ }
+
+ if (cfg.isPrimarySegmentStore()) {
+ registerCloseable(registerMBean(
+ CheckpointMBean.class,
+ new SegmentCheckpointMBean(segmentNodeStore),
+ CheckpointMBean.TYPE,
+ "Segment node store checkpoint management"
+ ));
+ }
+
+ if (cfg.registerDescriptors()) {
+ // ensure a clusterId is initialized
+ // and expose it as 'oak.clusterid' repository descriptor
+ GenericDescriptors clusterIdDesc = new GenericDescriptors();
+ clusterIdDesc.put(
+ ClusterRepositoryInfo.OAK_CLUSTERID_REPOSITORY_DESCRIPTOR_KEY,
+ new SimpleValueFactory().createValue(getOrCreateId(segmentNodeStore)),
+ true,
+ false
+ );
+ registerCloseable(register(Descriptors.class, clusterIdDesc));
+ // Register "discovery lite" descriptors
+ registerCloseable(register(Descriptors.class, new SegmentDiscoveryLiteDescriptors(segmentNodeStore)));
+ }
+
+ // If a shared data store register the repo id in the data store
+ if (cfg.isPrimarySegmentStore() && isShared(cfg.getBlobStore())) {
+ SharedDataStore sharedDataStore = (SharedDataStore) cfg.getBlobStore();
+ try {
+ sharedDataStore.addMetadataRecord(new ByteArrayInputStream(new byte[0]), SharedDataStoreUtils.SharedStoreRecordType.REPOSITORY.getNameFromId(getOrCreateId(segmentNodeStore)));
+ } catch (Exception e) {
+ throw new IOException("Could not register a unique repositoryId", e);
+ }
+ if (cfg.getBlobStore() instanceof BlobTrackingStore) {
+ BlobTrackingStore trackingStore = (BlobTrackingStore) cfg.getBlobStore();
+ if (trackingStore.getTracker() != null) {
+ trackingStore.getTracker().close();
+ }
+ trackingStore.addTracker(new BlobIdTracker(cfg.getRepositoryHome(), getOrCreateId(segmentNodeStore), cfg.getBlobSnapshotInterval(), sharedDataStore));
+ }
+ }
+
+ if (cfg.isPrimarySegmentStore() && cfg.getBlobStore() instanceof GarbageCollectableBlobStore) {
+ BlobGarbageCollector gc = new MarkSweepGarbageCollector(
+ new SegmentBlobReferenceRetriever(store),
+ (GarbageCollectableBlobStore) cfg.getBlobStore(),
+ executor,
+ TimeUnit.SECONDS.toMillis(cfg.getBlobGcMaxAge()),
+ getOrCreateId(segmentNodeStore),
+ cfg.getWhiteboard(),
+ cfg.getStatisticsProvider()
+ );
+ registerCloseable(registerMBean(
+ BlobGCMBean.class,
+ new BlobGC(gc, executor),
+ BlobGCMBean.TYPE,
+ "Segment node store blob garbage collection"
+ ));
+ }
+
+ // Expose an MBean for backup/restore operations
+
+ registerCloseable(registerMBean(
+ FileStoreBackupRestoreMBean.class,
+ new FileStoreBackupRestoreImpl(
+ segmentNodeStore,
+ store.getRevisions(),
+ store.getReader(),
+ cfg.getBackupDirectory(),
+ executor
+ ),
+ FileStoreBackupRestoreMBean.TYPE,
+ "Segment node store backup/restore"
+ ));
+
+ // Expose statistics about the SegmentNodeStore
+
+ registerCloseable(registerMBean(
+ SegmentNodeStoreStatsMBean.class,
+ segmentNodeStore.getStats(),
+ SegmentNodeStoreStatsMBean.TYPE,
+ "SegmentNodeStore statistics"
+ ));
+
+ if (cfg.isPrimarySegmentStore()) {
+ cfg.getLogger().info("Primary SegmentNodeStore initialized");
+ } else {
+ cfg.getLogger().info("Secondary SegmentNodeStore initialized, role={}", cfg.getRole());
+ }
+
+ // Register a factory service to expose the FileStore
+ registerCloseable(register(
+ SegmentStoreProvider.class,
+ new DefaultSegmentStoreProvider(store)
+ ));
+
+ if (cfg.isStandbyInstance()) {
+ return segmentNodeStore;
+ }
+
+ if (cfg.isPrimarySegmentStore()) {
+ Map props = new HashMap<>();
+ props.put(Constants.SERVICE_PID, SegmentNodeStore.class.getName());
+ props.put("oak.nodestore.description", new String[] {"nodeStoreType=segment"});
+ registerCloseable(register(NodeStore.class, segmentNodeStore, props));
+ }
+
+ return segmentNodeStore;
+ }
+
+ private Registration registerMBean(Class clazz, T bean, String type, String name) {
+ return registerMBean(clazz, bean, type, name, new HashMap<>());
+ }
+
+ private Registration registerMBean(Class clazz, T bean, String type, String name, Map attributes) {
+ return WhiteboardUtils.registerMBean(cfg.getWhiteboard(), clazz, bean, type, maybeAppendRole(name), maybePutRoleAttribute(attributes));
+ }
+
+ private Registration register(Class clazz, T service) {
+ return register(clazz, service, new HashMap<>());
+ }
+
+ private Registration register(Class clazz, T service, Map properties) {
+ return cfg.getWhiteboard().register(clazz, service, maybePutRoleProperty(properties));
+ }
+
+ private String maybeAppendRole(String name) {
+ if (cfg.getRole() != null) {
+ return name + " - " + cfg.getRole();
+ }
+ return name;
+ }
+
+ private String jmxRole() {
+ return cfg.getRole().replaceAll(":", "-");
+ }
+
+ private Map maybePutRoleAttribute(Map attributes) {
+ if (cfg.getRole() != null) {
+ attributes.put("role", jmxRole());
+ }
+ return attributes;
+ }
+
+ private Map maybePutRoleProperty(Map attributes) {
+ if (cfg.getRole() != null) {
+ attributes.put("role", cfg.getRole());
+ }
+ return attributes;
+ }
+
+ private void registerCloseable(Closeable c) {
+ cfg.getCloser().register(c);
+ }
+
+ private void registerCloseable(final AbstractServiceTracker> t) {
+ registerCloseable((Closeable) t::stop);
+ }
+
+ private void registerCloseable(final Registration r) {
+ registerCloseable((Closeable) r::unregister);
+ }
+
+ private void registerCloseable(final ObserverTracker t) {
+ registerCloseable((Closeable) t::stop);
+ }
+
+}
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
index b37eac6c7c..e195de2869 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
@@ -21,40 +21,9 @@ package org.apache.jackrabbit.oak.segment;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
-import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toBoolean;
-import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toInteger;
-import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toLong;
-import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework;
-import static org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils.isShared;
import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_STRING_CACHE_MB;
import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_TEMPLATE_CACHE_MB;
import static org.apache.jackrabbit.oak.segment.SegmentCache.DEFAULT_SEGMENT_CACHE_MB;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.BACKUP_DIRECTORY;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_DISABLE_ESTIMATION;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_FORCE_TIMEOUT;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_RETRY_COUNT;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_SIZE_DELTA_ESTIMATION;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.CUSTOM_BLOB_STORE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.CUSTOM_SEGMENT_STORE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.DEFAULT_BLOB_GC_MAX_AGE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.DEFAULT_BLOB_SNAPSHOT_INTERVAL;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.GC_PROGRESS_LOG;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.MEMORY_THRESHOLD;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.MODE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.NODE_DEDUPLICATION_CACHE_SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.PAUSE_COMPACTION;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.PROP_BLOB_GC_MAX_AGE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.PROP_BLOB_SNAPSHOT_INTERVAL;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.REPOSITORY_HOME_DIRECTORY;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.RETAINED_GENERATIONS;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.SEGMENT_CACHE_SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.STANDBY;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.STRING_CACHE_SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.STRING_DEDUPLICATION_CACHE_SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.TEMPLATE_CACHE_SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.TEMPLATE_DEDUPLICATION_CACHE_SIZE;
-import static org.apache.jackrabbit.oak.segment.SegmentNotFoundExceptionListener.IGNORE_SNFE;
import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_NODE_CACHE_SIZE_OSGi;
import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_STRING_CACHE_SIZE_OSGi;
import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_TEMPLATE_CACHE_SIZE_OSGi;
@@ -67,995 +36,516 @@ import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETA
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETRY_COUNT_DEFAULT;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.SIZE_DELTA_ESTIMATION_DEFAULT;
import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.DEFAULT_MAX_FILE_SIZE;
-import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET;
-import static org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo.getOrCreateId;
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
import java.io.File;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import com.google.common.base.Supplier;
import com.google.common.io.Closer;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.ReferencePolicy;
-import org.apache.felix.scr.annotations.ReferencePolicyOption;
-import org.apache.jackrabbit.commons.SimpleValueFactory;
-import org.apache.jackrabbit.oak.api.Descriptors;
-import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
-import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean;
-import org.apache.jackrabbit.oak.api.jmx.FileStoreBackupRestoreMBean;
-import org.apache.jackrabbit.oak.backup.impl.FileStoreBackupRestoreImpl;
-import org.apache.jackrabbit.oak.cache.CacheStats;
-import org.apache.jackrabbit.oak.cache.CacheStatsMBeanWrapper;
+import org.apache.jackrabbit.oak.osgi.OsgiUtil;
import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
-import org.apache.jackrabbit.oak.plugins.blob.BlobGC;
-import org.apache.jackrabbit.oak.plugins.blob.BlobGCMBean;
-import org.apache.jackrabbit.oak.plugins.blob.BlobGarbageCollector;
-import org.apache.jackrabbit.oak.plugins.blob.BlobTrackingStore;
-import org.apache.jackrabbit.oak.plugins.blob.MarkSweepGarbageCollector;
-import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore;
-import org.apache.jackrabbit.oak.plugins.blob.datastore.BlobIdTracker;
-import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils.SharedStoreRecordType;
-import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
-import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGC;
-import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGCMBean;
-import org.apache.jackrabbit.oak.segment.file.FileStore;
-import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder;
-import org.apache.jackrabbit.oak.segment.file.FileStoreGCMonitor;
-import org.apache.jackrabbit.oak.segment.file.FileStoreStatsMBean;
-import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
-import org.apache.jackrabbit.oak.segment.file.MetricsIOMonitor;
-import org.apache.jackrabbit.oak.segment.file.tar.SegmentTarReader;
import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
-import org.apache.jackrabbit.oak.segment.util.RoleUtils;
import org.apache.jackrabbit.oak.spi.blob.BlobStore;
-import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
-import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo;
-import org.apache.jackrabbit.oak.spi.commit.ObserverTracker;
-import org.apache.jackrabbit.oak.spi.descriptors.GenericDescriptors;
-import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
-import org.apache.jackrabbit.oak.spi.gc.GCMonitorTracker;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
-import org.apache.jackrabbit.oak.spi.state.RevisionGC;
-import org.apache.jackrabbit.oak.spi.state.RevisionGCMBean;
-import org.apache.jackrabbit.oak.spi.whiteboard.AbstractServiceTracker;
-import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
-import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor;
-import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
-import org.apache.jackrabbit.oak.stats.Clock;
import org.apache.jackrabbit.oak.stats.StatisticsProvider;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.framework.Constants;
+import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An OSGi wrapper for the segment node store.
*/
-@Component(policy = ConfigurationPolicy.REQUIRE,
- metatype = true,
- label = "Oak Segment Tar NodeStore service",
- description = "Apache Jackrabbit Oak NodeStore implementation based on the segment model. " +
- "For configuration refer to http://jackrabbit.apache.org/oak/docs/osgi_config.html#SegmentNodeStore. " +
- "Note that for system stability purpose it is advisable to not change these settings " +
- "at runtime. Instead the config change should be done via file system based config " +
- "file and this view should ONLY be used to determine which options are supported."
-)
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = SegmentNodeStoreService.Configuration.class)
public class SegmentNodeStoreService {
private static final Logger log = LoggerFactory.getLogger(SegmentNodeStoreService.class);
- @Property(
- label = "Repository Home Directory",
- description = "Path on the file system where repository data will be stored. "
- + "Defaults to the value of the framework property 'repository.home' or to 'repository' "
- + "if that is neither specified."
- )
+ // TODO(frm) This is only exposed to tests. Should it be removed?
+ public static final String CUSTOM_BLOB_STORE = "customBlobStore";
+
+ // TODO(frm) This is only exposed to tests. Should it be removed?
public static final String REPOSITORY_HOME_DIRECTORY = "repository.home";
- @Property(
- label = "Mode",
- description = "TarMK mode (64 for memory mapped file access, 32 for normal file access). " +
- "Default value is taken from the 'sun.arch.data.model' system property."
+ private static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60;
+
+ private static final long DEFAULT_BLOB_GC_MAX_AGE = 24 * 60 * 60;
+
+ @ObjectClassDefinition(
+ name = "Oak Segment Tar NodeStore service",
+ description = "Apache Jackrabbit Oak NodeStore implementation based on " +
+ "the segment model. For configuration refer to http://jackrabbit.apache.org/oak/docs/osgi_config.html#SegmentNodeStore. " +
+ "Note that for system stability purpose it is advisable to not " +
+ "change these settings at runtime. Instead the config change should " +
+ "be done via file system based config file and this view should ONLY " +
+ "be used to determine which options are supported."
)
- public static final String MODE = "tarmk.mode";
+ @interface Configuration {
- @Property(
- intValue = DEFAULT_MAX_FILE_SIZE,
- label = "Maximum tar file size (MB)",
+ @AttributeDefinition(
+ name = "Repository Home Directory",
+ description = "Path on the file system where repository data will be stored. "
+ + "Defaults to the value of the framework property 'repository.home' or to 'repository' "
+ + "if that is neither specified."
+ )
+ String repository_home() default "";
+
+ @AttributeDefinition(
+ name = "Mode",
+ description = "TarMK mode (64 for memory mapped file access, 32 for normal file access). " +
+ "Default value is taken from the 'sun.arch.data.model' system property."
+ )
+ String tarmk_mode() default "";
+
+ @AttributeDefinition(
+ name = "Maximum tar file size (MB)",
description = "The maximum size of the tar files in megabytes. " +
- "Default value is '" + DEFAULT_MAX_FILE_SIZE + "'."
- )
- public static final String SIZE = "tarmk.size";
+ "Default value is '" + DEFAULT_MAX_FILE_SIZE + "'."
+ )
+ int tarmk_size() default DEFAULT_MAX_FILE_SIZE;
- @Property(
- intValue = DEFAULT_SEGMENT_CACHE_MB,
- label = "Segment cache size (MB)",
+ @AttributeDefinition(
+ name = "Segment cache size (MB)",
description = "Cache size for storing most recently used segments in megabytes. " +
- "Default value is '" + DEFAULT_SEGMENT_CACHE_MB + "'."
- )
- public static final String SEGMENT_CACHE_SIZE = "segmentCache.size";
+ "Default value is '" + DEFAULT_SEGMENT_CACHE_MB + "'."
+ )
+ int segmentCache_size() default DEFAULT_SEGMENT_CACHE_MB;
- @Property(
- intValue = DEFAULT_STRING_CACHE_MB,
- label = "String cache size (MB)",
+ @AttributeDefinition(
+ name = "String cache size (MB)",
description = "Cache size for storing most recently used strings in megabytes. " +
- "Default value is '" + DEFAULT_STRING_CACHE_MB + "'."
- )
- public static final String STRING_CACHE_SIZE = "stringCache.size";
+ "Default value is '" + DEFAULT_STRING_CACHE_MB + "'."
+ )
+ int stringCache_size() default DEFAULT_STRING_CACHE_MB;
- @Property(
- intValue = DEFAULT_TEMPLATE_CACHE_MB,
- label = "Template cache size (MB)",
+ @AttributeDefinition(
+ name = "Template cache size (MB)",
description = "Cache size for storing most recently used templates in megabytes. " +
- "Default value is '" + DEFAULT_TEMPLATE_CACHE_MB + "'."
- )
- public static final String TEMPLATE_CACHE_SIZE = "templateCache.size";
+ "Default value is '" + DEFAULT_TEMPLATE_CACHE_MB + "'."
+ )
+ int templateCache_size() default DEFAULT_TEMPLATE_CACHE_MB;
- @Property(
- intValue = DEFAULT_STRING_CACHE_SIZE_OSGi,
- label = "String deduplication cache size (#items)",
+ @AttributeDefinition(
+ name = "String deduplication cache size (#items)",
description = "Maximum number of strings to keep in the deduplication cache. " +
- "Default value is '" + DEFAULT_STRING_CACHE_SIZE_OSGi + "'."
- )
- public static final String STRING_DEDUPLICATION_CACHE_SIZE = "stringDeduplicationCache.size";
+ "Default value is '" + DEFAULT_STRING_CACHE_SIZE_OSGi + "'."
+ )
+ int stringDeduplicationCache_size() default DEFAULT_STRING_CACHE_SIZE_OSGi;
- @Property(
- intValue = DEFAULT_TEMPLATE_CACHE_SIZE_OSGi,
- label = "Template deduplication cache size (#items)",
+ @AttributeDefinition(
+ name = "Template deduplication cache size (#items)",
description = "Maximum number of templates to keep in the deduplication cache. " +
- "Default value is '" + DEFAULT_TEMPLATE_CACHE_SIZE_OSGi + "'."
- )
- public static final String TEMPLATE_DEDUPLICATION_CACHE_SIZE = "templateDeduplicationCache.size";
+ "Default value is '" + DEFAULT_TEMPLATE_CACHE_SIZE_OSGi + "'."
+ )
+ int templateDeduplicationCache_size() default DEFAULT_TEMPLATE_CACHE_SIZE_OSGi;
- @Property(
- intValue = DEFAULT_NODE_CACHE_SIZE_OSGi,
- label = "Node deduplication cache size (#items)",
+ @AttributeDefinition(
+ name = "Node deduplication cache size (#items)",
description = "Maximum number of node to keep in the deduplication cache. If the supplied " +
- "value is not a power of 2, it will be rounded up to the next power of 2. " +
- "Default value is '" + DEFAULT_NODE_CACHE_SIZE_OSGi + "'."
- )
- public static final String NODE_DEDUPLICATION_CACHE_SIZE = "nodeDeduplicationCache.size";
+ "value is not a power of 2, it will be rounded up to the next power of 2. " +
+ "Default value is '" + DEFAULT_NODE_CACHE_SIZE_OSGi + "'."
+ )
+ int nodeDeduplicationCache_size() default DEFAULT_NODE_CACHE_SIZE_OSGi;
- @Property(
- boolValue = PAUSE_DEFAULT,
- label = "Pause compaction",
+ @AttributeDefinition(
+ name = "Pause compaction",
description = "When set to true the compaction phase is skipped during garbage collection. " +
- "Default value is '" + PAUSE_DEFAULT + "'."
- )
- public static final String PAUSE_COMPACTION = "pauseCompaction";
+ "Default value is '" + PAUSE_DEFAULT + "'."
+ )
+ boolean pauseCompaction() default PAUSE_DEFAULT;
- @Property(
- intValue = RETRY_COUNT_DEFAULT,
- label = "Compaction retries",
+ @AttributeDefinition(
+ name = "Compaction retries",
description = "Number of tries to compact concurrent commits on top of already " +
- "compacted commits. " +
- "Default value is '" + RETRY_COUNT_DEFAULT + "'."
- )
- public static final String COMPACTION_RETRY_COUNT = "compaction.retryCount";
+ "compacted commits. " +
+ "Default value is '" + RETRY_COUNT_DEFAULT + "'."
+ )
+ int compaction_retryCount() default RETRY_COUNT_DEFAULT;
- @Property(
- intValue = FORCE_TIMEOUT_DEFAULT,
- label = "Force compaction timeout",
+ @AttributeDefinition(
+ name = "Force compaction timeout",
description = "Number of seconds to attempt to force compact concurrent commits on top " +
- "of already compacted commits after the maximum number of retries has been " +
- "reached. Forced compaction tries to acquire an exclusive write lock on the " +
- "node store, blocking concurrent write access as long as the lock is held. " +
- "Default value is '" + FORCE_TIMEOUT_DEFAULT + "'."
- )
- public static final String COMPACTION_FORCE_TIMEOUT = "compaction.force.timeout";
-
- @Property(
- longValue = SIZE_DELTA_ESTIMATION_DEFAULT,
- label = "Garbage collection repository size threshold",
+ "of already compacted commits after the maximum number of retries has been " +
+ "reached. Forced compaction tries to acquire an exclusive write lock on the " +
+ "node store, blocking concurrent write access as long as the lock is held. " +
+ "Default value is '" + FORCE_TIMEOUT_DEFAULT + "'."
+ )
+ int compaction_force_timeout() default FORCE_TIMEOUT_DEFAULT;
+
+ @AttributeDefinition(
+ name = "Garbage collection repository size threshold",
description = "Garbage collection will be skipped unless the repository grew at least by " +
- "the number of bytes specified. " +
- "Default value is '" + SIZE_DELTA_ESTIMATION_DEFAULT + "'."
- )
- public static final String COMPACTION_SIZE_DELTA_ESTIMATION = "compaction.sizeDeltaEstimation";
+ "the number of bytes specified. " +
+ "Default value is '" + SIZE_DELTA_ESTIMATION_DEFAULT + "'."
+ )
+ long compaction_sizeDeltaEstimation() default SIZE_DELTA_ESTIMATION_DEFAULT;
- @Property(
- boolValue = DISABLE_ESTIMATION_DEFAULT,
- label = "Disable estimation phase",
+ @AttributeDefinition(
+ name = "Disable estimation phase",
description = "Disables the estimation phase allowing garbage collection to run unconditionally. " +
- "Default value is '" + DISABLE_ESTIMATION_DEFAULT + "'."
- )
- public static final String COMPACTION_DISABLE_ESTIMATION = "compaction.disableEstimation";
+ "Default value is '" + DISABLE_ESTIMATION_DEFAULT + "'."
+ )
+ boolean compaction_disableEstimation() default DISABLE_ESTIMATION_DEFAULT;
- @Property(
- intValue = RETAINED_GENERATIONS_DEFAULT,
- label = "Compaction retained generations",
+ @AttributeDefinition(
+ name = "Compaction retained generations",
description = "Number of segment generations to retain during garbage collection. " +
"The number of generations defaults to " + RETAINED_GENERATIONS_DEFAULT + " and " +
"can't be changed. This configuration option is considered deprecated " +
"and will be removed in the future."
- )
- public static final String RETAINED_GENERATIONS = "compaction.retainedGenerations";
+ )
+ int compaction_retainedGenerations() default RETAINED_GENERATIONS_DEFAULT;
- @Property(
- intValue = MEMORY_THRESHOLD_DEFAULT,
- label = "Compaction memory threshold",
+ @AttributeDefinition(
+ name = "Compaction memory threshold",
description = "Threshold of available heap memory in percent of total heap memory below " +
- "which the compaction phase is canceled. 0 disables heap memory monitoring. " +
- "Default value is '" + MEMORY_THRESHOLD_DEFAULT + "'."
- )
- public static final String MEMORY_THRESHOLD = "compaction.memoryThreshold";
+ "which the compaction phase is canceled. 0 disables heap memory monitoring. " +
+ "Default value is '" + MEMORY_THRESHOLD_DEFAULT + "'."
+ )
+ int compaction_memoryThreshold() default MEMORY_THRESHOLD_DEFAULT;
- @Property(
- longValue = GC_PROGRESS_LOG_DEFAULT,
- label = "Compaction progress log",
+ @AttributeDefinition(
+ name = "Compaction progress log",
description = "The number of nodes compacted after which a status message is logged. " +
- "-1 disables progress logging. " +
- "Default value is '" + GC_PROGRESS_LOG_DEFAULT + "'."
- )
- public static final String GC_PROGRESS_LOG = "compaction.progressLog";
+ "-1 disables progress logging. " +
+ "Default value is '" + GC_PROGRESS_LOG_DEFAULT + "'."
+ )
+ long compaction_progressLog() default GC_PROGRESS_LOG_DEFAULT;
- @Property(
- boolValue = false,
- label = "Standby mode",
+ @AttributeDefinition(
+ name = "Standby mode",
description = "Flag indicating this component will not register as a NodeStore but as a " +
- "NodeStoreProvider instead. " +
- "Default value is 'false'."
- )
- public static final String STANDBY = "standby";
+ "NodeStoreProvider instead. " +
+ "Default value is 'false'."
+ )
+ boolean standby() default false;
- @Property(boolValue = false,
- label = "Custom blob store",
+ @AttributeDefinition(
+ name = "Custom blob store",
description = "Boolean value indicating that a custom BlobStore is used for storing " +
- "large binary values."
- )
- public static final String CUSTOM_BLOB_STORE = "customBlobStore";
+ "large binary values."
+ )
+ boolean customBlobStore() default false;
- @Property(boolValue = false,
- label = "Custom segment store",
+ @AttributeDefinition(
+ name = "Custom segment store",
description = "Boolean value indicating that a custom (non-tar) segment store is used"
- )
- public static final String CUSTOM_SEGMENT_STORE = "customSegmentStore";
+ )
+ boolean customSegmentStore() default false;
- @Property(
- label = "Backup directory",
+ @AttributeDefinition(
+ name = "Backup directory",
description = "Directory (relative to current working directory) for storing repository backups. " +
- "Defaults to 'repository.home/segmentstore-backup'."
- )
- public static final String BACKUP_DIRECTORY = "repository.backup.dir";
+ "Defaults to 'repository.home/segmentstore-backup'."
+ )
+ String repository_backup_dir() default "";
+
+ @AttributeDefinition(
+ name = "Blob gc max age (in secs)",
+ description = "The blob garbage collection logic will only consider those blobs which " +
+ "are not accessed recently (currentTime - lastModifiedTime > blobGcMaxAgeInSecs). " +
+ "For example with the default setting only those blobs which have been created " +
+ "at least 24 hours ago will be considered for garbage collection. " +
+ "Default value is '" + DEFAULT_BLOB_GC_MAX_AGE + "'."
+ )
+ long blobGcMaxAgeInSecs() default DEFAULT_BLOB_GC_MAX_AGE;
+
+ @AttributeDefinition(
+ name = "Blob tracking snapshot interval",
+ description = "Interval in seconds in which snapshots of locally tracked blob ids are " +
+ "taken and synchronized with the blob store. This should be configured to be " +
+ "less than the frequency of blob garbage collection so that deletions during blob " +
+ "garbage collection can be accounted for in the next garbage collection execution. " +
+ "Default value is '" + DEFAULT_BLOB_SNAPSHOT_INTERVAL + "'."
+ )
+ long blobTrackSnapshotIntervalInSecs() default DEFAULT_BLOB_SNAPSHOT_INTERVAL;
+
+ }
@Reference(
- cardinality = ReferenceCardinality.OPTIONAL_UNARY,
- policy = ReferencePolicy.STATIC,
- policyOption = ReferencePolicyOption.GREEDY,
- target = ONLY_STANDALONE_TARGET
+ cardinality = ReferenceCardinality.OPTIONAL,
+ policy = ReferencePolicy.STATIC,
+ policyOption = ReferencePolicyOption.GREEDY,
+ target = ONLY_STANDALONE_TARGET
)
private volatile BlobStore blobStore;
@Reference(
- cardinality = ReferenceCardinality.OPTIONAL_UNARY,
- policy = ReferencePolicy.STATIC,
- policyOption = ReferencePolicyOption.GREEDY
+ cardinality = ReferenceCardinality.OPTIONAL,
+ policy = ReferencePolicy.STATIC,
+ policyOption = ReferencePolicyOption.GREEDY
)
private volatile SegmentNodeStorePersistence segmentStore;
@Reference
private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
-
- private Closer closer;
-
- /**
- * Blob modified before this time duration would be considered for Blob GC
- */
- static final long DEFAULT_BLOB_GC_MAX_AGE = 24 * 60 * 60;
-
- @Property(longValue = DEFAULT_BLOB_GC_MAX_AGE,
- label = "Blob gc max age (in secs)",
- description = "The blob garbage collection logic will only consider those blobs which " +
- "are not accessed recently (currentTime - lastModifiedTime > blobGcMaxAgeInSecs). " +
- "For example with the default setting only those blobs which have been created " +
- "at least 24 hours ago will be considered for garbage collection. " +
- "Default value is '" + DEFAULT_BLOB_GC_MAX_AGE + "'."
- )
- public static final String PROP_BLOB_GC_MAX_AGE = "blobGcMaxAgeInSecs";
- /**
- * Default interval for taking snapshots of locally tracked blob ids.
- */
- static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60;
-
- @Property(longValue = DEFAULT_BLOB_SNAPSHOT_INTERVAL,
- label = "Blob tracking snapshot interval",
- description = "Interval in seconds in which snapshots of locally tracked blob ids are " +
- "taken and synchronized with the blob store. This should be configured to be " +
- "less than the frequency of blob garbage collection so that deletions during blob " +
- "garbage collection can be accounted for in the next garbage collection execution. " +
- "Default value is '" + DEFAULT_BLOB_SNAPSHOT_INTERVAL + "'."
- )
- public static final String PROP_BLOB_SNAPSHOT_INTERVAL = "blobTrackSnapshotIntervalInSecs";
+ private final Closer closer = Closer.create();
@Activate
- public void activate(ComponentContext context) throws IOException {
- Configuration configuration = new Configuration(context);
- if (blobStore == null && configuration.hasCustomBlobStore()) {
- log.info("BlobStore enabled. SegmentNodeStore will be initialized once the blob " +
- "store becomes available");
- return;
- }
- if (segmentStore == null && configuration.hasCustomSegmentStore()) {
- log.info("customSegmentStore enabled. SegmentNodeStore will be initialized once the custom segment " +
- "store becomes available");
- return;
- }
- closer = Closer.create();
+ public void activate(ComponentContext context, Configuration configuration) throws IOException {
OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext());
- registerSegmentStore(context, blobStore, segmentStore, statisticsProvider, closer, whiteboard, null, true);
- }
-
- /**
- * Configures and registers a new SegmentNodeStore instance together will
- * all required components. Anything that must be disposed of (like
- * registered services or MBeans) will be registered via the
- * {@code registration} parameter.
- *
- * @param context An instance of {@link ComponentContext}.
- * @param blobStore An instance of {@link BlobStore}. It can be
- * {@code null}.
- * @param segmentStore An instance of {@link SegmentNodeStorePersistence}. It can be
- * {@code null}.
- * @param statisticsProvider An instance of {@link StatisticsProvider}.
- * @param closer An instance of {@link Closer}. It will be used
- * to track every registered service or
- * component.
- * @param whiteboard An instance of {@link Whiteboard}. It will be
- * used to register services in the OSGi
- * framework.
- * @param role The role of this component. It can be {@code
- * null}.
- * @param descriptors Determines if repository descriptors related to
- * discovery services should be registered.
- * @return A configured {@link SegmentNodeStore}, or {@code null} if the
- * setup failed.
- * @throws IOException In case an unrecoverable error occurs.
- */
- static SegmentNodeStore registerSegmentStore(
- @NotNull ComponentContext context,
- @Nullable BlobStore blobStore,
- @Nullable SegmentNodeStorePersistence segmentStore,
- @NotNull StatisticsProvider statisticsProvider,
- @NotNull Closer closer,
- @NotNull Whiteboard whiteboard,
- @Nullable String role,
- boolean descriptors
+ registerSegmentStore(context, configuration, blobStore, segmentStore, statisticsProvider, closer, whiteboard, log);
+ }
+
+ private static SegmentNodeStore registerSegmentStore(
+ ComponentContext context,
+ Configuration configuration,
+ BlobStore blobStore,
+ SegmentNodeStorePersistence segmentStore,
+ StatisticsProvider statisticsProvider,
+ Closer closer,
+ Whiteboard whiteboard,
+ Logger logger
) throws IOException {
- Configuration configuration = new Configuration(context, role);
- Closeables closeables = new Closeables(closer);
- Registrations registrations = new Registrations(whiteboard, role);
-
- // Listen for GCMonitor services
- GCMonitor gcMonitor = GCMonitor.EMPTY;
-
- if (configuration.isPrimarySegmentStore()) {
- GCMonitorTracker tracker = new GCMonitorTracker();
- tracker.start(whiteboard);
- closeables.add(tracker);
- gcMonitor = tracker;
- }
-
- // Create the gc options
- if (configuration.getCompactionGainThreshold() != null) {
- log.warn("Detected deprecated flag 'compaction.gainThreshold'. "
- + "Please use 'compaction.sizeDeltaEstimation' instead and "
- + "'compaction.disableEstimation' to disable estimation.");
- }
- if (configuration.getRetainedGenerations() != RETAINED_GENERATIONS_DEFAULT) {
- log.warn(
- "The number of retained generations defaults to {} and can't be " +
- "changed. This configuration option is considered deprecated " +
- "and will be removed in the future.",
- RETAINED_GENERATIONS_DEFAULT
- );
- }
- SegmentGCOptions gcOptions = new SegmentGCOptions(configuration.getPauseCompaction(), configuration.getRetryCount(), configuration.getForceCompactionTimeout())
- .setGcSizeDeltaEstimation(configuration.getSizeDeltaEstimation())
- .setMemoryThreshold(configuration.getMemoryThreshold())
- .setEstimationDisabled(configuration.getDisableEstimation())
- .setGCLogInterval(configuration.getGCProcessLog());
- if (configuration.isStandbyInstance()) {
- gcOptions.setRetainedGenerations(1);
- }
-
- // Build the FileStore
- FileStoreBuilder builder = fileStoreBuilder(configuration.getSegmentDirectory())
- .withSegmentCacheSize(configuration.getSegmentCacheSize())
- .withStringCacheSize(configuration.getStringCacheSize())
- .withTemplateCacheSize(configuration.getTemplateCacheSize())
- .withStringDeduplicationCacheSize(configuration.getStringDeduplicationCacheSize())
- .withTemplateDeduplicationCacheSize(configuration.getTemplateDeduplicationCacheSize())
- .withNodeDeduplicationCacheSize(configuration.getNodeDeduplicationCacheSize())
- .withMaxFileSize(configuration.getMaxFileSize())
- .withMemoryMapping(configuration.getMemoryMapping())
- .withGCMonitor(gcMonitor)
- .withIOMonitor(new MetricsIOMonitor(statisticsProvider))
- .withIOLogging(LoggerFactory.getLogger(SegmentTarReader.class))
- .withStatisticsProvider(statisticsProvider)
- .withGCOptions(gcOptions);
-
- if (configuration.hasCustomBlobStore() && blobStore != null) {
- log.info("Initializing SegmentNodeStore with BlobStore [{}]", blobStore);
- builder.withBlobStore(blobStore);
- }
-
- if (configuration.hasCustomSegmentStore() && segmentStore != null) {
- log.info("Initializing SegmentNodeStore with custom persistence [{}]", segmentStore);
- builder.withCustomPersistence(segmentStore);
- }
-
- if (configuration.isStandbyInstance()) {
- builder.withSnfeListener(IGNORE_SNFE);
- }
-
- final FileStore store;
- try {
- store = builder.build();
- } catch (InvalidFileStoreVersionException e) {
- log.error("The storage format is not compatible with this version of Oak Segment Tar", e);
- return null;
- }
- // store should be closed last
- closeables.add(store);
-
- // Listen for Executor services on the whiteboard
-
- WhiteboardExecutor executor = new WhiteboardExecutor();
- executor.start(whiteboard);
- closeables.add(executor);
-
- // Expose stats about the segment cache
-
- CacheStatsMBean segmentCacheStats = addRoleToName(store.getSegmentCacheStats(), role);
- closeables.add(registrations.registerMBean(
- CacheStatsMBean.class,
- segmentCacheStats,
- CacheStats.TYPE,
- segmentCacheStats.getName()
- ));
-
- // Expose stats about the string and template caches
-
- CacheStatsMBean stringCacheStats = addRoleToName(store.getStringCacheStats(), role);
- closeables.add(registrations.registerMBean(
- CacheStatsMBean.class,
- stringCacheStats,
- CacheStats.TYPE,
- stringCacheStats.getName()
- ));
-
- CacheStatsMBean templateCacheStats = addRoleToName(store.getTemplateCacheStats(), role);
- closeables.add(registrations.registerMBean(
- CacheStatsMBean.class,
- templateCacheStats,
- CacheStats.TYPE,
- templateCacheStats.getName()
- ));
-
- WriterCacheManager cacheManager = builder.getCacheManager();
- CacheStatsMBean stringDeduplicationCacheStats = addRoleToName(cacheManager.getStringCacheStats(), role);
- if (stringDeduplicationCacheStats != null) {
- closeables.add(registrations.registerMBean(
- CacheStatsMBean.class,
- stringDeduplicationCacheStats,
- CacheStats.TYPE,
- stringDeduplicationCacheStats.getName()
- ));
- }
-
- CacheStatsMBean templateDeduplicationCacheStats = addRoleToName(cacheManager.getTemplateCacheStats(), role);
- if (templateDeduplicationCacheStats != null) {
- closeables.add(registrations.registerMBean(
- CacheStatsMBean.class,
- templateDeduplicationCacheStats,
- CacheStats.TYPE,
- templateDeduplicationCacheStats.getName()
- ));
- }
-
- CacheStatsMBean nodeDeduplicationCacheStats = addRoleToName(cacheManager.getNodeCacheStats(), role);
- if (nodeDeduplicationCacheStats != null) {
- closeables.add(registrations.registerMBean(
- CacheStatsMBean.class,
- nodeDeduplicationCacheStats,
- CacheStats.TYPE,
- nodeDeduplicationCacheStats.getName()
- ));
- }
-
- // Expose an MBean to managing and monitoring garbage collection
- final FileStoreGCMonitor monitor = new FileStoreGCMonitor(Clock.SIMPLE);
- closeables.add(registrations.register(
- GCMonitor.class,
- monitor
- ));
- if (!configuration.isStandbyInstance()) {
- closeables.add(registrations.registerMBean(
- SegmentRevisionGC.class,
- new SegmentRevisionGCMBean(store, gcOptions, monitor),
- SegmentRevisionGC.TYPE,
- "Segment node store revision garbage collection"
- ));
- }
-
- Runnable cancelGC = new Runnable() {
+ return SegmentNodeStoreRegistrar.registerSegmentNodeStore(new SegmentNodeStoreRegistrar.Configuration() {
- @Override
- public void run() {
- store.cancelGC();
+ int roundToNextPowerOfTwo(int size) {
+ return 1 << (32 - Integer.numberOfLeadingZeros(Math.max(0, size - 1)));
}
- };
- Supplier statusMessage = new Supplier() {
-
- @Override
- public String get() {
- return monitor.getStatus();
+ String getMode() {
+ String mode = configuration.tarmk_mode();
+ if (isNullOrEmpty(mode)) {
+ return System.getProperty("tarmk.mode", System.getProperty("sun.arch.data.model", "32"));
+ }
+ return mode;
}
- };
- closeables.add(registrations.registerMBean(
- RevisionGCMBean.class,
- new RevisionGC(store.getGCRunner(), cancelGC, statusMessage, executor),
- RevisionGCMBean.TYPE,
- "Revision garbage collection"
- ));
-
- // Expose statistics about the FileStore
-
- closeables.add(registrations.registerMBean(
- FileStoreStatsMBean.class,
- store.getStats(),
- FileStoreStatsMBean.TYPE,
- "FileStore statistics"
- ));
-
- // register segment node store
-
- SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = SegmentNodeStoreBuilders.builder(store).withStatisticsProvider(statisticsProvider);
- if (configuration.isStandbyInstance() || !configuration.isPrimarySegmentStore()) {
- segmentNodeStoreBuilder.dispatchChanges(false);
- }
- SegmentNodeStore segmentNodeStore = segmentNodeStoreBuilder.build();
-
- if (configuration.isPrimarySegmentStore()) {
- ObserverTracker observerTracker = new ObserverTracker(segmentNodeStore);
- observerTracker.start(context.getBundleContext());
- closeables.add(observerTracker);
- }
-
- if (configuration.isPrimarySegmentStore()) {
- closeables.add(registrations.registerMBean(
- CheckpointMBean.class,
- new SegmentCheckpointMBean(segmentNodeStore),
- CheckpointMBean.TYPE,
- "Segment node store checkpoint management"
- ));
- }
-
- if (descriptors) {
- // ensure a clusterId is initialized
- // and expose it as 'oak.clusterid' repository descriptor
- GenericDescriptors clusterIdDesc = new GenericDescriptors();
- clusterIdDesc.put(
- ClusterRepositoryInfo.OAK_CLUSTERID_REPOSITORY_DESCRIPTOR_KEY,
- new SimpleValueFactory().createValue(getOrCreateId(segmentNodeStore)),
- true,
- false
- );
- closeables.add(registrations.register(Descriptors.class, clusterIdDesc));
- // Register "discovery lite" descriptors
- closeables.add(registrations.register(Descriptors.class, new SegmentDiscoveryLiteDescriptors(segmentNodeStore)));
- }
-
- // If a shared data store register the repo id in the data store
- if (configuration.isPrimarySegmentStore() && isShared(blobStore)) {
- SharedDataStore sharedDataStore = (SharedDataStore) blobStore;
- try {
- sharedDataStore.addMetadataRecord(new ByteArrayInputStream(new byte[0]), SharedStoreRecordType.REPOSITORY.getNameFromId(getOrCreateId(segmentNodeStore)));
- } catch (Exception e) {
- throw new IOException("Could not register a unique repositoryId", e);
- }
- if (blobStore instanceof BlobTrackingStore) {
- BlobTrackingStore trackingStore = (BlobTrackingStore) blobStore;
- if (trackingStore.getTracker() != null) {
- trackingStore.getTracker().close();
+ int getCacheSize(String name, int otherwise) {
+ Integer size = Integer.getInteger(name);
+ if (size != null) {
+ return size;
}
- trackingStore.addTracker(new BlobIdTracker(configuration.getRepositoryHome(), getOrCreateId(segmentNodeStore), configuration.getBlobSnapshotInterval(), sharedDataStore));
+ return otherwise;
}
- }
-
- if (configuration.isPrimarySegmentStore() && blobStore instanceof GarbageCollectableBlobStore) {
- BlobGarbageCollector gc = new MarkSweepGarbageCollector(
- new SegmentBlobReferenceRetriever(store),
- (GarbageCollectableBlobStore) blobStore,
- executor,
- TimeUnit.SECONDS.toMillis(configuration.getBlobGcMaxAge()),
- getOrCreateId(segmentNodeStore),
- whiteboard,
- statisticsProvider
- );
- closeables.add(registrations.registerMBean(
- BlobGCMBean.class,
- new BlobGC(gc, executor),
- BlobGCMBean.TYPE,
- "Segment node store blob garbage collection"
- ));
- }
-
- // Expose an MBean for backup/restore operations
-
- closeables.add(registrations.registerMBean(
- FileStoreBackupRestoreMBean.class,
- new FileStoreBackupRestoreImpl(
- segmentNodeStore,
- store.getRevisions(),
- store.getReader(),
- configuration.getBackupDirectory(),
- executor
- ),
- FileStoreBackupRestoreMBean.TYPE,
- "Segment node store backup/restore"
- ));
-
- // Expose statistics about the SegmentNodeStore
-
- closeables.add(registrations.registerMBean(
- SegmentNodeStoreStatsMBean.class,
- segmentNodeStore.getStats(),
- SegmentNodeStoreStatsMBean.TYPE,
- "SegmentNodeStore statistics"
- ));
-
- if (configuration.isPrimarySegmentStore()) {
- log.info("Primary SegmentNodeStore initialized");
- } else {
- log.info("Secondary SegmentNodeStore initialized, role={}", role);
- }
-
- // Register a factory service to expose the FileStore
- closeables.add(registrations.register(
- SegmentStoreProvider.class,
- new DefaultSegmentStoreProvider(store)
- ));
-
- if (configuration.isStandbyInstance()) {
- return segmentNodeStore;
- }
-
- if (configuration.isPrimarySegmentStore()) {
- Map props = new HashMap();
- props.put(Constants.SERVICE_PID, SegmentNodeStore.class.getName());
- props.put("oak.nodestore.description", new String[] {"nodeStoreType=segment"});
- closeables.add(registrations.register(NodeStore.class, segmentNodeStore, props));
- }
-
- return segmentNodeStore;
- }
-
- @Deactivate
- public void deactivate() {
- closeQuietly(closer);
- closer = null;
- }
- private static CacheStatsMBean addRoleToName(CacheStatsMBean cacheStatsMBean, String role) {
- return new CacheStatsMBeanWrapper(cacheStatsMBean) {
@Override
- public String getName() {
- return RoleUtils.maybeAppendRole(super.getName(), role);
+ public boolean isPrimarySegmentStore() {
+ return true;
}
- };
- }
-}
-
-/**
- * Encapsulates a {@link Closer} and makes it easier to track the lifecycle
- * of entities that can be disposed.
- */
-class Closeables implements Closeable {
-
- private final Closer closer;
-
- Closeables(Closer closer) {
- this.closer = closer;
- }
-
- void add(Closeable c) {
- closer.register(c);
- }
-
- void add(final AbstractServiceTracker> t) {
- add(new Closeable() {
@Override
- public void close() {
- t.stop();
+ public boolean isSecondarySegmentStore() {
+ return false;
}
- });
- }
-
- void add(final Registration r) {
- add(new Closeable() {
-
@Override
- public void close() {
- r.unregister();
+ public boolean isStandbyInstance() {
+ return configuration.standby();
}
- });
- }
-
- void add(final ObserverTracker t) {
- add(new Closeable() {
-
@Override
- public void close() {
- t.stop();
+ public String getRole() {
+ return null;
}
- });
- }
-
- @Override
- public void close() throws IOException {
- closer.close();
- }
-
-}
-
-/**
- * Allows simple access to the configuration of this component. Provides
- * default values for unspecified properties and type conversion.
- */
-class Configuration {
-
- private static int roundToNextPowerOfTwo(int size) {
- return 1 << (32 - Integer.numberOfLeadingZeros(Math.max(0, size - 1)));
- }
-
- private final ComponentContext context;
-
- private final String role;
-
- Configuration(ComponentContext context) {
- this(context, null);
- }
-
- Configuration(ComponentContext context, String role) {
- this.context = context;
- this.role = role;
- }
-
- String property(String name) {
- return lookupConfigurationThenFramework(context, name);
- }
-
- /**
- * Chooses repository home directory name based on repository.home
- * property, defaulting to repository if property is not set.
- *
- * @return repository home directory name.
- */
- String getRepositoryHome() {
- String root = property(REPOSITORY_HOME_DIRECTORY);
- if (isNullOrEmpty(root)) {
- return "repository";
- }
- return root;
- }
-
- /**
- * Creates a new sub-directory relative to {@link #getRepositoryHome()} for
- * storing segments.
- *
- * @return directory for storing segments.
- */
- File getSegmentDirectory() {
- return new File(getRepositoryHome(), appendRole("segmentstore"));
- }
-
- /**
- * Creates a new sub-directory relative to {@link #getRepositoryHome()} for
- * storing repository backups.
- *
- * @return directory for storing repository backups.
- */
- File getBackupDirectory() {
- String backupDirectory = property(BACKUP_DIRECTORY);
- if (backupDirectory != null) {
- return new File(backupDirectory);
- }
- return new File(getRepositoryHome(), appendRole("segmentstore-backup"));
- }
-
- int getSegmentCacheSize() {
- return toInteger(getCacheSize(SEGMENT_CACHE_SIZE), DEFAULT_SEGMENT_CACHE_MB);
- }
-
- int getStringCacheSize() {
- return toInteger(getCacheSize(STRING_CACHE_SIZE), DEFAULT_STRING_CACHE_MB);
- }
-
- int getTemplateCacheSize() {
- return toInteger(getCacheSize(TEMPLATE_CACHE_SIZE), DEFAULT_TEMPLATE_CACHE_MB);
- }
-
- int getStringDeduplicationCacheSize() {
- return toInteger(getCacheSize(STRING_DEDUPLICATION_CACHE_SIZE), DEFAULT_STRING_CACHE_SIZE_OSGi);
- }
-
- int getTemplateDeduplicationCacheSize() {
- return toInteger(getCacheSize(TEMPLATE_DEDUPLICATION_CACHE_SIZE), DEFAULT_TEMPLATE_CACHE_SIZE_OSGi);
- }
-
- int getNodeDeduplicationCacheSize() {
- return roundToNextPowerOfTwo(toInteger(getCacheSize(NODE_DEDUPLICATION_CACHE_SIZE), DEFAULT_NODE_CACHE_SIZE_OSGi));
- }
+ @Override
+ public int getRetainedGenerations() {
+ return configuration.compaction_retainedGenerations();
+ }
- boolean getPauseCompaction() {
- return toBoolean(property(PAUSE_COMPACTION), PAUSE_DEFAULT);
- }
+ @Override
+ public int getDefaultRetainedGenerations() {
+ return RETAINED_GENERATIONS_DEFAULT;
+ }
- int getRetryCount() {
- return toInteger(property(COMPACTION_RETRY_COUNT), RETRY_COUNT_DEFAULT);
- }
+ @Override
+ public boolean getPauseCompaction() {
+ return configuration.pauseCompaction();
+ }
- int getForceCompactionTimeout() {
- return toInteger(property(COMPACTION_FORCE_TIMEOUT), FORCE_TIMEOUT_DEFAULT);
- }
+ @Override
+ public int getRetryCount() {
+ return configuration.compaction_retryCount();
+ }
- int getRetainedGenerations() {
- return toInteger(property(RETAINED_GENERATIONS), RETAINED_GENERATIONS_DEFAULT);
- }
+ @Override
+ public int getForceCompactionTimeout() {
+ return configuration.compaction_force_timeout();
+ }
- long getSizeDeltaEstimation() {
- return toLong(property(COMPACTION_SIZE_DELTA_ESTIMATION), SIZE_DELTA_ESTIMATION_DEFAULT);
- }
+ @Override
+ public long getSizeDeltaEstimation() {
+ return configuration.compaction_sizeDeltaEstimation();
+ }
- int getMemoryThreshold() {
- return toInteger(property(MEMORY_THRESHOLD), MEMORY_THRESHOLD_DEFAULT);
- }
+ @Override
+ public int getMemoryThreshold() {
+ return configuration.compaction_memoryThreshold();
+ }
- boolean getDisableEstimation() {
- return toBoolean(property(COMPACTION_DISABLE_ESTIMATION), DISABLE_ESTIMATION_DEFAULT);
- }
+ @Override
+ public boolean getDisableEstimation() {
+ return configuration.compaction_disableEstimation();
+ }
- String getCompactionGainThreshold() {
- return property("compaction.gainThreshold");
- }
+ @Override
+ public long getGCProcessLog() {
+ return configuration.compaction_progressLog();
+ }
- long getGCProcessLog() {
- return toLong(property(GC_PROGRESS_LOG), GC_PROGRESS_LOG_DEFAULT);
- }
+ @Override
+ public File getSegmentDirectory() {
+ return new File(getRepositoryHome(), "segmentstore");
+ }
- int getMaxFileSize() {
- return toInteger(property(SIZE), DEFAULT_MAX_FILE_SIZE);
- }
+ @Override
+ public int getSegmentCacheSize() {
+ Integer size = Integer.getInteger("segmentCache.size");
+ if (size != null) {
+ return size;
+ }
+ return configuration.segmentCache_size();
+ }
- String getMode() {
- String mode = property(MODE);
- if (mode != null) {
- return mode;
- }
- return System.getProperty(MODE, System.getProperty("sun.arch.data.model", "32"));
- }
+ @Override
+ public int getStringCacheSize() {
+ return getCacheSize("stringCache.size", configuration.stringCache_size());
+ }
- boolean getMemoryMapping() {
- return getMode().equals("64");
- }
+ @Override
+ public int getTemplateCacheSize() {
+ Integer size = Integer.getInteger("templateCache.size");
+ if (size != null) {
+ return size;
+ }
+ return configuration.templateCache_size();
+ }
- long getBlobSnapshotInterval() {
- return toLong(property(PROP_BLOB_SNAPSHOT_INTERVAL), DEFAULT_BLOB_SNAPSHOT_INTERVAL);
- }
+ @Override
+ public int getStringDeduplicationCacheSize() {
+ Integer size = Integer.getInteger("stringDeduplicationCache.size");
+ if (size != null) {
+ return size;
+ }
+ return configuration.stringDeduplicationCache_size();
+ }
- boolean isStandbyInstance() {
- return toBoolean(property(STANDBY), false);
- }
+ @Override
+ public int getTemplateDeduplicationCacheSize() {
+ Integer size = Integer.getInteger("templateDeduplicationCache.size");
+ if (size != null) {
+ return size;
+ }
+ return configuration.templateDeduplicationCache_size();
+ }
- boolean hasCustomBlobStore() {
- return toBoolean(property(CUSTOM_BLOB_STORE), false);
- }
+ @Override
+ public int getNodeDeduplicationCacheSize() {
+ Integer size = Integer.getInteger("nodeDeduplicationCache.size");
+ if (size != null) {
+ return roundToNextPowerOfTwo(size);
+ }
+ return roundToNextPowerOfTwo(configuration.nodeDeduplicationCache_size());
+ }
- boolean hasCustomSegmentStore() {
- return toBoolean(property(CUSTOM_SEGMENT_STORE), false);
- }
+ @Override
+ public int getMaxFileSize() {
+ return configuration.tarmk_size();
+ }
- long getBlobGcMaxAge() {
- return toLong(property(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE);
- }
+ @Override
+ public boolean getMemoryMapping() {
+ return getMode().equals("64");
+ }
- boolean isPrimarySegmentStore() {
- return role == null;
- }
+ @Override
+ public boolean hasCustomBlobStore() {
+ return configuration.customBlobStore();
+ }
- private String appendRole(String name) {
- if (role == null) {
- return name;
- } else {
- return name + "-" + role;
- }
- }
+ @Override
+ public boolean hasCustomSegmentStore() {
+ return configuration.customSegmentStore();
+ }
- private String getCacheSize(String propertyName) {
- String cacheSize = property(propertyName);
- if (cacheSize != null) {
- return cacheSize;
- }
- return System.getProperty(propertyName);
- }
+ @Override
+ public boolean registerDescriptors() {
+ return true;
+ }
-}
+ @Override
+ public String getRepositoryHome() {
+ String repositoryHome = OsgiUtil.lookupConfigurationThenFramework(context, "repository.home");
+ if (isNullOrEmpty(repositoryHome)) {
+ return "repository";
+ }
+ return repositoryHome;
+ }
-/**
- * Performs registrations of services and MBean in a uniform way. Augments
- * the metadata of services and MBeans with an optionally provided role
- * name.
- */
-class Registrations {
+ @Override
+ public long getBlobSnapshotInterval() {
+ return configuration.blobTrackSnapshotIntervalInSecs();
+ }
- private final Whiteboard whiteboard;
+ @Override
+ public long getBlobGcMaxAge() {
+ return configuration.blobGcMaxAgeInSecs();
+ }
- private final String role;
+ @Override
+ public File getBackupDirectory() {
+ String backupDirectory = configuration.repository_backup_dir();
+ if (isNullOrEmpty(backupDirectory)) {
+ return new File(getRepositoryHome(), "segmentstore-backup");
+ }
+ return new File(backupDirectory);
+ }
- Registrations(Whiteboard whiteboard, String role) {
- this.whiteboard = whiteboard;
- this.role = role;
- }
+ @Override
+ public Whiteboard getWhiteboard() {
+ return whiteboard;
+ }
- Registration registerMBean(Class clazz, T bean, String type, String name) {
- return registerMBean(clazz, bean, type, name, new HashMap());
- }
+ @Override
+ public Closer getCloser() {
+ return closer;
+ }
- Registration registerMBean(Class clazz, T bean, String type, String name, Map attributes) {
- return WhiteboardUtils.registerMBean(whiteboard, clazz, bean, type, maybeAppendRole(name), maybePutRoleAttribute(attributes));
- }
+ @Override
+ public Logger getLogger() {
+ return logger;
+ }
- Registration register(Class clazz, T service) {
- return register(clazz, service, new HashMap());
- }
+ @Override
+ public StatisticsProvider getStatisticsProvider() {
+ return statisticsProvider;
+ }
- Registration register(Class clazz, T service, Map properties) {
- return whiteboard.register(clazz, service, maybePutRoleProperty(properties));
- }
+ @Override
+ public BlobStore getBlobStore() {
+ return blobStore;
+ }
- private String maybeAppendRole(String name) {
- return RoleUtils.maybeAppendRole(name, role);
- }
+ @Override
+ public SegmentNodeStorePersistence getSegmentNodeStorePersistence() {
+ return segmentStore;
+ }
- private String jmxRole() {
- return role.replaceAll(":", "-");
- }
+ @Override
+ public BundleContext getBundleContext() {
+ return context.getBundleContext();
+ }
- private Map maybePutRoleAttribute(Map attributes) {
- if (role != null) {
- attributes.put("role", jmxRole());
- }
- return attributes;
+ });
}
- private Map maybePutRoleProperty(Map attributes) {
- if (role != null) {
- attributes.put("role", role);
- }
- return attributes;
+ @Deactivate
+ public void deactivate() {
+ closeQuietly(closer);
}
-}
+}
\ No newline at end of file
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java
index 8fb994456c..63da5af0eb 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java
@@ -17,9 +17,9 @@
package org.apache.jackrabbit.oak.segment.osgi;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,8 +30,8 @@ import org.slf4j.LoggerFactory;
* detected problem and hinting at a possible solution.
*/
@Component(
- policy = ConfigurationPolicy.REQUIRE,
- configurationPid = "org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService"
+ configurationPolicy = ConfigurationPolicy.REQUIRE,
+ configurationPid = "org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService"
)
public class SegmentNodeStoreServiceDeprecationError {
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java
index 4da5b6004f..a456f026ff 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java
@@ -16,9 +16,9 @@
*/
package org.apache.jackrabbit.oak.segment.osgi;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,8 +29,8 @@ import org.slf4j.LoggerFactory;
* detected problem and hinting at a possible solution.
*/
@Component(
- policy = ConfigurationPolicy.REQUIRE,
- configurationPid = "org.apache.jackrabbit.oak.plugins.segment.standby.store.StandbyStoreService"
+ configurationPolicy = ConfigurationPolicy.REQUIRE,
+ configurationPid = "org.apache.jackrabbit.oak.plugins.segment.standby.store.StandbyStoreService"
)
public class StandbyStoreServiceDeprecationError {
diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
index 8b7da9c6ba..3847a8c11b 100644
--- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
+++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
@@ -17,25 +17,15 @@
package org.apache.jackrabbit.oak.segment.standby.store;
-import static java.lang.String.valueOf;
-import static org.apache.felix.scr.annotations.ReferencePolicy.STATIC;
-import static org.apache.felix.scr.annotations.ReferencePolicyOption.GREEDY;
+import static org.osgi.service.component.annotations.ReferencePolicy.STATIC;
+import static org.osgi.service.component.annotations.ReferencePolicyOption.GREEDY;
-import java.io.Closeable;
import java.io.File;
import java.util.Dictionary;
import java.util.Hashtable;
import com.google.common.base.StandardSystemProperty;
import com.google.common.io.Closer;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.PropertyOption;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.jackrabbit.oak.commons.PropertiesUtil;
import org.apache.jackrabbit.oak.segment.SegmentStore;
import org.apache.jackrabbit.oak.segment.SegmentStoreProvider;
import org.apache.jackrabbit.oak.segment.file.FileStore;
@@ -43,71 +33,99 @@ import org.apache.jackrabbit.oak.segment.standby.client.StandbyClientSync;
import org.apache.jackrabbit.oak.segment.standby.server.StandbyServerSync;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@Property(name = "org.apache.sling.installer.configuration.persist", label = "Persist configuration", description = "Must be always disabled to avoid storing the configuration in the repository", boolValue = false)
-@Component(metatype = true, policy = ConfigurationPolicy.REQUIRE)
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = StandbyStoreService.Configuration.class)
public class StandbyStoreService {
- private final Logger log = LoggerFactory.getLogger(getClass());
+ private static final Logger log = LoggerFactory.getLogger(StandbyStoreService.class);
- private static final String MODE_PRIMARY = "primary";
-
- private static final String MODE_STANDBY = "standby";
-
- public static final String MODE_DEFAULT = MODE_PRIMARY;
-
- @Property(options = {
- @PropertyOption(name = MODE_PRIMARY, value = MODE_PRIMARY),
- @PropertyOption(name = MODE_STANDBY, value = MODE_STANDBY)},
- value = MODE_DEFAULT)
- public static final String MODE = "mode";
-
- public static final int PORT_DEFAULT = 8023;
-
- @Property(intValue = PORT_DEFAULT)
- public static final String PORT = "port";
-
- public static final String PRIMARY_HOST_DEFAULT = "127.0.0.1";
-
- @Property(value = PRIMARY_HOST_DEFAULT)
- public static final String PRIMARY_HOST = "primary.host";
-
- public static final int INTERVAL_DEFAULT = 5;
-
- @Property(intValue = INTERVAL_DEFAULT)
- public static final String INTERVAL = "interval";
-
- public static final String[] ALLOWED_CLIENT_IP_RANGES_DEFAULT = new String[] {};
-
- @Property(cardinality = Integer.MAX_VALUE)
- public static final String ALLOWED_CLIENT_IP_RANGES = "primary.allowed-client-ip-ranges";
-
- public static final boolean SECURE_DEFAULT = false;
-
- @Property(boolValue = SECURE_DEFAULT)
- public static final String SECURE = "secure";
-
- public static final int READ_TIMEOUT_DEFAULT = 60000;
-
- @Property(intValue = READ_TIMEOUT_DEFAULT)
- public static final String READ_TIMEOUT = "standby.readtimeout";
+ private static final int BLOB_CHUNK_SIZE = Integer.getInteger("oak.standby.blob.chunkSize", 1024 * 1024);
- public static final boolean AUTO_CLEAN_DEFAULT = true;
+ @ObjectClassDefinition(
+ name = "Apache Jackrabbit Oak Segment Tar Cold Standby Service",
+ description = "Provides continuous backups of repositories based on Segment Tar"
+ )
+ @interface Configuration {
+
+ @AttributeDefinition(
+ name = "Persist configuration",
+ description = "Must be always disabled to avoid storing the configuration in the repository"
+ )
+ boolean org_apache_sling_installer_configuration_persist() default false;
+
+ @AttributeDefinition(
+ name = "Mode",
+ description = "TarMK Cold Standby mode (primary or standby)",
+ options = {
+ @Option(label = "primary", value = "primary"),
+ @Option(label = "standby", value = "standby")}
+ )
+ String mode() default "primary";
+
+ @AttributeDefinition(
+ name = "Port",
+ description = "TCP/IP port to use"
+ )
+ int port() default 8023;
+
+ @AttributeDefinition(
+ name = "Primary Host",
+ description = "Primary host (standby mode only)"
+ )
+ String primary_host() default "127.0.0.1";
+
+ @AttributeDefinition(
+ name = "Sync interval (seconds)",
+ description = "Sync interval in seconds (standby mode only)"
+ )
+ int interval() default 5;
+
+ @AttributeDefinition(
+ name = "Allowed IP-Ranges",
+ description = "Accept incoming requests for these host names and IP-ranges only (primary mode only)",
+ cardinality = Integer.MAX_VALUE
+ )
+ String[] primary_allowed$_$client$_$ip$_$ranges() default {};
+
+ @AttributeDefinition(
+ name = "Secure",
+ description = "Use secure connections"
+ )
+ boolean secure() default false;
+
+ @AttributeDefinition(
+ name = "Standby Read Timeout",
+ description = "Timeout for requests issued from the standby instance in milliseconds"
+ )
+ int standby_readtimeout() default 60000;
+
+ @AttributeDefinition(
+ name = "Standby Automatic Cleanup",
+ description = "Call the cleanup method when the root segment Garbage Collector (GC) generation number increases"
+ )
+ boolean standby_autoclean() default true;
- @Property(boolValue = AUTO_CLEAN_DEFAULT)
- public static final String AUTO_CLEAN = "standby.autoclean";
+ }
@Reference(policy = STATIC, policyOption = GREEDY)
private SegmentStoreProvider storeProvider = null;
-
- private static final int BLOB_CHUNK_SIZE = Integer.getInteger("oak.standby.blob.chunkSize", 1024 * 1024);
private final Closer closer = Closer.create();
@Activate
- private void activate(ComponentContext context) {
+ private void activate(ComponentContext context, Configuration config) {
SegmentStore segmentStore = storeProvider.getSegmentStore();
if (!(segmentStore instanceof FileStore)) {
@@ -116,15 +134,15 @@ public class StandbyStoreService {
FileStore fileStore = (FileStore) segmentStore;
- String mode = valueOf(context.getProperties().get(MODE));
+ String mode = config.mode();
- if (MODE_PRIMARY.equals(mode)) {
- bootstrapMaster(context, fileStore);
+ if (mode.equals("primary")) {
+ bootstrapMaster(config, fileStore);
return;
}
- if (MODE_STANDBY.equals(mode)) {
- bootstrapSlave(context, fileStore);
+ if (mode.equals("standby")) {
+ bootstrapSlave(context, config, fileStore);
return;
}
@@ -136,11 +154,10 @@ public class StandbyStoreService {
closer.close();
}
- private void bootstrapMaster(ComponentContext context, FileStore fileStore) {
- Dictionary, ?> props = context.getProperties();
- int port = PropertiesUtil.toInteger(props.get(PORT), PORT_DEFAULT);
- String[] ranges = PropertiesUtil.toStringArray(props.get(ALLOWED_CLIENT_IP_RANGES), ALLOWED_CLIENT_IP_RANGES_DEFAULT);
- boolean secure = PropertiesUtil.toBoolean(props.get(SECURE), SECURE_DEFAULT);
+ private void bootstrapMaster(Configuration config, FileStore fileStore) {
+ int port = config.port();
+ String[] ranges = config.primary_allowed$_$client$_$ip$_$ranges();
+ boolean secure = config.secure();
StandbyServerSync standbyServerSync = new StandbyServerSync(port, fileStore, BLOB_CHUNK_SIZE, ranges, secure);
closer.register(standbyServerSync);
@@ -149,14 +166,13 @@ public class StandbyStoreService {
log.info("Started primary on port {} with allowed IP ranges {}", port, ranges);
}
- private void bootstrapSlave(ComponentContext context, FileStore fileStore) {
- Dictionary, ?> props = context.getProperties();
- int port = PropertiesUtil.toInteger(props.get(PORT), PORT_DEFAULT);
- long interval = PropertiesUtil.toInteger(props.get(INTERVAL), INTERVAL_DEFAULT);
- String host = PropertiesUtil.toString(props.get(PRIMARY_HOST), PRIMARY_HOST_DEFAULT);
- boolean secure = PropertiesUtil.toBoolean(props.get(SECURE), SECURE_DEFAULT);
- int readTimeout = PropertiesUtil.toInteger(props.get(READ_TIMEOUT), READ_TIMEOUT_DEFAULT);
- boolean clean = PropertiesUtil.toBoolean(props.get(AUTO_CLEAN), AUTO_CLEAN_DEFAULT);
+ private void bootstrapSlave(ComponentContext context, Configuration config, FileStore fileStore) {
+ int port = config.port();
+ long interval = config.interval();
+ String host = config.primary_host();
+ boolean secure = config.secure();
+ int readTimeout = config.standby_readtimeout();
+ boolean clean = config.standby_autoclean();
StandbyClientSync standbyClientSync = new StandbyClientSync(host, port, fileStore, secure, readTimeout, clean, new File(StandardSystemProperty.JAVA_IO_TMPDIR.value()));
closer.register(standbyClientSync);
@@ -165,20 +181,9 @@ public class StandbyStoreService {
dictionary.put("scheduler.period", interval);
dictionary.put("scheduler.concurrent", false);
ServiceRegistration registration = context.getBundleContext().registerService(Runnable.class.getName(), standbyClientSync, dictionary);
- closer.register(asCloseable(registration));
+ closer.register(registration::unregister);
log.info("Started standby on port {} with {}s sync frequency", port, interval);
}
- private static Closeable asCloseable(final ServiceRegistration r) {
- return new Closeable() {
-
- @Override
- public void close() {
- r.unregister();
- }
-
- };
- }
-
}
diff --git a/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties b/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties
deleted file mode 100644
index 9dc70b8bfa..0000000000
--- a/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# 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.
-#
-
-# suppress inspection "UnusedProperty" for whole file
-
-org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService.name = Apache Jackrabbit Oak Segment Tar Cold Standby Service
-org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService.description = Provides continuous backups of repositories based on Segment Tar
-
-mode.name = Mode
-mode.description = TarMK Cold Standby mode (primary or standby)
-
-port.name = Port
-port.description = TCP/IP port to use
-
-primary.host.name = Primary Host
-primary.host.description = Primary host (standby mode only)
-
-interval.name = Sync interval (seconds)
-interval.description = Sync interval in seconds (standby mode only)
-
-primary.allowed-client-ip-ranges.name = Allowed IP-Ranges
-primary.allowed-client-ip-ranges.description = Accept incoming requests for these host names and IP-ranges only (primary mode only)
-
-secure.name = Secure
-secure.description = Use secure connections
-
-org.apache.sling.installer.configuration.persist.name = Persist configuration
-org.apache.sling.installer.configuration.persist.description = Must be always disabled to avoid storing the configuration in the repository
-
-standby.readtimeout.name = Standby Read Timeout
-standby.readtimeout.description = Timeout for requests issued from the standby instance in milliseconds
-
-standby.autoclean.name = Standby Automatic Cleanup
-standby.autoclean.description = Call the cleanup method when the root segment Garbage Collector (GC) generation number increases
\ No newline at end of file
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java
index 68b2d0a9a5..a4d3b5ed0e 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java
@@ -35,9 +35,9 @@ public class SegmentNodeStoreFactoryTest extends SegmentNodeStoreServiceTest {
protected void registerSegmentNodeStoreService(boolean customBlobStore) {
Map properties = newHashMap();
- properties.put(SegmentNodeStoreFactory.ROLE, "some-role");
- properties.put(SegmentNodeStoreFactory.CUSTOM_BLOB_STORE, customBlobStore);
- properties.put(SegmentNodeStoreService.REPOSITORY_HOME_DIRECTORY, folder.getRoot().getAbsolutePath());
+ properties.put("role", "some-role");
+ properties.put("customBlobStore", customBlobStore);
+ properties.put("repository.home", folder.getRoot().getAbsolutePath());
segmentNodeStoreFactory = context.registerInjectActivateService(new SegmentNodeStoreFactory(), properties);
}
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java
index 77eb0a6bdc..f665c6ab64 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java
@@ -21,6 +21,7 @@ package org.apache.jackrabbit.oak.segment.osgi;
import java.io.InputStream;
+import javax.print.attribute.HashPrintServiceAttributeSet;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -47,6 +48,10 @@ class ComponentDescriptor {
return element.hasAttribute(name) && element.getAttribute(name).equals(value);
}
+ private static boolean hasNoAttribute(Element element, String name) {
+ return !element.hasAttribute(name);
+ }
+
boolean hasName(String name) {
return hasAttribute(root, "name", name);
}
@@ -182,6 +187,8 @@ class ComponentDescriptor {
private String unbind;
+ private String field;
+
private HasReference(String name) {
this.name = name;
}
@@ -246,6 +253,11 @@ class ComponentDescriptor {
return this;
}
+ HasReference withField(String field) {
+ this.field = field;
+ return this;
+ }
+
boolean check() {
NodeList references = root.getElementsByTagName("reference");
for (int i = 0; i < references.getLength(); i++) {
@@ -254,10 +266,10 @@ class ComponentDescriptor {
if (iface != null && !hasAttribute(reference, "interface", iface)) {
return false;
}
- if (cardinality != null && !hasAttribute(reference, "cardinality", cardinality)) {
+ if (cardinality != null && !hasValidCardinality(reference)) {
return false;
}
- if (policy != null && !hasAttribute(reference, "policy", policy)) {
+ if (policy != null && !hasValidPolicy(reference)) {
return false;
}
if (policyOption != null && !hasAttribute(reference, "policy-option", policyOption)) {
@@ -272,12 +284,29 @@ class ComponentDescriptor {
if (unbind != null && !hasAttribute(reference, "unbind", unbind)) {
return false;
}
+ if (field != null && !hasAttribute(reference, "field", field)) {
+ return false;
+ }
return true;
}
}
return false;
}
+ private boolean hasValidCardinality(Element reference) {
+ if (cardinality.equals("1..1") && hasNoAttribute(reference, "cardinality")) {
+ return true;
+ }
+ return hasAttribute(reference, "cardinality", cardinality);
+ }
+
+ private boolean hasValidPolicy(Element reference) {
+ if (policy.equals("static") && hasNoAttribute(reference, "policy")) {
+ return true;
+ }
+ return hasAttribute(reference, "policy", policy);
+ }
+
}
HasReference hasReference(String name) {
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java
index 965898927f..d1708c4127 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java
@@ -52,36 +52,32 @@ public class SegmentNodeStoreFactoryTest {
.withStaticPolicy()
.withGreedyPolicyOption()
.withTarget("(&(!(split.blobstore=old))(!(split.blobstore=new)))")
- .withBind("bindBlobStore")
- .withUnbind("unbindBlobStore")
+ .withField("blobStore")
.check());
assertTrue(cd.hasReference("segmentStore")
.withInterface("org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence")
.withOptionalUnaryCardinality()
.withStaticPolicy()
.withGreedyPolicyOption()
- .withBind("bindSegmentStore")
- .withUnbind("unbindSegmentStore")
+ .withField("segmentStore")
.check());
assertTrue(cd.hasReference("statisticsProvider")
.withInterface("org.apache.jackrabbit.oak.stats.StatisticsProvider")
.withMandatoryUnaryCardinality()
.withStaticPolicy()
- .withBind("bindStatisticsProvider")
- .withUnbind("unbindStatisticsProvider")
+ .withField("statisticsProvider")
.check());
}
@Test
public void testMetatypeInformation() throws Exception {
- MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory.xml"));
+ MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory$Configuration.xml"));
assertTrue(mi.hasDesignate()
- .withPid("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory")
.withFactoryPid("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory")
- .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory")
+ .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory$Configuration")
.check());
- ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory");
+ ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory$Configuration");
assertTrue(ocd.hasAttributeDefinition("role")
.withStringType()
.check());
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreMonitorServiceTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreMonitorServiceTest.java
index a1c951b45b..e64f406748 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreMonitorServiceTest.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreMonitorServiceTest.java
@@ -33,25 +33,23 @@ public class SegmentNodeStoreMonitorServiceTest {
assertTrue(cd.hasRequireConfigurationPolicy());
assertTrue(cd.hasActivateMethod("activate"));
assertTrue(cd.hasImplementationClass("org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService"));
- assertTrue(cd.hasProperty("commitsTrackerWriterGroups").check());
assertTrue(cd.hasReference("snsStatsMBean")
.withInterface("org.apache.jackrabbit.oak.segment.SegmentNodeStoreStatsMBean")
.withMandatoryUnaryCardinality()
.withStaticPolicy()
- .withBind("bindSnsStatsMBean")
- .withUnbind("unbindSnsStatsMBean")
+ .withField("snsStatsMBean")
.check());
}
@Test
public void testMetatypeInformation() throws Exception {
- MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService.xml"));
+ MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService$Configuration.xml"));
assertTrue(mi.hasDesignate()
.withPid("org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService")
- .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService")
+ .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService$Configuration")
.check());
- ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService");
+ ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService$Configuration");
assertTrue(ocd.hasAttributeDefinition("commitsTrackerWriterGroups")
.withStringType()
.withCardinality("2147483647")
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceTest.java
index 77841c5de6..3a45c47db2 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceTest.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceTest.java
@@ -123,35 +123,32 @@ public class SegmentNodeStoreServiceTest {
.withStaticPolicy()
.withGreedyPolicyOption()
.withTarget("(&(!(split.blobstore=old))(!(split.blobstore=new)))")
- .withBind("bindBlobStore")
- .withUnbind("unbindBlobStore")
+ .withField("blobStore")
.check());
assertTrue(cd.hasReference("segmentStore")
.withInterface("org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence")
.withOptionalUnaryCardinality()
.withStaticPolicy()
.withGreedyPolicyOption()
- .withBind("bindSegmentStore")
- .withUnbind("unbindSegmentStore")
+ .withField("segmentStore")
.check());
assertTrue(cd.hasReference("statisticsProvider")
.withInterface("org.apache.jackrabbit.oak.stats.StatisticsProvider")
.withMandatoryUnaryCardinality()
.withStaticPolicy()
- .withBind("bindStatisticsProvider")
- .withUnbind("unbindStatisticsProvider")
+ .withField("statisticsProvider")
.check());
}
@Test
public void testMetatypeInformation() throws Exception {
- MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.xml"));
+ MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreService$Configuration.xml"));
assertTrue(mi.hasDesignate()
.withPid("org.apache.jackrabbit.oak.segment.SegmentNodeStoreService")
- .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreService")
+ .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreService$Configuration")
.check());
- ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreService");
+ ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreService$Configuration");
assertTrue(ocd.hasAttributeDefinition("repository.home")
.withStringType()
.check());
diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceTest.java
index b466650f14..9e7d929add 100644
--- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceTest.java
+++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceTest.java
@@ -22,6 +22,7 @@ package org.apache.jackrabbit.oak.segment.osgi;
import static org.junit.Assert.assertTrue;
import org.apache.jackrabbit.oak.segment.osgi.MetatypeInformation.ObjectClassDefinition;
+import org.junit.Ignore;
import org.junit.Test;
public class StandbyStoreServiceTest {
@@ -52,8 +53,6 @@ public class StandbyStoreServiceTest {
.withIntegerType()
.withValue("5")
.check());
- assertTrue(cd.hasProperty("primary.allowed-client-ip-ranges")
- .check());
assertTrue(cd.hasProperty("secure")
.withBooleanType()
.withValue("false")
@@ -71,20 +70,19 @@ public class StandbyStoreServiceTest {
.withMandatoryUnaryCardinality()
.withStaticPolicy()
.withGreedyPolicyOption()
- .withBind("bindStoreProvider")
- .withUnbind("unbindStoreProvider")
+ .withField("storeProvider")
.check());
}
@Test
public void testMetatypeInformation() throws Exception {
- MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService.xml"));
+ MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService$Configuration.xml"));
assertTrue(mi.hasDesignate()
.withPid("org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService")
- .withReference("org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService")
+ .withReference("org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService$Configuration")
.check());
- ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService");
+ ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService$Configuration");
assertTrue(ocd.hasAttributeDefinition("org.apache.sling.installer.configuration.persist")
.withBooleanType()
.withDefaultValue("false")
@@ -106,10 +104,6 @@ public class StandbyStoreServiceTest {
.withIntegerType()
.withDefaultValue("5")
.check());
- assertTrue(ocd.hasAttributeDefinition("primary.allowed-client-ip-ranges")
- .withStringType()
- .withCardinality("2147483647")
- .check());
assertTrue(ocd.hasAttributeDefinition("secure")
.withBooleanType()
.withDefaultValue("false")
@@ -122,6 +116,10 @@ public class StandbyStoreServiceTest {
.withBooleanType()
.withDefaultValue("true")
.check());
+ assertTrue(ocd.hasAttributeDefinition("primary.allowed-client-ip-ranges")
+ .withStringType()
+ .withCardinality("2147483647")
+ .check());
}
}