// Copyright 2017 JanusGraph Authors
//
// Licensed 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.janusgraph.graphdb.configuration;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.janusgraph.core.*;
import org.janusgraph.core.attribute.AttributeSerializer;
import org.janusgraph.core.schema.DefaultSchemaMaker;
import org.janusgraph.diskstorage.configuration.Configuration;
import org.janusgraph.diskstorage.StandardIndexProvider;
import org.janusgraph.diskstorage.StandardStoreManager;
import org.janusgraph.diskstorage.keycolumnvalue.ttl.TTLKCVSManager;
import org.janusgraph.graphdb.tinkerpop.JanusGraphDefaultSchemaMaker;
import org.janusgraph.graphdb.tinkerpop.Tp3DefaultSchemaMaker;
import org.janusgraph.graphdb.database.management.ManagementSystem;
import org.janusgraph.graphdb.types.typemaker.DisableDefaultSchemaMaker;
import org.janusgraph.util.stats.NumberUtil;
import org.janusgraph.diskstorage.util.time.*;
import org.janusgraph.diskstorage.configuration.*;
import org.janusgraph.diskstorage.configuration.backend.CommonsConfiguration;
import org.janusgraph.diskstorage.configuration.backend.KCVSConfiguration;
import org.janusgraph.diskstorage.idmanagement.ConflictAvoidanceMode;
import org.janusgraph.diskstorage.idmanagement.ConsistentKeyIDAuthority;
import org.janusgraph.diskstorage.keycolumnvalue.KeyColumnValueStoreManager;
import org.janusgraph.diskstorage.keycolumnvalue.StoreFeatures;
import org.janusgraph.diskstorage.log.kcvs.KCVSLog;
import org.janusgraph.diskstorage.log.kcvs.KCVSLogManager;
import org.janusgraph.graphdb.database.cache.MetricInstrumentedSchemaCache;
import org.janusgraph.graphdb.database.cache.StandardSchemaCache;
import org.janusgraph.graphdb.database.cache.SchemaCache;
import org.janusgraph.graphdb.database.serialize.StandardSerializer;
import org.janusgraph.util.encoding.LongEncoding;
import org.janusgraph.util.system.ConfigurationUtil;
import org.janusgraph.util.system.NetworkUtil;
import org.apache.tinkerpop.gremlin.structure.Graph;
import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import javax.management.MBeanServerFactory;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.configuration.*;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import org.janusgraph.diskstorage.Backend;
import org.janusgraph.graphdb.database.idassigner.VertexIDAssigner;
import org.janusgraph.graphdb.database.serialize.Serializer;
import org.janusgraph.graphdb.transaction.StandardTransactionBuilder;
import org.janusgraph.util.stats.MetricManager;
/**
* Provides functionality to configure a {@link org.janusgraph.core.JanusGraph} INSTANCE.
* <p/>
* <p/>
* A graph database configuration is uniquely associated with a graph database and must not be used for multiple
* databases.
* <p/>
* After a graph database has been initialized with respect to a configuration, some parameters of graph database
* configuration may no longer be modifiable.
*
* @author Matthias Bröcheler (me@matthiasb.com);
*/
public class GraphDatabaseConfiguration {
private static final Logger log =
LoggerFactory.getLogger(GraphDatabaseConfiguration.class);
public static ConfigNamespace ROOT_NS = new ConfigNamespace(null,"root","Root Configuration Namespace for the JanusGraph Graph Database");
// ########## Graph-level Config Options ##########
// ################################################
public static final ConfigNamespace GRAPH_NS = new ConfigNamespace(ROOT_NS,"graph",
"General configuration options");
public static final ConfigOption<Boolean> ALLOW_SETTING_VERTEX_ID = new ConfigOption<Boolean>(GRAPH_NS,"set-vertex-id",
"Whether user provided vertex ids should be enabled and JanusGraph's automatic id allocation be disabled. " +
"Useful when operating JanusGraph in concert with another storage system that assigns long ids but disables some " +
"of JanusGraph's advanced features which can lead to inconsistent data. EXPERT FEATURE - USE WITH GREAT CARE.",
ConfigOption.Type.FIXED, false);
public static final ConfigOption<TimestampProviders> TIMESTAMP_PROVIDER = new ConfigOption<TimestampProviders>(GRAPH_NS, "timestamps",
"The timestamp resolution to use when writing to storage and indices. Sets the time granularity for the " +
"entire graph cluster. To avoid potential inaccuracies, the configured time resolution should match " +
"those of the backend systems. Some JanusGraph storage backends declare a preferred timestamp resolution that " +
"reflects design constraints in the underlying service. When the backend provides " +
"a preferred default, and when this setting is not explicitly declared in the config file, the backend " +
"default is used and the general default associated with this setting is ignored. An explicit " +
"declaration of this setting overrides both the general and backend-specific defaults.",
ConfigOption.Type.FIXED, TimestampProviders.class, TimestampProviders.MICRO);
// public static final ConfigOption<KryoInstanceCacheImpl> KRYO_INSTANCE_CACHE = new ConfigOption<KryoInstanceCacheImpl>(GRAPH_NS, "kryo-instance-cache",
// "Controls how Kryo instances are created and cached. Kryo instances are not " +
// "safe for concurrent access. JanusGraph is responsible guaranteeing that concurrent threads use separate " +
// "Kryo instances. JanusGraph defaults to a Kryo caching approach based on ThreadLocal, as recommended by the " +
// "Kryo documentation (https://github.com/EsotericSoftware/kryo#threading). " +
// "However, these ThreadLocals are not necessarily removed when JanusGraph shuts down. When JanusGraph runs on an " +
// "externally-controlled thread pool that reuses threads indefinitely, such as that provided by Tomcat, " +
// "these unremoved ThreadLocals can potentially cause unintended reference retention for as long as the " +
// "affected threads remain alive. In that type of execution environment, consider setting this to " +
// "CONCURRENT_HASH_MAP. The CHM implementation releases all references when JanusGraph is shutdown, but it " +
// "also subject to some synchronization-related performance overhead that the ThreadLocal-based default " +
// "implementation avoids. Recent versions of Kryo include a class called KryoPool that offers another way " +
// "to solve this problem. However, KryoPool is not supported in older deployments because the version of Kryo " +
// "used by older deployments predates KryoPool's introduction.",
// ConfigOption.Type.MASKABLE, KryoInstanceCacheImpl.class, KryoInstanceCacheImpl.THREAD_LOCAL);
public static final ConfigOption<String> UNIQUE_INSTANCE_ID = new ConfigOption<String>(GRAPH_NS,"unique-instance-id",
"Unique identifier for this JanusGraph instance. This must be unique among all instances " +
"concurrently accessing the same stores or indexes. It's automatically generated by " +
"concatenating the hostname, process id, and a static (process-wide) counter. " +
"Leaving it unset is recommended.",
ConfigOption.Type.LOCAL, String.class);
public static final ConfigOption<Short> UNIQUE_INSTANCE_ID_SUFFIX = new ConfigOption<Short>(GRAPH_NS,"unique-instance-id-suffix",
"When this is set and " + UNIQUE_INSTANCE_ID.getName() + " is not, this JanusGraph " +
"instance's unique identifier is generated by concatenating the hostname to the " +
"provided number. This is a legacy option which is currently only useful if the JVM's " +
"ManagementFactory.getRuntimeMXBean().getName() is not unique between processes.",
ConfigOption.Type.LOCAL, Short.class);
public static final ConfigOption<String> INITIAL_JANUSGRAPH_VERSION = new ConfigOption<String>(GRAPH_NS,"janusgraph-version",
"The version of JanusGraph with which this database was created. Automatically set on first start. Don't manually set this property.",
ConfigOption.Type.FIXED, String.class).hide();
public static final ConfigOption<String> TITAN_COMPATIBLE_VERSIONS = new ConfigOption<String>(GRAPH_NS,"titan-version",
"Titan version for backwards compatibility which this database was created. Automatically set on first start. Don't manually set this property.",
ConfigOption.Type.FIXED, String.class).hide();
public static final ConfigOption<Boolean> ALLOW_STALE_CONFIG = new ConfigOption<Boolean>(GRAPH_NS,"allow-stale-config",
"Whether to allow the local and storage-backend-hosted copies of the configuration to contain conflicting values for " +
"options with any of the following types: " + Joiner.on(", ").join(ConfigOption.getManagedTypes()) + ". " +
"These types are managed globally through the storage backend and cannot be overridden by changing the " +
"local configuration. This type of conflict usually indicates misconfiguration. When this option is true, " +
"JanusGraph will log these option conflicts, but continue normal operation using the storage-backend-hosted value " +
"for each conflicted option. When this option is false, JanusGraph will log these option conflicts, but then it " +
"will throw an exception, refusing to start.",
ConfigOption.Type.MASKABLE, Boolean.class, true);
// ################ INSTANCE REGISTRATION (system) #######################
// ##############################################################
public static final ConfigNamespace REGISTRATION_NS = new ConfigNamespace(ROOT_NS,"system-registration",
"This is used internally to keep track of open instances.",true);
public static final ConfigOption<Instant> REGISTRATION_TIME = new ConfigOption<>(REGISTRATION_NS,"startup-time",
"Timestamp when this instance was started. Automatically set.", ConfigOption.Type.GLOBAL, Instant.class).hide();
// ########## OLAP Style Processing ##########
// ################################################
public static ConfigNamespace JOB_NS = new ConfigNamespace(null,"job","Root Configuration Namespace for JanusGraph OLAP jobs");
public static final ConfigOption<Long> JOB_START_TIME = new ConfigOption<Long>(JOB_NS,"start-time",
"Timestamp (ms since epoch) when the job started. Automatically set.", ConfigOption.Type.LOCAL, Long.class).hide();
public static final ConfigNamespace COMPUTER_NS = new ConfigNamespace(ROOT_NS,"computer",
"GraphComputer related configuration");
public static final ConfigOption<String> COMPUTER_RESULT_MODE = new ConfigOption<String>(COMPUTER_NS,"result-mode",
"How the graph computer should return the computed results. 'persist' for writing them into the graph, " +
"'localtx' for writing them into the local transaction, or 'none' (default)", ConfigOption.Type.MASKABLE, "none");
// ################ Transaction #######################
// ################################################
public static final ConfigNamespace TRANSACTION_NS = new ConfigNamespace(ROOT_NS,"tx",
"Configuration options for transaction handling");
public static final ConfigOption<Boolean> SYSTEM_LOG_TRANSACTIONS = new ConfigOption<Boolean>(TRANSACTION_NS,"log-tx",
"Whether transaction mutations should be logged to JanusGraph's write-ahead transaction log which can be used for recovery of partially failed transactions",
ConfigOption.Type.GLOBAL, false);
public static final ConfigOption<Duration> MAX_COMMIT_TIME = new ConfigOption<>(TRANSACTION_NS,"max-commit-time",
"Maximum time (in ms) that a transaction might take to commit against all backends. This is used by the distributed " +
"write-ahead log processing to determine when a transaction can be considered failed (i.e. after this time has elapsed)." +
"Must be longer than the maximum allowed write time.",
ConfigOption.Type.GLOBAL, Duration.ofSeconds(10));
public static final ConfigNamespace TRANSACTION_RECOVERY_NS = new ConfigNamespace(TRANSACTION_NS,"recovery",
"Configuration options for transaction recovery processes");
public static final ConfigOption<Boolean> VERBOSE_TX_RECOVERY = new ConfigOption<Boolean>(TRANSACTION_RECOVERY_NS,"verbose",
"Whether the transaction recovery system should print recovered transactions and other activity to standard output",
ConfigOption.Type.MASKABLE, false);
// ################ Query Processing #######################
// ################################################
public static final ConfigNamespace QUERY_NS = new ConfigNamespace(ROOT_NS,"query",
"Configuration options for query processing");
public static final ConfigOption<Boolean> IGNORE_UNKNOWN_INDEX_FIELD = new ConfigOption<Boolean>(QUERY_NS, "ignore-unknown-index-key",
"Whether to ignore undefined types encountered in user-provided index queries",
ConfigOption.Type.MASKABLE, false);
public static final String UKNOWN_FIELD_NAME = "unknown_key";
public static final ConfigOption<Boolean> FORCE_INDEX_USAGE = new ConfigOption<Boolean>(QUERY_NS,"force-index",
"Whether JanusGraph should throw an exception if a graph query cannot be answered using an index. Doing so" +
"limits the functionality of JanusGraph's graph queries but ensures that slow graph queries are avoided " +
"on large graphs. Recommended for production use of JanusGraph.",
ConfigOption.Type.MASKABLE, false);
public static final ConfigOption<Boolean> PROPERTY_PREFETCHING = new ConfigOption<Boolean>(QUERY_NS,"fast-property",
"Whether to pre-fetch all properties on first singular vertex property access. This can eliminate backend calls on subsequent" +
"property access for the same vertex at the expense of retrieving all properties at once. This can be " +
"expensive for vertices with many properties",
ConfigOption.Type.MASKABLE, false);
public static final ConfigOption<Boolean> ADJUST_LIMIT = new ConfigOption<Boolean>(QUERY_NS,"smart-limit",
"Whether the query optimizer should try to guess a smart limit for the query to ensure responsiveness in " +
"light of possibly large result sets. Those will be loaded incrementally if this option is enabled.",
ConfigOption.Type.MASKABLE, true);
public static final ConfigOption<Boolean> USE_MULTIQUERY = new ConfigOption<Boolean>(QUERY_NS,"batch",
"Whether traversal queries should be batched when executed against the storage backend. This can lead to significant " +
"performance improvement if there is a non-trivial latency to the backend.",
ConfigOption.Type.MASKABLE, false);
// ################ SCHEMA #######################
// ################################################
public static final ConfigNamespace SCHEMA_NS = new ConfigNamespace(ROOT_NS,"schema",
"Schema related configuration options");
public static final ConfigOption<String> AUTO_TYPE = new ConfigOption<String>(SCHEMA_NS,"default",
"Configures the DefaultSchemaMaker to be used by this graph. If set to 'none', automatic schema creation is disabled. " +
"Defaults to a blueprints compatible schema maker with MULTI edge labels and SINGLE property keys",
ConfigOption.Type.MASKABLE, "default" , new Predicate<String>() {
@Override
public boolean apply(@Nullable String s) {
if (s==null) return false;
if (preregisteredAutoType.containsKey(s)) return true;
try {
Class<?> clazz = ClassUtils.getClass(s);
return DefaultSchemaMaker.class.isAssignableFrom(clazz);
} catch (ClassNotFoundException e) {
return false;
}
}
});
private static final Map<String, DefaultSchemaMaker> preregisteredAutoType =
ImmutableMap.of("none", DisableDefaultSchemaMaker.INSTANCE,
"default", JanusGraphDefaultSchemaMaker.INSTANCE,
"tp3", Tp3DefaultSchemaMaker.INSTANCE);
// ################ CACHE #######################
// ################################################
public static final ConfigNamespace CACHE_NS = new ConfigNamespace(ROOT_NS,"cache","Configuration options that modify JanusGraph's caching behavior");
public static final ConfigOption<Boolean> DB_CACHE = new ConfigOption<Boolean>(CACHE_NS,"db-cache",
"Whether to enable JanusGraph's database-level cache, which is shared across all transactions. " +
"Enabling this option speeds up traversals by holding hot graph elements in memory, " +
"but also increases the likelihood of reading stale data. Disabling it forces each " +
"transaction to independently fetch graph elements from storage before reading/writing them.",
ConfigOption.Type.MASKABLE, false);
/**
* The size of the database level cache.
* If this value is between 0.0 (strictly bigger) and 1.0 (strictly smaller), then it is interpreted as a
* percentage of the total heap space available to the JVM this JanusGraph instance is running in.
* If this value is bigger than 1.0 it is interpreted as an absolute size in bytes.
*/
// public static final String DB_CACHE_SIZE_KEY = "db-cache-size";
// public static final double DB_CACHE_SIZE_DEFAULT = 0.3;
public static final ConfigOption<Double> DB_CACHE_SIZE = new ConfigOption<Double>(CACHE_NS,"db-cache-size",
"Size of JanusGraph's database level cache. Values between 0 and 1 are interpreted as a percentage " +
"of VM heap, while larger values are interpreted as an absolute size in bytes.",
ConfigOption.Type.MASKABLE, 0.3);
/**
* How long the database level cache will keep keys expired while the mutations that triggered the expiration
* are being persisted. This value should be larger than the time it takes for persisted mutations to become visible.
* This setting only ever makes sense for distributed storage backends where writes may be accepted but are not
* immediately readable.
*/
// public static final String DB_CACHE_CLEAN_WAIT_KEY = "db-cache-clean-wait";
// public static final long DB_CACHE_CLEAN_WAIT_DEFAULT = 50;
public static final ConfigOption<Integer> DB_CACHE_CLEAN_WAIT = new ConfigOption<Integer>(CACHE_NS,"db-cache-clean-wait",
"How long, in milliseconds, database-level cache will keep entries after flushing them. " +
"This option is only useful on distributed storage backends that are capable of acknowledging writes " +
"without necessarily making them immediately visible.",
ConfigOption.Type.GLOBAL_OFFLINE, 50);
/**
* The default expiration time for elements held in the database level cache. This is the time period before
* JanusGraph will check against storage backend for a newer query answer.
* Setting this value to 0 will cache elements forever (unless they get evicted due to space constraints). This only
* makes sense when this is the only JanusGraph instance interacting with a storage backend.
*/
// public static final String DB_CACHE_TIME_KEY = "db-cache-time";
// public static final long DB_CACHE_TIME_DEFAULT = 10000;
public static final ConfigOption<Long> DB_CACHE_TIME = new ConfigOption<Long>(CACHE_NS,"db-cache-time",
"Default expiration time, in milliseconds, for entries in the database-level cache. " +
"Entries are evicted when they reach this age even if the cache has room to spare. " +
"Set to 0 to disable expiration (cache entries live forever or until memory pressure " +
"triggers eviction when set to 0).",
ConfigOption.Type.GLOBAL_OFFLINE, 10000l);
/**
* Configures the maximum number of recently-used vertices cached by a transaction. The smaller the cache size, the
* less memory a transaction can consume at maximum. For many concurrent, long running transactions in memory constraint
* environments, reducing the cache size can avoid OutOfMemory and GC limit exceeded exceptions.
* Note, however, that all modifications in a transaction must always be kept in memory and hence this setting does not
* have much impact on write intense transactions. Those must be split into smaller transactions in the case of memory errors.
* <p/>
* The recently-used vertex cache can contain both dirty and clean vertices, that is, both vertices which have been
* created or updated in the current transaction and vertices which have only been read in the current transaction.
*/
// public static final String TX_CACHE_SIZE_KEY = "tx-cache-size";
// public static final int TX_CACHE_SIZE_DEFAULT = 20000;
public static final ConfigOption<Integer> TX_CACHE_SIZE = new ConfigOption<Integer>(CACHE_NS,"tx-cache-size",
"Maximum size of the transaction-level cache of recently-used vertices.",
ConfigOption.Type.MASKABLE, 20000);
/**
* Configures the initial size of the dirty (modified) vertex map used by a transaction. All vertices created or
* updated by a transaction are held in that transaction's dirty vertex map until the transaction commits.
* This option sets the initial size of the dirty map. Unlike {@link #TX_CACHE_SIZE}, this is not a maximum.
* The transaction will transparently allocate more space to store dirty vertices if this initial size hint
* is exceeded. Transactions that know how many vertices they are likely to modify a priori can avoid resize
* costs associated with growing the dirty vertex data structure by setting this option.
*/
public static final ConfigOption<Integer> TX_DIRTY_SIZE = new ConfigOption<Integer>(CACHE_NS, "tx-dirty-size",
"Initial size of the transaction-level cache of uncommitted dirty vertices. " +
"This is a performance hint for write-heavy, performance-sensitive transactional workloads. " +
"If set, it should roughly match the median vertices modified per transaction.",
ConfigOption.Type.MASKABLE, Integer.class);
/**
* The default value of {@link #TX_DIRTY_SIZE} when batch loading is disabled.
* This value is only considered if the user does not specify a value for
* {@code #TX_DIRTY_CACHE_SIZE} explictly in either the graph or transaction config.
*/
private static final int TX_DIRTY_SIZE_DEFAULT_WITHOUT_BATCH = 32;
/**
* The default value of {@link #TX_DIRTY_SIZE} when batch loading is enabled.
* This value is only considered if the user does not specify a value for
* {@code #TX_DIRTY_CACHE_SIZE} explictly in either the graph or transaction config.
*/
private static final int TX_DIRTY_SIZE_DEFAULT_WITH_BATCH = 4096;
// ################ STORAGE #######################
// ################################################
// public static final String STORAGE_NAMESPACE = "storage";
public static final ConfigNamespace STORAGE_NS = new ConfigNamespace(ROOT_NS,"storage","Configuration options for the storage backend. Some options are applicable only for certain backends.");
/**
* Storage directory for those storage backends that require local storage
*/
public static final ConfigOption<String> STORAGE_DIRECTORY = new ConfigOption<String>(STORAGE_NS,"directory",
"Storage directory for those storage backends that require local storage",
ConfigOption.Type.LOCAL, String.class);
// public static final String STORAGE_DIRECTORY_KEY = "directory";
/**
* Path to a configuration file for those storage backends that
* require/support a separate config file
*/
public static final ConfigOption<String> STORAGE_CONF_FILE = new ConfigOption<String>(STORAGE_NS,"conf-file",
"Path to a configuration file for those storage backends which require/support a single separate config file.",
ConfigOption.Type.LOCAL, String.class);
// public static final String STORAGE_CONF_FILE_KEY = "conffile";
/**
* Define the storage backed to use for persistence
*/
public static final ConfigOption<String> STORAGE_BACKEND = new ConfigOption<String>(STORAGE_NS,"backend",
"The primary persistence provider used by JanusGraph. This is required. It should be set one of " +
"JanusGraph's built-in shorthand names for its standard storage backends " +
"(shorthands: " + Joiner.on(", ").join(StandardStoreManager.getAllShorthands()) + ") " +
"or to the full package and classname of a custom/third-party StoreManager implementation.",
ConfigOption.Type.LOCAL, String.class);
// public static final String STORAGE_BACKEND_KEY = "backend";
// public static final String STORAGE_BACKEND_DEFAULT = "local";
/**
* Specifies whether this database is read-only, i.e. write operations are not supported
*/
public static final ConfigOption<Boolean> STORAGE_READONLY = new ConfigOption<Boolean>(STORAGE_NS,"read-only",
"Read-only database",
ConfigOption.Type.LOCAL, false);
// public static final String STORAGE_READONLY_KEY = "read-only";
// public static final boolean STORAGE_READONLY_DEFAULT = false;
/**
* Enables batch loading which improves write performance but assumes that only one thread is interacting with
* the graph
*/
public static final ConfigOption<Boolean> STORAGE_BATCH = new ConfigOption<Boolean>(STORAGE_NS,"batch-loading",
"Whether to enable batch loading into the storage backend",
ConfigOption.Type.LOCAL, false);
// public static final String STORAGE_BATCH_KEY = "batch-loading";
// public static final boolean STORAGE_BATCH_DEFAULT = false;
/**
* Enables transactions on storage backends that support them
*/
public static final ConfigOption<Boolean> STORAGE_TRANSACTIONAL = new ConfigOption<Boolean>(STORAGE_NS,"transactions",
"Enables transactions on storage backends that support them",
ConfigOption.Type.MASKABLE, true);
// public static final String STORAGE_TRANSACTIONAL_KEY = "transactions";
// public static final boolean STORAGE_TRANSACTIONAL_DEFAULT = true;
/**
* Buffers graph mutations locally up to the specified number before persisting them against the storage backend.
* Set to 0 to disable buffering. Buffering is disabled automatically if the storage backend does not support buffered mutations.
*/
public static final ConfigOption<Integer> BUFFER_SIZE = new ConfigOption<Integer>(STORAGE_NS,"buffer-size",
"Size of the batch in which mutations are persisted",
ConfigOption.Type.MASKABLE, 1024, ConfigOption.positiveInt());
// public static final String BUFFER_SIZE_KEY = "buffer-size";
// public static final int BUFFER_SIZE_DEFAULT = 1024;
/**
* Number of times the database attempts to persist the transactional state to the storage layer.
* Persisting the state of a committed transaction might fail for various reasons, some of which are
* temporary such as network failures. For temporary failures, JanusGraph will re-attempt to persist the
* state up to the number of times specified.
*/
// public static final ConfigOption<Integer> WRITE_ATTEMPTS = new ConfigOption<Integer>(STORAGE_NS,"write-attempts",
// "Number of attempts for write operations that might experience temporary failures",
// ConfigOption.Type.MASKABLE, 5, ConfigOption.positiveInt());
// public static final String WRITE_ATTEMPTS_KEY = "write-attempts";
// public static final int WRITE_ATTEMPTS_DEFAULT = 5;
/**
* Number of times the database attempts to execute a read operation against the storage layer in the current transaction.
* A read operation might fail for various reasons, some of which are
* temporary such as network failures. For temporary failures, JanusGraph will re-attempt to read the
* state up to the number of times specified before failing the transaction
*/
// public static final ConfigOption<Integer> READ_ATTEMPTS = new ConfigOption<Integer>(STORAGE_NS,"read-attempts",
// "Number of attempts for read operations that might experience temporary failures",
// ConfigOption.Type.MASKABLE, 3, ConfigOption.positiveInt());
// public static final String READ_ATTEMPTS_KEY = "read-attempts";
// public static final int READ_ATTEMPTS_DEFAULT = 3;
public static final ConfigOption<Duration> STORAGE_WRITE_WAITTIME = new ConfigOption<>(STORAGE_NS,"write-time",
"Maximum time (in ms) to wait for a backend write operation to complete successfully. If a backend write operation" +
"fails temporarily, JanusGraph will backoff exponentially and retry the operation until the wait time has been exhausted. ",
ConfigOption.Type.MASKABLE, Duration.ofSeconds(100L));
public static final ConfigOption<Duration> STORAGE_READ_WAITTIME = new ConfigOption<>(STORAGE_NS,"read-time",
"Maximum time (in ms) to wait for a backend read operation to complete successfully. If a backend read operation" +
"fails temporarily, JanusGraph will backoff exponentially and retry the operation until the wait time has been exhausted. ",
ConfigOption.Type.MASKABLE, Duration.ofSeconds(10L));
/**
* If enabled, JanusGraph attempts to parallelize storage operations against the storage backend using a fixed thread pool shared
* across the entire JanusGraph graph database instance. Parallelization is only applicable to certain storage operations and
* can be beneficial when the operation is I/O bound.
*/
public static final ConfigOption<Boolean> PARALLEL_BACKEND_OPS = new ConfigOption<Boolean>(STORAGE_NS,"parallel-backend-ops",
"Whether JanusGraph should attempt to parallelize storage operations",
ConfigOption.Type.MASKABLE, true);
// public static final String PARALLEL_BACKEND_OPS_KEY = "parallel-backend-ops";
// public static final boolean PARALLEL_BACKEND_OPS_DEFAULT = true;
/**
* A unique identifier for the machine running the JanusGraph instance.
* It must be ensured that no other machine accessing the storage backend can have the same identifier.
*/
// public static final ConfigOption<String> INSTANCE_RID_RAW = new ConfigOption<String>(STORAGE_NS,"machine-id",
// "A unique identifier for the machine running the JanusGraph instance",
// ConfigOption.Type.LOCAL, String.class);
// public static final String INSTANCE_RID_RAW_KEY = "machine-id";
public static final ConfigOption<String[]> STORAGE_HOSTS = new ConfigOption<String[]>(STORAGE_NS,"hostname",
"The hostname or comma-separated list of hostnames of storage backend servers. " +
"This is only applicable to some storage backends, such as cassandra and hbase.",
ConfigOption.Type.LOCAL, new String[]{NetworkUtil.getLoopbackAddress()});
/**
* Configuration key for the port on which to connect to remote storage backend servers.
* <p/>
* Value = {@value}
*/
public static final ConfigOption<Integer> STORAGE_PORT = new ConfigOption<Integer>(STORAGE_NS,"port",
"The port on which to connect to storage backend servers.",
ConfigOption.Type.LOCAL, Integer.class);
/**
* Username and password keys to be used to specify an access credential that may be needed to connect
* with a secured storage backend.
*/
public static final ConfigOption<String> AUTH_USERNAME = new ConfigOption<String>(STORAGE_NS,"username",
"Username to authenticate against backend",
ConfigOption.Type.LOCAL, String.class);
public static final ConfigOption<String> AUTH_PASSWORD = new ConfigOption<String>(STORAGE_NS,"password",
"Password to authenticate against backend",
ConfigOption.Type.LOCAL, String.class);
/**
* Default timeout when connecting to a remote database instance
* <p/>
*/
public static final ConfigOption<Duration> CONNECTION_TIMEOUT = new ConfigOption<>(STORAGE_NS,"connection-timeout",
"Default timeout, in milliseconds, when connecting to a remote database instance",
ConfigOption.Type.MASKABLE, Duration.ofMillis(10000L));
// public static final int CONNECTION_TIMEOUT_DEFAULT = 10000;
// public static final String CONNECTION_TIMEOUT_KEY = "connection-timeout";
/**
* Time in milliseconds for backend manager to wait for the storage backends to
* become available when JanusGraph is run in server mode. Should the backend manager
* experience exceptions when attempting to access the storage backend it will retry
* until this timeout is exceeded.
* <p/>
* A wait time of 0 disables waiting.
* <p/>
*/
public static final ConfigOption<Duration> SETUP_WAITTIME = new ConfigOption<>(STORAGE_NS,"setup-wait",
"Time in milliseconds for backend manager to wait for the storage backends to become available when JanusGraph is run in server mode",
ConfigOption.Type.MASKABLE, Duration.ofMillis(60000L));
// public static final int SETUP_WAITTIME_DEFAULT = 60000;
// public static final String SETUP_WAITTIME_KEY = "setup-wait";
/**
* Default number of results to pull over the wire when iterating over a distributed
* storage backend.
* This is batch size of results to pull when iterating a result set.
*/
public static final ConfigOption<Integer> PAGE_SIZE = new ConfigOption<Integer>(STORAGE_NS,"page-size",
"JanusGraph break requests that may return many results from distributed storage backends " +
"into a series of requests for small chunks/pages of results, where each chunk contains " +
"up to this many elements.",
ConfigOption.Type.MASKABLE, 100);
// public static final int PAGE_SIZE_DEFAULT = 100;
// public static final String PAGE_SIZE_KEY = "page-size";
public static final ConfigNamespace LOCK_NS =
new ConfigNamespace(STORAGE_NS, "lock", "Options for locking on eventually-consistent stores");
/**
* Number of times the system attempts to acquire a lock before giving up and throwing an exception.
*/
public static final ConfigOption<Integer> LOCK_RETRY = new ConfigOption<Integer>(LOCK_NS, "retries",
"Number of times the system attempts to acquire a lock before giving up and throwing an exception",
ConfigOption.Type.MASKABLE, 3);
// public static final String LOCK_RETRY_COUNT = "lock-retries";
// public static final int LOCK_RETRY_COUNT_DEFAULT = 3;
/**
* The number of milliseconds the system waits for a lock application to be acknowledged by the storage backend.
* Also, the time waited at the end of all lock applications before verifying that the applications were successful.
* This value should be a small multiple of the average consistent write time.
*/
public static final ConfigOption<Duration> LOCK_WAIT = new ConfigOption<>(LOCK_NS, "wait-time",
"Number of milliseconds the system waits for a lock application to be acknowledged by the storage backend. " +
"Also, the time waited at the end of all lock applications before verifying that the applications were successful. " +
"This value should be a small multiple of the average consistent write time.",
ConfigOption.Type.GLOBAL_OFFLINE, Duration.ofMillis(100L));
// public static final String LOCK_WAIT_MS = "lock-wait-time";
// public static final long LOCK_WAIT_MS_DEFAULT = 100;
/**
* Number of milliseconds after which a lock is considered to have expired. Lock applications that were not released
* are considered expired after this time and released.
* This value should be larger than the maximum time a transaction can take in order to guarantee that no correctly
* held applications are expired pre-maturely and as small as possible to avoid dead lock.
*/
public static final ConfigOption<Duration> LOCK_EXPIRE = new ConfigOption<>(LOCK_NS, "expiry-time",
"Number of milliseconds after which a lock is considered to have expired. " +
"Lock applications that were not released are considered expired after this time and released. " +
"This value should be larger than the maximum time a transaction can take in order to guarantee " +
"that no correctly held applications are expired pre-maturely and as small as possible to avoid dead lock.",
ConfigOption.Type.GLOBAL_OFFLINE, Duration.ofMillis(300 * 1000L));
// public static final String LOCK_EXPIRE_MS = "lock-expiry-time";
// public static final long LOCK_EXPIRE_MS_DEFAULT = 300 * 1000;
/**
* Whether to attempt to delete expired locks from the storage backend. True
* will attempt to delete expired locks in a background daemon thread. False
* will never attempt to delete expired locks. This option is only
* meaningful for the default lock backend.
*
* @see #LOCK_BACKEND
*/
public static final ConfigOption<Boolean> LOCK_CLEAN_EXPIRED = new ConfigOption<Boolean>(LOCK_NS, "clean-expired",
"Whether to delete expired locks from the storage backend",
ConfigOption.Type.MASKABLE, false);
/**
* Locker type to use. The supported types are in {@link org.janusgraph.diskstorage.Backend}.
*/
public static final ConfigOption<String> LOCK_BACKEND = new ConfigOption<String>(LOCK_NS, "backend",
"Locker type to use",
ConfigOption.Type.GLOBAL_OFFLINE, "consistentkey");
// public static final String LOCK_BACKEND = "lock-backend";
// public static final String LOCK_BACKEND_DEFAULT = "consistentkey";
/**
* Configuration setting key for the local lock mediator prefix
*/
public static final ConfigOption<String> LOCK_LOCAL_MEDIATOR_GROUP =
new ConfigOption<String>(LOCK_NS, "local-mediator-group",
"This option determines the LocalLockMediator instance used for early detection of lock contention " +
"between concurrent JanusGraph graph instances within the same process which are connected to the same " +
"storage backend. JanusGraph instances that have the same value for this variable will attempt to discover " +
"lock contention among themselves in memory before proceeding with the general-case distributed locking " +
"code. JanusGraph generates an appropriate default value for this option at startup. Overridding " +
"the default is generally only useful in testing.", ConfigOption.Type.LOCAL, String.class);
// ################ STORAGE - META #######################
public static final ConfigNamespace STORE_META_NS = new ConfigNamespace(STORAGE_NS,"meta","Meta data to include in storage backend retrievals",true);
public static final ConfigOption<Boolean> STORE_META_TIMESTAMPS = new ConfigOption<Boolean>(STORE_META_NS,"timestamps",
"Whether to include timestamps in retrieved entries for storage backends that automatically annotated entries with timestamps",
ConfigOption.Type.GLOBAL, false);
public static final ConfigOption<Boolean> STORE_META_TTL = new ConfigOption<Boolean>(STORE_META_NS,"ttl",
"Whether to include ttl in retrieved entries for storage backends that automatically annotated entries with timestamps",
ConfigOption.Type.GLOBAL, false);
public static final ConfigOption<Boolean> STORE_META_VISIBILITY = new ConfigOption<Boolean>(STORE_META_NS,"visibility",
"Whether to include visibility in retrieved entries for storage backends that automatically annotated entries with timestamps",
ConfigOption.Type.GLOBAL, true);
// ################ CLUSTERING ###########################
// ################################################
public static final ConfigNamespace CLUSTER_NS = new ConfigNamespace(ROOT_NS,"cluster","Configuration options for multi-machine deployments");
/**
* Whether the id space should be partitioned for equal distribution of keys. If the keyspace is ordered, this needs to be
* enabled to ensure an even distribution of data. If the keyspace is random/hashed, then enabling this only has the benefit
* of de-congesting a single id pool in the database.
*/
// public static final ConfigOption<Boolean> CLUSTER_PARTITION = new ConfigOption<Boolean>(CLUSTER_NS,"partition",
// "Whether the graph's element should be randomly distributed across the cluster " +
// "(true) or explicitly allocated to individual partition blocks based on the configured graph partitioner (false). " +
// "Unless explicitly set, this defaults false for stores that hash keys and defaults true for stores that preserve key order " +
// "(such as HBase and Cassandra with ByteOrderedPartitioner).",
// ConfigOption.Type.FIXED, false);
public static final ConfigOption<Integer> CLUSTER_MAX_PARTITIONS = new ConfigOption<Integer>(CLUSTER_NS,"max-partitions",
"The number of virtual partition blocks created in the partitioned graph. This should be larger than the maximum expected number of nodes" +
"in the JanusGraph graph cluster. Must be bigger than 1 and a power of 2.",
ConfigOption.Type.FIXED, 32, new Predicate<Integer>() {
@Override
public boolean apply(@Nullable Integer integer) {
return integer!=null && integer>1 && NumberUtil.isPowerOf2(integer);
}
});
// ################ IDS ###########################
// ################################################
public static final ConfigNamespace IDS_NS = new ConfigNamespace(ROOT_NS,"ids","General configuration options for graph element IDs");
// public static final String IDS_NAMESPACE = "ids";
/**
* Size of the block to be acquired. Larger block sizes require fewer block applications but also leave a larger
* fraction of the id pool occupied and potentially lost. For write heavy applications, larger block sizes should
* be chosen.
*/
public static final ConfigOption<Integer> IDS_BLOCK_SIZE = new ConfigOption<Integer>(IDS_NS,"block-size",
"Globally reserve graph element IDs in chunks of this size. Setting this too low will make commits " +
"frequently block on slow reservation requests. Setting it too high will result in IDs wasted when a " +
"graph instance shuts down with reserved but mostly-unused blocks.",
ConfigOption.Type.GLOBAL_OFFLINE, 10000);
// public static final String IDS_BLOCK_SIZE_KEY = "block-size";
// public static final int IDS_BLOCK_SIZE_DEFAULT = 10000;
/**
* If flush ids is enabled, vertices and edges are assigned ids immediately upon creation. If not, then ids are only
* assigned when the transaction is committed.
*/
public static final ConfigOption<Boolean> IDS_FLUSH = new ConfigOption<Boolean>(IDS_NS,"flush",
"When true, vertices and edges are assigned IDs immediately upon creation. When false, " +
"IDs are assigned only when the transaction commits. Must be disabled for graph partitioning to work.",
ConfigOption.Type.MASKABLE, true);
// public static final String IDS_FLUSH_KEY = "flush";
// public static final boolean IDS_FLUSH_DEFAULT = true;
/**
* The number of milliseconds that the JanusGraph id pool manager will wait before giving up on allocating a new block
* of ids. Note, that failure to allocate a new id block will cause the entire database to fail, hence this value
* should be set conservatively. Choose a high value if there is a lot of contention around id allocation.
*/
public static final ConfigOption<Duration> IDS_RENEW_TIMEOUT = new ConfigOption<>(IDS_NS,"renew-timeout",
"The number of milliseconds that the JanusGraph id pool manager will wait before giving up on allocating a new block of ids",
ConfigOption.Type.MASKABLE, Duration.ofMillis(120000L));
// public static final String IDS_RENEW_TIMEOUT_KEY = "renew-timeout";
// public static final long IDS_RENEW_TIMEOUT_DEFAULT = 60 * 1000; // 1 minute
/**
* Configures when the id pool manager will attempt to allocate a new id block. When all but the configured percentage
* of the current block is consumed, a new block will be allocated. Larger values should be used if a lot of ids
* are allocated in a short amount of time. Value must be in (0,1].
*/
public static final ConfigOption<Double> IDS_RENEW_BUFFER_PERCENTAGE = new ConfigOption<Double>(IDS_NS,"renew-percentage",
"When the most-recently-reserved ID block has only this percentage of its total IDs remaining " +
"(expressed as a value between 0 and 1), JanusGraph asynchronously begins reserving another block. " +
"This helps avoid transaction commits waiting on ID reservation even if the block size is relatively small.",
ConfigOption.Type.MASKABLE, 0.3);
// public static final String IDS_RENEW_BUFFER_PERCENTAGE_KEY = "renew-percentage";
// public static final double IDS_RENEW_BUFFER_PERCENTAGE_DEFAULT = 0.3; // 30 %
// ################ IDAUTHORITY ###################
// ################################################
// public static final String STORAGE_NAMESPACE = "storage";
public static final ConfigNamespace IDAUTHORITY_NS = new ConfigNamespace(IDS_NS,"authority","Configuration options for graph element ID reservation/allocation");
/**
* The number of milliseconds the system waits for an id block application to be acknowledged by the storage backend.
* Also, the time waited after the application before verifying that the application was successful.
*/
public static final ConfigOption<Duration> IDAUTHORITY_WAIT = new ConfigOption<>(IDAUTHORITY_NS,"wait-time",
"The number of milliseconds the system waits for an ID block reservation to be acknowledged by the storage backend",
ConfigOption.Type.GLOBAL_OFFLINE, Duration.ofMillis(300L));
// public static final String IDAUTHORITY_WAIT_MS_KEY = "idauthority-wait-time";
// public static final long IDAUTHORITY_WAIT_MS_DEFAULT = 300;
/**
* Sets the strategy used by {@link ConsistentKeyIDAuthority} to avoid
* contention in ID block alloction between JanusGraph instances concurrently
* sharing a single distributed storage backend.
*/
// This is set to GLOBAL_OFFLINE as opposed to MASKABLE or GLOBAL to prevent mixing both global-randomized and local-manual modes within the same cluster
public static final ConfigOption<ConflictAvoidanceMode> IDAUTHORITY_CONFLICT_AVOIDANCE = new ConfigOption<ConflictAvoidanceMode>(IDAUTHORITY_NS,"conflict-avoidance-mode",
"This setting helps separate JanusGraph instances sharing a single graph storage backend avoid contention when reserving ID blocks, " +
"increasing overall throughput.",
ConfigOption.Type.GLOBAL_OFFLINE, ConflictAvoidanceMode.class, ConflictAvoidanceMode.NONE);
/**
* When JanusGraph allocates IDs with {@link org.janusgraph.diskstorage.idmanagement.ConflictAvoidanceMode#GLOBAL_AUTO}
* configured, it picks a random unique ID marker and attempts to allocate IDs
* from a partition using the marker. The ID markers function as
* subpartitions with each ID partition. If the attempt fails because that
* partition + uniqueid combination is already completely allocated, then
* JanusGraph will generate a new random unique ID and try again. This controls
* the maximum number of attempts before JanusGraph assumes the entire partition
* is allocated and fails the request. It must be set to at least 1 and
* should generally be set to 3 or more.
* <p/>
* This setting has no effect when {@link #IDAUTHORITY_CONFLICT_AVOIDANCE} is not configured to
* {@link org.janusgraph.diskstorage.idmanagement.ConflictAvoidanceMode#GLOBAL_AUTO}.
*/
public static final ConfigOption<Integer> IDAUTHORITY_CAV_RETRIES = new ConfigOption<Integer>(IDAUTHORITY_NS,"randomized-conflict-avoidance-retries",
"Number of times the system attempts ID block reservations with random conflict avoidance tags before giving up and throwing an exception",
ConfigOption.Type.MASKABLE, 5);
// public static final String IDAUTHORITY_RETRY_COUNT_KEY = "idauthority-retries";
// public static final int IDAUTHORITY_RETRY_COUNT_DEFAULT = 20;
/**
* Configures the number of bits of JanusGraph assigned ids that are reserved for a unique id marker that
* allows the id allocation to be scaled over multiple sub-clusters and to reduce race-conditions
* when a lot of JanusGraph instances attempt to allocate ids at the same time (e.g. during parallel bulk loading)
*
* IMPORTANT: This should never ever, ever be modified from its initial value and ALL JanusGraph instances must use the
* same value. Otherwise, data corruption will occur.
*
* This setting has no effect when {@link #IDAUTHORITY_CONFLICT_AVOIDANCE} is configured to
* {@link org.janusgraph.diskstorage.idmanagement.ConflictAvoidanceMode#NONE}. However, note that while the
* conflict avoidance mode can be changed, this setting cannot ever be changed and must therefore be considered a priori.
*/
public static final ConfigOption<Integer> IDAUTHORITY_CAV_BITS = new ConfigOption<Integer>(IDAUTHORITY_NS,"conflict-avoidance-tag-bits",
"Configures the number of bits of JanusGraph-assigned element IDs that are reserved for the conflict avoidance tag",
ConfigOption.Type.FIXED, 4 , new Predicate<Integer>() {
@Override
public boolean apply(@Nullable Integer uniqueIdBitWidth) {
return uniqueIdBitWidth>=0 && uniqueIdBitWidth<=16;
}
});
/**
* Unique id marker to be used by this JanusGraph instance when allocating ids. The unique id marker
* must be non-negative and fit within the number of unique id bits configured.
* By assigning different unique id markers to individual JanusGraph instances it can be assured
* that those instances don't conflict with one another when attempting to allocate new id blocks.
*
* IMPORTANT: The configured unique id marker must fit within the configured unique id bit width.
*
* This setting has no effect when {@link #IDAUTHORITY_CONFLICT_AVOIDANCE} is configured to
* {@link org.janusgraph.diskstorage.idmanagement.ConflictAvoidanceMode#NONE}.
*/
public static final ConfigOption<Integer> IDAUTHORITY_CAV_TAG = new ConfigOption<Integer>(IDAUTHORITY_NS,"conflict-avoidance-tag",
"Conflict avoidance tag to be used by this JanusGraph instance when allocating IDs",
ConfigOption.Type.LOCAL, 0);
// ############## External Index ######################
// ################################################
public static final String INDEX_NAMESPACE = "index";
public static final ConfigNamespace INDEX_NS = new ConfigNamespace(ROOT_NS,"index","Configuration options for the individual indexing backends",true);
/**
* Define the indexing backed to use for index support
*/
public static final ConfigOption<String> INDEX_BACKEND = new ConfigOption<String>(INDEX_NS,"backend",
"The indexing backend used to extend and optimize JanusGraph's query functionality. " +
"This setting is optional. JanusGraph can use multiple heterogeneous index backends. " +
"Hence, this option can appear more than once, so long as the user-defined name between " +
"\"" + INDEX_NS.getName() + "\" and \"backend\" is unique among appearances." +
"Similar to the storage backend, this should be set to one of " +
"JanusGraph's built-in shorthand names for its standard index backends " +
"(shorthands: " + Joiner.on(", ").join(StandardIndexProvider.getAllShorthands()) + ") " +
"or to the full package and classname of a custom/third-party IndexProvider implementation.",
ConfigOption.Type.GLOBAL_OFFLINE, "elasticsearch");
// public static final String INDEX_BACKEND_KEY = "backend";
// public static final String INDEX_BACKEND_DEFAULT = "lucene";
public static final ConfigOption<String> INDEX_DIRECTORY = new ConfigOption<String>(INDEX_NS,"directory",
"Directory to store index data locally",
ConfigOption.Type.MASKABLE, String.class);
public static final ConfigOption<String> INDEX_NAME = new ConfigOption<String>(INDEX_NS,"index-name",
"Name of the index if required by the indexing backend",
ConfigOption.Type.GLOBAL_OFFLINE, "janusgraph");
public static final ConfigOption<String[]> INDEX_HOSTS = new ConfigOption<String[]>(INDEX_NS,"hostname",
"The hostname or comma-separated list of hostnames of index backend servers. " +
"This is only applicable to some index backends, such as elasticsearch and solr.",
ConfigOption.Type.MASKABLE, new String[]{NetworkUtil.getLoopbackAddress()});
public static final ConfigOption<Integer> INDEX_PORT = new ConfigOption<Integer>(INDEX_NS,"port",
"The port on which to connect to index backend servers",
ConfigOption.Type.MASKABLE, Integer.class);
public static final ConfigOption<String> INDEX_CONF_FILE = new ConfigOption<String>(INDEX_NS,"conf-file",
"Path to a configuration file for those indexing backends that require/support a separate config file",
ConfigOption.Type.MASKABLE, String.class);
public static final ConfigOption<Integer> INDEX_MAX_RESULT_SET_SIZE = new ConfigOption<Integer>(INDEX_NS, "max-result-set-size",
"Maxium number of results to return if no limit is specified",
ConfigOption.Type.MASKABLE, 100000);
public static final ConfigOption<Boolean> INDEX_NAME_MAPPING = new ConfigOption<Boolean>(INDEX_NS,"map-name",
"Whether to use the name of the property key as the field name in the index. It must be ensured, that the" +
"indexed property key names are valid field names. Renaming the property key will NOT rename the field " +
"and its the developers responsibility to avoid field collisions.",
ConfigOption.Type.GLOBAL, true);
// ############## Logging System ######################
// ################################################
public static final ConfigNamespace LOG_NS = new ConfigNamespace(GraphDatabaseConfiguration.ROOT_NS,"log","Configuration options for JanusGraph's logging system",true);
public static final String MANAGEMENT_LOG = "janusgraph";
public static final String TRANSACTION_LOG = "tx";
public static final String USER_LOG = "user";
public static final String USER_LOG_PREFIX = "ulog_";
public static final Duration TRANSACTION_LOG_DEFAULT_TTL = Duration.ofDays(7);
public static final ConfigOption<String> LOG_BACKEND = new ConfigOption<String>(LOG_NS,"backend",
"Define the log backed to use",
ConfigOption.Type.GLOBAL_OFFLINE, "default");
public static final ConfigOption<Integer> LOG_NUM_BUCKETS = new ConfigOption<Integer>(LOG_NS,"num-buckets",
"The number of buckets to split log entries into for load balancing",
ConfigOption.Type.GLOBAL_OFFLINE, 1, ConfigOption.positiveInt());
public static final ConfigOption<Integer> LOG_SEND_BATCH_SIZE = new ConfigOption<Integer>(LOG_NS,"send-batch-size",
"Maximum number of log messages to batch up for sending for logging implementations that support batch sending",
ConfigOption.Type.MASKABLE, 256, ConfigOption.positiveInt());
public static final ConfigOption<Integer> LOG_READ_BATCH_SIZE = new ConfigOption<Integer>(LOG_NS,"read-batch-size",
"Maximum number of log messages to read at a time for logging implementations that read messages in batches",
ConfigOption.Type.MASKABLE, 1024, ConfigOption.positiveInt());
public static final ConfigOption<Duration> LOG_SEND_DELAY = new ConfigOption<>(LOG_NS,"send-delay",
"Maximum time in ms that messages can be buffered locally before sending in batch",
ConfigOption.Type.MASKABLE, Duration.ofMillis(1000L));
public static final ConfigOption<Duration> LOG_READ_INTERVAL = new ConfigOption<>(LOG_NS,"read-interval",
"Time in ms between message readings from the backend for this logging implementations that read message in batch",
ConfigOption.Type.MASKABLE, Duration.ofMillis(5000L));
public static final ConfigOption<Integer> LOG_READ_THREADS = new ConfigOption<Integer>(LOG_NS,"read-threads",
"Number of threads to be used in reading and processing log messages",
ConfigOption.Type.MASKABLE, 1, ConfigOption.positiveInt());
public static final ConfigOption<Duration> LOG_STORE_TTL = new ConfigOption<Duration>(LOG_NS,"ttl",
"Sets a TTL on all log entries, meaning" +
"that all entries added to this log expire after the configured amount of time. Requires" +
"that the log implementation supports TTL.",
ConfigOption.Type.GLOBAL, Duration.class, new Predicate<Duration>() {
@Override
public boolean apply(@Nullable Duration sd) {
return null != sd && !sd.isZero();
}
});
// ############## Attributes ######################
// ################################################
public static final ConfigNamespace ATTRIBUTE_NS = new ConfigNamespace(ROOT_NS,"attributes","Configuration options for attribute handling");
public static final ConfigNamespace CUSTOM_ATTRIBUTE_NS = new ConfigNamespace(ATTRIBUTE_NS,"custom","Custom attribute serialization and handling",true);
public static final String ATTRIBUTE_PREFIX = "attribute";
public static final ConfigOption<String> CUSTOM_ATTRIBUTE_CLASS = new ConfigOption<String>(CUSTOM_ATTRIBUTE_NS,"attribute-class",
"Class of the custom attribute to be registered",
ConfigOption.Type.GLOBAL_OFFLINE, String.class);
public static final ConfigOption<String> CUSTOM_SERIALIZER_CLASS = new ConfigOption<String>(CUSTOM_ATTRIBUTE_NS,"serializer-class",
"Class of the custom attribute serializer to be registered",
ConfigOption.Type.GLOBAL_OFFLINE, String.class);
// private static final String ATTRIBUTE_PREFIX = "attribute";
// private static final String SERIALIZER_PREFIX = "serializer";
// ################ Metrics #######################
// ################################################
/**
* Configuration key prefix for Metrics.
*/
// public static final String METRICS_NAMESPACE = "metrics";
public static final ConfigNamespace METRICS_NS = new ConfigNamespace(ROOT_NS,"metrics","Configuration options for metrics reporting");
/**
* Whether to enable basic timing and operation count monitoring on backend
* methods using the {@code com.codahale.metrics} package.
*/
public static final ConfigOption<Boolean> BASIC_METRICS = new ConfigOption<Boolean>(METRICS_NS,"enabled",
"Whether to enable basic timing and operation count monitoring on backend",
ConfigOption.Type.MASKABLE, false);
// public static final String BASIC_METRICS = "enable-basic-metrics";
// public static final boolean BASIC_METRICS_DEFAULT = false;
/**
* This is the prefix used outside of a graph database configuration, or for
* operations where a system-internal transaction is necessary as an
* implementation detail. It currently can't be modified, though there is no
* substantial technical obstacle preventing it from being configured --
* some kind of configuration object is in scope everywhere it is used, and
* it could theoretically be stored in and read from that object.
*/
public static final String METRICS_PREFIX_DEFAULT = "org.janusgraph";
public static final String METRICS_SYSTEM_PREFIX_DEFAULT = METRICS_PREFIX_DEFAULT + "." + "sys";
public static final String METRICS_SCHEMA_PREFIX_DEFAULT = METRICS_SYSTEM_PREFIX_DEFAULT + "." + "schema";
/**
* The default name prefix for Metrics reported by JanusGraph. All metric names
* will begin with this string and a period. This value can be overridden on
* a transaction-specific basis through
* {@link StandardTransactionBuilder#groupName(String)}.
* <p/>
* Default = {@literal #METRICS_PREFIX_DEFAULT}
*/
public static final ConfigOption<String> METRICS_PREFIX = new ConfigOption<String>(METRICS_NS,"prefix",
"The default name prefix for Metrics reported by JanusGraph.",
ConfigOption.Type.MASKABLE, METRICS_PREFIX_DEFAULT);
// public static final String METRICS_PREFIX_KEY = "prefix";
/**
* Whether to aggregate measurements for the edge store, vertex index, edge
* index, and ID store.
* <p/>
* If true, then metrics for each of these backends will use the same metric
* name ("stores"). All of their measurements will be combined. This setting
* measures the sum of JanusGraph's backend activity without distinguishing
* between contributions of its various internal stores.
* <p/>
* If false, then metrics for each of these backends will use a unique
* metric name ("idStore", "edgeStore", "vertexIndex", and "edgeIndex").
* This setting exposes the activity associated with each backend component,
* but it also multiplies the number of measurements involved by four.
* <p/>
* This option has no effect when {@link #BASIC_METRICS} is false.
*/
public static final ConfigOption<Boolean> METRICS_MERGE_STORES = new ConfigOption<Boolean>(METRICS_NS,"merge-stores",
"Whether to aggregate measurements for the edge store, vertex index, edge index, and ID store",
ConfigOption.Type.MASKABLE, true);
// public static final String MERGE_BASIC_METRICS_KEY = "merge-basic-metrics";
// public static final boolean MERGE_BASIC_METRICS_DEFAULT = true;
public static final ConfigNamespace METRICS_CONSOLE_NS = new ConfigNamespace(METRICS_NS,"console","Configuration options for metrics reporting to console");
/**
* Metrics console reporter interval in milliseconds. Leaving this
* configuration key absent or null disables the console reporter.
*/
public static final ConfigOption<Duration> METRICS_CONSOLE_INTERVAL = new ConfigOption<>(METRICS_CONSOLE_NS,"interval",
"Time between Metrics reports printing to the console, in milliseconds",
ConfigOption.Type.MASKABLE, Duration.class);
// public static final String METRICS_CONSOLE_INTERVAL_KEY = "console.interval";
// public static final Long METRICS_CONSOLE_INTERVAL_DEFAULT = null;
public static final ConfigNamespace METRICS_CSV_NS = new ConfigNamespace(METRICS_NS,"csv","Configuration options for metrics reporting to CSV file");
/**
* Metrics CSV reporter interval in milliseconds. Leaving this configuration
* key absent or null disables the CSV reporter.
*/
public static final ConfigOption<Duration> METRICS_CSV_INTERVAL = new ConfigOption<>(METRICS_CSV_NS,"interval",
"Time between dumps of CSV files containing Metrics data, in milliseconds",
ConfigOption.Type.MASKABLE, Duration.class);
// public static final String METRICS_CSV_INTERVAL_KEY = "csv.interval";
// public static final Long METRICS_CSV_INTERVAL_DEFAULT = null;
/**
* Metrics CSV output directory. It will be created if it doesn't already
* exist. This option must be non-null if {@link #METRICS_CSV_INTERVAL} is
* non-null. This option has no effect if {@code #METRICS_CSV_INTERVAL} is
* null.
*/
public static final ConfigOption<String> METRICS_CSV_DIR = new ConfigOption<String>(METRICS_CSV_NS,"directory",
"Metrics CSV output directory",
ConfigOption.Type.MASKABLE, String.class);
// public static final String METRICS_CSV_DIR_KEY = "csv.dir";
// public static final String METRICS_CSV_DIR_DEFAULT = null;
public static final ConfigNamespace METRICS_JMX_NS = new ConfigNamespace(METRICS_NS,"jmx","Configuration options for metrics reporting through JMX");
/**
* Whether to report Metrics through a JMX MBean.
*/
public static final ConfigOption<Boolean> METRICS_JMX_ENABLED = new ConfigOption<Boolean>(METRICS_JMX_NS,"enabled",
"Whether to report Metrics through a JMX MBean",
ConfigOption.Type.MASKABLE, false);
// public static final String METRICS_JMX_ENABLED_KEY = "jmx.enabled";
// public static final boolean METRICS_JMX_ENABLED_DEFAULT = false;
/**
* The JMX domain in which to report Metrics. If null, then Metrics applies
* its default value.
*/
public static final ConfigOption<String> METRICS_JMX_DOMAIN = new ConfigOption<String>(METRICS_JMX_NS,"domain",
"The JMX domain in which to report Metrics",
ConfigOption.Type.MASKABLE, String.class);
// public static final String METRICS_JMX_DOMAIN_KEY = "jmx.domain";
// public static final String METRICS_JMX_DOMAIN_DEFAULT = null;
/**
* The JMX agentId through which to report Metrics. Calling
* {@link MBeanServerFactory#findMBeanServer(String)} on this value must
* return exactly one {@code MBeanServer} at runtime. If null, then Metrics
* applies its default value.
*/
public static final ConfigOption<String> METRICS_JMX_AGENTID = new ConfigOption<String>(METRICS_JMX_NS,"agentid",
"The JMX agentId used by Metrics",
ConfigOption.Type.MASKABLE, String.class);
// public static final String METRICS_JMX_AGENTID_KEY = "jmx.agentid";
// public static final String METRICS_JMX_AGENTID_DEFAULT = null;
public static final ConfigNamespace METRICS_SLF4J_NS = new ConfigNamespace(METRICS_NS,"slf4j","Configuration options for metrics reporting through slf4j");
/**
* Metrics Slf4j reporter interval in milliseconds. Leaving this
* configuration key absent or null disables the Slf4j reporter.
*/
public static final ConfigOption<Duration> METRICS_SLF4J_INTERVAL = new ConfigOption<>(METRICS_SLF4J_NS,"interval",
"Time between slf4j logging reports of Metrics data, in milliseconds",
ConfigOption.Type.MASKABLE, Duration.class);
// public static final String METRICS_SLF4J_INTERVAL_KEY = "slf4j.interval";
// public static final Long METRICS_SLF4J_INTERVAL_DEFAULT = null;
/**
* The complete name of the Logger through which Metrics will report via
* Slf4j. If non-null, then Metrics will be dumped on
* {@link LoggerFactory#getLogger(String)} with the configured value as the
* argument. If null, then Metrics will use its default Slf4j logger.
*/
public static final ConfigOption<String> METRICS_SLF4J_LOGGER = new ConfigOption<String>(METRICS_SLF4J_NS,"logger",
"The complete name of the Logger through which Metrics will report via Slf4j",
ConfigOption.Type.MASKABLE, String.class);
// public static final String METRICS_SLF4J_LOGGER_KEY = "slf4j.logger";
// public static final String METRICS_SLF4J_LOGGER_DEFAULT = null;
/**
* The configuration namespace within {@link #METRICS_NS} for Ganglia.
*/
public static final ConfigNamespace METRICS_GANGLIA_NS = new ConfigNamespace(METRICS_NS,"ganglia","Configuration options for metrics reporting through Ganglia");
/**
* The unicast host or multicast group name to which Metrics will send
* Ganglia data. Setting this config key has no effect unless
* {@link #GANGLIA_INTERVAL} is also set.
*/
public static final ConfigOption<String> GANGLIA_HOST_OR_GROUP = new ConfigOption<String>(METRICS_GANGLIA_NS,"hostname",
"The unicast host or multicast group name to which Metrics will send Ganglia data",
ConfigOption.Type.MASKABLE, String.class);
//
// public static final String GANGLIA_HOST_OR_GROUP_KEY = "hostname";
/**
* The number of milliseconds to wait between sending Metrics data to the
* host or group specified by {@link #GANGLIA_HOST_OR_GROUP}. This has no
* effect unless {@link #GANGLIA_HOST_OR_GROUP} is also set.
*/
public static final ConfigOption<Duration> GANGLIA_INTERVAL = new ConfigOption<>(METRICS_GANGLIA_NS,"interval",
"The number of milliseconds to wait between sending Metrics data to Ganglia",
ConfigOption.Type.MASKABLE, Duration.class);
// public static final String GANGLIA_INTERVAL_KEY = "interval";
/**
* The port to which Ganglia data are sent.
* <p/>
*/
public static final ConfigOption<Integer> GANGLIA_PORT = new ConfigOption<Integer>(METRICS_GANGLIA_NS,"port",
"The port to which Ganglia data are sent",
ConfigOption.Type.MASKABLE, 8649);
// public static final String GANGLIA_PORT = "port";
// public static final int GANGLIA_PORT_DEFAULT = 8649;
/**
* Whether to interpret {@link #GANGLIA_HOST_OR_GROUP} as a unicast or
* multicast address. If present, it must be either the string "multicast"
* or the string "unicast".
* <p/>
*/
public static final ConfigOption<String> GANGLIA_ADDRESSING_MODE = new ConfigOption<String>(METRICS_GANGLIA_NS,"addressing-mode",
"Whether to communicate to Ganglia via uni- or multicast",
ConfigOption.Type.MASKABLE, "unicast", new Predicate<String>() {
@Override
public boolean apply(@Nullable String s) {
return s!=null && s.equalsIgnoreCase("unicast") || s.equalsIgnoreCase("multicast");
}
});
// public static final String GANGLIA_ADDRESSING_MODE_KEY = "addressing-mode";
// public static final String GANGLIA_ADDRESSING_MODE_DEFAULT = "unicast";
/**
* The multicast TTL to set on outgoing Ganglia datagrams. This has no
* effect when {@link #GANGLIA_ADDRESSING_MODE} is set to "multicast".
* <p/>
* This is a TTL in the multicast protocol sense (number of routed hops),
* not a timestamp sense.
*/
public static final ConfigOption<Integer> GANGLIA_TTL = new ConfigOption<Integer>(METRICS_GANGLIA_NS,"ttl",
"The multicast TTL to set on outgoing Ganglia datagrams",
ConfigOption.Type.MASKABLE, 1);
// public static final String GANGLIA_TTL_KEY = "ttl";
// public static final int GANGLIA_TTL_DEFAULT = 1;
/**
* Whether to send data to Ganglia in the 3.1 protocol format (true) or the
* 3.0 protocol format (false).
* <p/>
*/
public static final ConfigOption<Boolean> GANGLIA_USE_PROTOCOL_31 = new ConfigOption<Boolean>(METRICS_GANGLIA_NS,"protocol-31",
"Whether to send data to Ganglia in the 3.1 protocol format",
ConfigOption.Type.MASKABLE, true);
// public static final String GANGLIA_USE_PROTOCOL_31_KEY = "protocol-31";
// public static final boolean GANGLIA_USE_PROTOCOL_31_DEFAULT = true;
/**
* The host UUID to set on outgoing Ganglia datagrams. If null, no UUID is
* set on outgoing data.
* <p/>
* See https://github.com/ganglia/monitor-core/wiki/UUIDSources
* <p/>
*/
public static final ConfigOption<String> GANGLIA_UUID = new ConfigOption<String>(METRICS_GANGLIA_NS,"uuid",
"The host UUID to set on outgoing Ganglia datagrams. " +
"See https://github.com/ganglia/monitor-core/wiki/UUIDSources for information about this setting.",
ConfigOption.Type.LOCAL, String.class);
// public static final String GANGLIA_UUID_KEY = "uuid";
// public static final UUID GANGLIA_UUID_DEFAULT = null;
/**
* If non-null, it must be a valid Gmetric spoof string formatted as an
* IP:hostname pair. If null, Ganglia will automatically determine the IP
* and hostname to set on outgoing datagrams.
* <p/>
* See http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
* <p/>
*/
public static final ConfigOption<String> GANGLIA_SPOOF = new ConfigOption<String>(METRICS_GANGLIA_NS,"spoof",
"If non-null, it must be a valid Gmetric spoof string formatted as an IP:hostname pair. " +
"See http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing for information about this setting.",
ConfigOption.Type.MASKABLE, String.class, new Predicate<String>() {
@Override
public boolean apply(@Nullable String s) {
return s!=null && 0 < s.indexOf(':');
}
});
// public static final String GANGLIA_SPOOF_KEY = "spoof";
// public static final String GANGLIA_SPOOF_DEFAULT = null;
/**
* The configuration namespace within {@link #METRICS_NS} for
* Graphite.
*/
public static final ConfigNamespace METRICS_GRAPHITE_NS = new ConfigNamespace(METRICS_NS,"graphite","Configuration options for metrics reporting through Graphite");
// public static final String GRAPHITE_NAMESPACE = "graphite";
/**
* The hostname to receive Graphite plaintext protocol metric data. Setting
* this config key has no effect unless {@link #GRAPHITE_INTERVAL} is also
* set.
*/
public static final ConfigOption<String> GRAPHITE_HOST = new ConfigOption<String>(METRICS_GRAPHITE_NS,"hostname",
"The hostname to receive Graphite plaintext protocol metric data",
ConfigOption.Type.MASKABLE, String.class);
// public static final String GRAPHITE_HOST_KEY = "hostname";
/**
* The number of milliseconds to wait between sending Metrics data to the
* host specified {@link #GRAPHITE_HOST}. This has no effect unless
* {@link #GRAPHITE_HOST} is also set.
*/
public static final ConfigOption<Duration> GRAPHITE_INTERVAL = new ConfigOption<>(METRICS_GRAPHITE_NS,"interval",
"The number of milliseconds to wait between sending Metrics data",
ConfigOption.Type.MASKABLE, Duration.class);
// public static final String GRAPHITE_INTERVAL_KEY = "interval";
/**
* The port to which Graphite data are sent.
* <p/>
*/
public static final ConfigOption<Integer> GRAPHITE_PORT = new ConfigOption<Integer>(METRICS_GRAPHITE_NS,"port",
"The port to which Graphite data are sent",
ConfigOption.Type.MASKABLE, 2003);
// public static final String GRAPHITE_PORT_KEY = "port";
// public static final int GRAPHITE_PORT_DEFAULT = 2003;
/**
* A Graphite-specific prefix for reported metrics. If non-null, Metrics
* prepends this and a "." to all metric names before reporting them to
* Graphite.
* <p/>
*/
public static final ConfigOption<String> GRAPHITE_PREFIX = new ConfigOption<String>(METRICS_GRAPHITE_NS,"prefix",
"A Graphite-specific prefix for reported metrics",
ConfigOption.Type.MASKABLE, String.class);
// public static final String GRAPHITE_PREFIX_KEY = "prefix";
// public static final String GRAPHITE_PREFIX_DEFAULT = null;
public static final ConfigNamespace GREMLIN_NS = new ConfigNamespace(ROOT_NS,"gremlin",
"Gremlin configuration options");
public static final ConfigOption<String> GREMLIN_GRAPH = new ConfigOption<String>(GREMLIN_NS, "graph",
"The implementation of graph factory that will be used by gremlin server", ConfigOption.Type.LOCAL, "org.janusgraph.core.JanusGraphFactory");
// ################ Begin Class Definition #######################
// ###############################################################
public static final String SYSTEM_PROPERTIES_STORE_NAME = "system_properties";
public static final String SYSTEM_CONFIGURATION_IDENTIFIER = "configuration";
public static final String USER_CONFIGURATION_IDENTIFIER = "userconfig";
private static final String INCOMPATIBLE_VERSION_EXCEPTION = "StorageBackend version is incompatible with current JanusGraph version: storage [%1s] vs. runtime [%2s]";
private final Configuration configuration;
private final ReadConfiguration configurationAtOpen;
private String uniqueGraphId;
private ModifiableConfiguration localConfiguration;
private boolean readOnly;
private boolean flushIDs;
private boolean forceIndexUsage;
private boolean batchLoading;
private int txVertexCacheSize;
private int txDirtyVertexSize;
private DefaultSchemaMaker defaultSchemaMaker;
private Boolean propertyPrefetching;
private boolean adjustQueryLimit;
private Boolean useMultiQuery;
private boolean allowVertexIdSetting;
private boolean logTransactions;
private String metricsPrefix;
private String unknownIndexKeyName;
private StoreFeatures storeFeatures = null;
public GraphDatabaseConfiguration(ReadConfiguration localConfig) {
Preconditions.checkNotNull(localConfig);
configurationAtOpen = localConfig;
BasicConfiguration localbc = new BasicConfiguration(ROOT_NS,localConfig, BasicConfiguration.Restriction.NONE);
ModifiableConfiguration overwrite = new ModifiableConfiguration(ROOT_NS,new CommonsConfiguration(), BasicConfiguration.Restriction.NONE);
// KeyColumnValueStoreManager storeManager=null;
final KeyColumnValueStoreManager storeManager = Backend.getStorageManager(localbc);
final StoreFeatures storeFeatures = storeManager.getFeatures();
KCVSConfiguration kcvsConfig=Backend.getStandaloneGlobalConfiguration(storeManager,localbc);
ReadConfiguration globalConfig=null;
//Copy over local config options
localConfiguration = new ModifiableConfiguration(ROOT_NS, new CommonsConfiguration(), BasicConfiguration.Restriction.LOCAL);
localConfiguration.setAll(getLocalSubset(localbc.getAll()));
//Read out global configuration
try {
// If lock prefix is unspecified, specify it now
if (!localbc.has(LOCK_LOCAL_MEDIATOR_GROUP)) {
overwrite.set(LOCK_LOCAL_MEDIATOR_GROUP, storeManager.getName());
}
//Freeze global configuration if not already frozen!
ModifiableConfiguration globalWrite = new ModifiableConfiguration(ROOT_NS,kcvsConfig, BasicConfiguration.Restriction.GLOBAL);
if (!globalWrite.isFrozen()) {
//Copy over global configurations
globalWrite.setAll(getGlobalSubset(localbc.getAll()));
//Write JanusGraph version
Preconditions.checkArgument(!globalWrite.has(INITIAL_JANUSGRAPH_VERSION),"Database has already been initialized but not frozen");
globalWrite.set(INITIAL_JANUSGRAPH_VERSION,JanusGraphConstants.VERSION);
/* If the configuration does not explicitly set a timestamp provider and
* the storage backend both supports timestamps and has a preference for
* a specific timestamp provider, then apply the backend's preference.
*/
if (!localbc.has(TIMESTAMP_PROVIDER)) {
StoreFeatures f = storeManager.getFeatures();
TimestampProviders backendPreference = null;
if (f.hasTimestamps() && null != (backendPreference = f.getPreferredTimestamps())) {
globalWrite.set(TIMESTAMP_PROVIDER, backendPreference);
log.info("Set timestamps to {} according to storage backend preference", globalWrite.get(TIMESTAMP_PROVIDER));
}
globalWrite.set(TIMESTAMP_PROVIDER, TIMESTAMP_PROVIDER.getDefaultValue());
log.info("Set default timestamp provider {}", globalWrite.get(TIMESTAMP_PROVIDER));
} else {
log.info("Using configured timestamp provider {}", localbc.get(TIMESTAMP_PROVIDER));
}
globalWrite.freezeConfiguration();
} else {
try {
String version = globalWrite.get(INITIAL_JANUSGRAPH_VERSION);
Preconditions.checkArgument(version!=null,"JanusGraph version has not been initialized");
if (!JanusGraphConstants.VERSION.equals(version) && !JanusGraphConstants.COMPATIBLE_VERSIONS.contains(version)) {
throw new JanusGraphException(String.format(INCOMPATIBLE_VERSION_EXCEPTION, version, JanusGraphConstants.VERSION));
}
} catch (IllegalStateException ise) {
checkBackwardCompatibilityWithTitan(globalWrite);
}
final boolean managedOverridesAllowed;
if (localbc.has(ALLOW_STALE_CONFIG))
managedOverridesAllowed = localbc.get(ALLOW_STALE_CONFIG);
else if (globalWrite.has(ALLOW_STALE_CONFIG))
managedOverridesAllowed = globalWrite.get(ALLOW_STALE_CONFIG);
else
managedOverridesAllowed = ALLOW_STALE_CONFIG.getDefaultValue();
// Check for disagreement between local and backend values for GLOBAL(_OFFLINE) and FIXED options
// The point of this check is to find edits to the local config which have no effect (and therefore likely indicate misconfiguration)
Set<String> optionsWithDiscrepancies = Sets.newHashSet();
for (Map.Entry<ConfigElement.PathIdentifier, Object> ent : getManagedSubset(localbc.getAll()).entrySet()) {
ConfigElement.PathIdentifier pid = ent.getKey();
assert pid.element.isOption();
ConfigOption<?> opt = (ConfigOption<?>)pid.element;
Object localValue = ent.getValue();
// Get the storage backend's setting and compare with localValue
Object storeValue = globalWrite.get(opt, pid.umbrellaElements);
// Most validation predicate impls disallow null, but we can't assume that here
final boolean match;
if (null != localValue && null != storeValue) {
match = localValue.equals(storeValue);
} else if (null == localValue && null == storeValue) {
match = true;
} else {
match = false;
}
// Log each option with value disagreement between local and backend configs
if (!match) {
String fullOptionName = ConfigElement.getPath(pid.element, pid.umbrellaElements);
String template = "Local setting {}={} (Type: {}) is overridden by globally managed value ({}). Use the {} interface instead of the local configuration to control this setting.";
Object replacements[] = new Object[] { fullOptionName, localValue, opt.getType(), storeValue, ManagementSystem.class.getSimpleName() };
if (managedOverridesAllowed) { // Lower log severity when this is enabled
log.warn(template, replacements);
} else {
log.error(template, replacements);
}
optionsWithDiscrepancies.add(fullOptionName);
}
}
if (0 < optionsWithDiscrepancies.size() && !managedOverridesAllowed) {
String template = "Local settings present for one or more globally managed options: [%s]. These options are controlled through the %s interface; local settings have no effect.";
throw new JanusGraphConfigurationException(String.format(template, Joiner.on(", ").join(optionsWithDiscrepancies), ManagementSystem.class.getSimpleName()));
}
}
globalConfig = kcvsConfig.asReadConfiguration();
} finally {
kcvsConfig.close();
}
Configuration combinedConfig = new MixedConfiguration(ROOT_NS,globalConfig,localConfig);
//Compute unique instance id
this.uniqueGraphId = getOrGenerateUniqueInstanceId(combinedConfig);
overwrite.set(UNIQUE_INSTANCE_ID, this.uniqueGraphId);
//Default log configuration for system and tx log
//TRANSACTION LOG: send_delay=0, ttl=2days and backend=default
Preconditions.checkArgument(combinedConfig.get(LOG_BACKEND,TRANSACTION_LOG).equals(LOG_BACKEND.getDefaultValue()),
"Must use default log backend for transaction log");
Preconditions.checkArgument(!combinedConfig.has(LOG_SEND_DELAY,TRANSACTION_LOG) ||
combinedConfig.get(LOG_SEND_DELAY, TRANSACTION_LOG).isZero(),"Send delay must be 0 for transaction log.");
overwrite.set(LOG_SEND_DELAY, Duration.ZERO,TRANSACTION_LOG);
if (!combinedConfig.has(LOG_STORE_TTL,TRANSACTION_LOG) && TTLKCVSManager.supportsAnyTTL(storeFeatures)) {
overwrite.set(LOG_STORE_TTL,TRANSACTION_LOG_DEFAULT_TTL,TRANSACTION_LOG);
}
//SYSTEM MANAGEMENT LOG: backend=default and send_delay=0 and key_consistent=true and fixed-partitions=true
Preconditions.checkArgument(combinedConfig.get(LOG_BACKEND,MANAGEMENT_LOG).equals(LOG_BACKEND.getDefaultValue()),
"Must use default log backend for system log");
Preconditions.checkArgument(!combinedConfig.has(LOG_SEND_DELAY,MANAGEMENT_LOG) ||
combinedConfig.get(LOG_SEND_DELAY,MANAGEMENT_LOG).isZero(),"Send delay must be 0 for system log.");
overwrite.set(LOG_SEND_DELAY, Duration.ZERO, MANAGEMENT_LOG);
Preconditions.checkArgument(!combinedConfig.has(KCVSLog.LOG_KEY_CONSISTENT, MANAGEMENT_LOG) ||
combinedConfig.get(KCVSLog.LOG_KEY_CONSISTENT, MANAGEMENT_LOG), "Management log must be configured to be key-consistent");
overwrite.set(KCVSLog.LOG_KEY_CONSISTENT,true,MANAGEMENT_LOG);
Preconditions.checkArgument(!combinedConfig.has(KCVSLogManager.LOG_FIXED_PARTITION,MANAGEMENT_LOG)
|| combinedConfig.get(KCVSLogManager.LOG_FIXED_PARTITION,MANAGEMENT_LOG),"Fixed partitions must be enabled for management log");
overwrite.set(KCVSLogManager.LOG_FIXED_PARTITION,true,MANAGEMENT_LOG);
this.configuration = new MergedConfiguration(overwrite,combinedConfig);
preLoadConfiguration();
}
private void checkBackwardCompatibilityWithTitan(ModifiableConfiguration globalWrite) {
String version = globalWrite.get(TITAN_COMPATIBLE_VERSIONS);
Preconditions.checkArgument(version!=null,"JanusGraph version nor Titan compatibility have not been initialized");
if (!JanusGraphConstants.TITAN_COMPATIBLE_VERSIONS.contains(version)) {
throw new JanusGraphException(String.format(INCOMPATIBLE_VERSION_EXCEPTION, version, JanusGraphConstants.VERSION));
}
}
private static Map<ConfigElement.PathIdentifier, Object> getGlobalSubset(Map<ConfigElement.PathIdentifier, Object> m) {
return Maps.filterEntries(m, new Predicate<Map.Entry<ConfigElement.PathIdentifier, Object>>() {
@Override
public boolean apply(@Nullable Map.Entry<ConfigElement.PathIdentifier, Object> entry) {
assert entry.getKey().element.isOption();
return ((ConfigOption)entry.getKey().element).isGlobal();
}
});
}
private static Map<ConfigElement.PathIdentifier, Object> getLocalSubset(Map<ConfigElement.PathIdentifier, Object> m) {
return Maps.filterEntries(m, new Predicate<Map.Entry<ConfigElement.PathIdentifier, Object>>() {
@Override
public boolean apply(@Nullable Map.Entry<ConfigElement.PathIdentifier, Object> entry) {
assert entry.getKey().element.isOption();
return ((ConfigOption)entry.getKey().element).isLocal();
}
});
}
private static Map<ConfigElement.PathIdentifier, Object> getManagedSubset(Map<ConfigElement.PathIdentifier, Object> m) {
return Maps.filterEntries(m, new Predicate<Map.Entry<ConfigElement.PathIdentifier, Object>>() {
@Override
public boolean apply(@Nullable Map.Entry<ConfigElement.PathIdentifier, Object> entry) {
assert entry.getKey().element.isOption();
return ((ConfigOption)entry.getKey().element).isManaged();
}
});
}
private static final AtomicLong INSTANCE_COUNTER = new AtomicLong(0);
private static String computeUniqueInstanceId(Configuration config) {
final String suffix;
if (config.has(GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID_SUFFIX)) {
suffix = LongEncoding.encode(config.get(
GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID_SUFFIX));
} else {
suffix = ManagementFactory.getRuntimeMXBean().getName() + LongEncoding.encode(INSTANCE_COUNTER.incrementAndGet());
}
byte[] addrBytes;
try {
addrBytes = Inet4Address.getLocalHost().getAddress();
} catch (UnknownHostException e) {
throw new JanusGraphConfigurationException("Cannot determine local host", e);
}
String uid = new String(Hex.encodeHex(addrBytes)) + suffix;
for (char c : ConfigElement.ILLEGAL_CHARS) {
uid = StringUtils.replaceChars(uid,c,'-');
}
return uid;
}
public static String getOrGenerateUniqueInstanceId(Configuration config) {
String uid;
if (!config.has(UNIQUE_INSTANCE_ID)) {
uid = computeUniqueInstanceId(config);
log.info("Generated {}={}", UNIQUE_INSTANCE_ID.getName(), uid);
} else {
uid = config.get(UNIQUE_INSTANCE_ID);
}
Preconditions.checkArgument(!StringUtils.containsAny(uid,ConfigElement.ILLEGAL_CHARS),"Invalid unique identifier: %s",uid);
return uid;
}
public static final ModifiableConfiguration buildGraphConfiguration() {
return new ModifiableConfiguration(ROOT_NS,
new CommonsConfiguration(new BaseConfiguration()),
BasicConfiguration.Restriction.NONE);
}
public static final ModifiableConfiguration buildJobConfiguration() {
return new ModifiableConfiguration(JOB_NS,
new CommonsConfiguration(new BaseConfiguration()),
BasicConfiguration.Restriction.NONE);
}
public static final String getSystemMetricsPrefix() {
return METRICS_SYSTEM_PREFIX_DEFAULT;
}
public static ModifiableConfiguration getGlobalSystemConfig(Backend backend) {
return new ModifiableConfiguration(ROOT_NS,
backend.getGlobalSystemConfig(), BasicConfiguration.Restriction.GLOBAL);
}
private void preLoadConfiguration() {
readOnly = configuration.get(STORAGE_READONLY);
flushIDs = configuration.get(IDS_FLUSH);
forceIndexUsage = configuration.get(FORCE_INDEX_USAGE);
batchLoading = configuration.get(STORAGE_BATCH);
String autoTypeMakerName = configuration.get(AUTO_TYPE);
if (preregisteredAutoType.containsKey(autoTypeMakerName))
defaultSchemaMaker = preregisteredAutoType.get(autoTypeMakerName);
else defaultSchemaMaker = ConfigurationUtil.instantiate(autoTypeMakerName);
//Disable auto-type making when batch-loading is enabled since that may overwrite types without warning
if (batchLoading) defaultSchemaMaker = DisableDefaultSchemaMaker.INSTANCE;
txVertexCacheSize = configuration.get(TX_CACHE_SIZE);
//Check for explicit dirty vertex cache size first, then fall back on batch-loading-dependent default
if (configuration.has(TX_DIRTY_SIZE)) {
txDirtyVertexSize = configuration.get(TX_DIRTY_SIZE);
} else {
txDirtyVertexSize = batchLoading ?
TX_DIRTY_SIZE_DEFAULT_WITH_BATCH :
TX_DIRTY_SIZE_DEFAULT_WITHOUT_BATCH;
}
propertyPrefetching = configuration.get(PROPERTY_PREFETCHING);
useMultiQuery = configuration.get(USE_MULTIQUERY);
adjustQueryLimit = configuration.get(ADJUST_LIMIT);
allowVertexIdSetting = configuration.get(ALLOW_SETTING_VERTEX_ID);
logTransactions = configuration.get(SYSTEM_LOG_TRANSACTIONS);
unknownIndexKeyName = configuration.get(IGNORE_UNKNOWN_INDEX_FIELD) ? UKNOWN_FIELD_NAME : null;
configureMetrics();
}
private void configureMetrics() {
Preconditions.checkNotNull(configuration);
metricsPrefix = configuration.get(METRICS_PREFIX);
if (!configuration.get(BASIC_METRICS)) {
metricsPrefix = null;
} else {
Preconditions.checkNotNull(metricsPrefix);
}
configureMetricsConsoleReporter();
configureMetricsCsvReporter();
configureMetricsJmxReporter();
configureMetricsSlf4jReporter();
configureMetricsGangliaReporter();
configureMetricsGraphiteReporter();
}
private void configureMetricsConsoleReporter() {
if (configuration.has(METRICS_CONSOLE_INTERVAL)) {
MetricManager.INSTANCE.addConsoleReporter(configuration.get(METRICS_CONSOLE_INTERVAL));
}
}
private void configureMetricsCsvReporter() {
if (configuration.has(METRICS_CSV_DIR)) {
MetricManager.INSTANCE.addCsvReporter(configuration.get(METRICS_CSV_INTERVAL), configuration.get(METRICS_CSV_DIR));
}
}
private void configureMetricsJmxReporter() {
if (configuration.get(METRICS_JMX_ENABLED)) {
MetricManager.INSTANCE.addJmxReporter(configuration.get(METRICS_JMX_DOMAIN), configuration.get(METRICS_JMX_AGENTID));
}
}
private void configureMetricsSlf4jReporter() {
if (configuration.has(METRICS_SLF4J_INTERVAL)) {
// null loggerName is allowed -- that means Metrics will use its internal default
MetricManager.INSTANCE.addSlf4jReporter(configuration.get(METRICS_SLF4J_INTERVAL),
configuration.has(METRICS_SLF4J_LOGGER) ? configuration.get(METRICS_SLF4J_LOGGER) : null);
}
}
private void configureMetricsGangliaReporter() {
if (configuration.has(GANGLIA_HOST_OR_GROUP)) {
final String host = configuration.get(GANGLIA_HOST_OR_GROUP);
final Duration ival = configuration.get(GANGLIA_INTERVAL);
final Integer port = configuration.get(GANGLIA_PORT);
final UDPAddressingMode addrMode;
final String addrModeStr = configuration.get(GANGLIA_ADDRESSING_MODE);
if (addrModeStr.equalsIgnoreCase("multicast")) {
addrMode = UDPAddressingMode.MULTICAST;
} else if (addrModeStr.equalsIgnoreCase("unicast")) {
addrMode = UDPAddressingMode.UNICAST;
} else throw new AssertionError();
final Boolean proto31 = configuration.get(GANGLIA_USE_PROTOCOL_31);
final int ttl = configuration.get(GANGLIA_TTL);
final UUID uuid = configuration.has(GANGLIA_UUID)? UUID.fromString(configuration.get(GANGLIA_UUID)):null;
String spoof = null;
if (configuration.has(GANGLIA_SPOOF)) spoof = configuration.get(GANGLIA_SPOOF);
try {
MetricManager.INSTANCE.addGangliaReporter(host, port, addrMode, ttl, proto31, uuid, spoof, ival);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private void configureMetricsGraphiteReporter() {
if (configuration.has(GRAPHITE_HOST)) {
MetricManager.INSTANCE.addGraphiteReporter(configuration.get(GRAPHITE_HOST),
configuration.get(GRAPHITE_PORT),
configuration.get(GRAPHITE_PREFIX),
configuration.get(GRAPHITE_INTERVAL));
}
}
public boolean isReadOnly() {
return readOnly;
}
public boolean hasFlushIDs() {
return flushIDs;
}
public boolean hasForceIndexUsage() {
return forceIndexUsage;
}
public int getTxVertexCacheSize() {
return txVertexCacheSize;
}
public int getTxDirtyVertexSize() {
return txDirtyVertexSize;
}
public boolean isBatchLoading() {
return batchLoading;
}
public String getUniqueGraphId() {
return uniqueGraphId;
}
public String getMetricsPrefix() {
return metricsPrefix;
}
public DefaultSchemaMaker getDefaultSchemaMaker() {
return defaultSchemaMaker;
}
public boolean allowVertexIdSetting() {
return allowVertexIdSetting;
}
public Duration getMaxCommitTime() {
return configuration.get(MAX_COMMIT_TIME);
}
public Duration getMaxWriteTime() {
return configuration.get(STORAGE_WRITE_WAITTIME);
}
public boolean hasPropertyPrefetching() {
if (propertyPrefetching == null) {
return getStoreFeatures().isDistributed();
} else {
return propertyPrefetching;
}
}
public boolean useMultiQuery() {
return useMultiQuery;
}
public boolean adjustQueryLimit() {
return adjustQueryLimit;
}
public String getUnknownIndexKeyName() {
return unknownIndexKeyName;
}
public boolean hasLogTransactions() {
return logTransactions;
}
public TimestampProvider getTimestampProvider() {
return configuration.get(TIMESTAMP_PROVIDER);
}
public static List<RegisteredAttributeClass<?>> getRegisteredAttributeClasses(Configuration configuration) {
List<RegisteredAttributeClass<?>> all = new ArrayList<RegisteredAttributeClass<?>>();
for (String attributeId : configuration.getContainedNamespaces(CUSTOM_ATTRIBUTE_NS)) {
Preconditions.checkArgument(attributeId.startsWith(ATTRIBUTE_PREFIX),"Invalid attribute definition: %s",attributeId);
int position;
try {
position = Integer.parseInt(attributeId.substring(ATTRIBUTE_PREFIX.length()));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Expected entry of the form ["+ ATTRIBUTE_PREFIX +"X] where X is a number but given" + attributeId);
}
Class<?> clazz = null;
AttributeSerializer<?> serializer = null;
String classname = configuration.get(CUSTOM_ATTRIBUTE_CLASS,attributeId);
try {
clazz = Class.forName(classname);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Could not find attribute class" + classname, e);
}
Preconditions.checkNotNull(clazz);
Preconditions.checkArgument(configuration.has(CUSTOM_SERIALIZER_CLASS, attributeId));
String serializername = configuration.get(CUSTOM_SERIALIZER_CLASS, attributeId);
try {
Class<?> sclass = Class.forName(serializername);
serializer = (AttributeSerializer) sclass.newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Could not find serializer class" + serializername);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Could not instantiate serializer class" + serializername, e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Could not instantiate serializer class" + serializername, e);
}
Preconditions.checkNotNull(serializer);
RegisteredAttributeClass reg = new RegisteredAttributeClass(position, clazz, serializer);
for (int i = 0; i < all.size(); i++) {
if (all.get(i).equals(reg)) {
throw new IllegalArgumentException("Duplicate attribute registration: " + all.get(i) + " and " + reg);
}
}
all.add(reg);
}
return all;
}
public VertexIDAssigner getIDAssigner(Backend backend) {
return new VertexIDAssigner(configuration, backend.getIDAuthority(), backend.getStoreFeatures());
}
public String getBackendDescription() {
String clazzname = configuration.get(STORAGE_BACKEND);
if (clazzname.equalsIgnoreCase("berkeleyje")) {
return clazzname + ":" + configuration.get(STORAGE_DIRECTORY);
} else {
return clazzname + ":" + Arrays.toString(configuration.get(STORAGE_HOSTS));
}
}
public Configuration getConfiguration() {
return configuration;
}
public Backend getBackend() {
Backend backend = new Backend(configuration);
backend.initialize(configuration);
storeFeatures = backend.getStoreFeatures();
return backend;
}
public StoreFeatures getStoreFeatures() {
Preconditions.checkArgument(storeFeatures != null, "Cannot retrieve store features before the storage backend has been initialized");
return storeFeatures;
}
public Serializer getSerializer() {
return getSerializer(configuration);
}
public static Serializer getSerializer(Configuration configuration) {
Serializer serializer = new StandardSerializer();
for (RegisteredAttributeClass<?> clazz : getRegisteredAttributeClasses(configuration)) {
clazz.registerWith(serializer);
}
return serializer;
}
public SchemaCache getTypeCache(SchemaCache.StoreRetrieval retriever) {
if (configuration.get(BASIC_METRICS)) return new MetricInstrumentedSchemaCache(retriever);
else return new StandardSchemaCache(retriever);
}
public org.apache.commons.configuration.Configuration getLocalConfiguration() {
org.apache.commons.configuration.Configuration config = ((CommonsConfiguration)localConfiguration.getConfiguration()).getCommonConfiguration();
config.setProperty(Graph.GRAPH, JanusGraphFactory.class.getName());
return config;
}
public org.apache.commons.configuration.Configuration getConfigurationAtOpen() {
org.apache.commons.configuration.Configuration result = new BaseConfiguration();
for (String k : configurationAtOpen.getKeys("")) {
result.setProperty(k, configurationAtOpen.get(k, Object.class));
}
return result;
}
/* ----------------------------------------
Methods for writing/reading config files
-------------------------------------------*/
public static String getPath(File dir) {
return dir.getAbsolutePath() + File.separator;
}
}