// 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.database.idassigner.placement; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import org.janusgraph.core.JanusGraphVertex; import org.janusgraph.core.JanusGraphVertexProperty; import org.janusgraph.diskstorage.configuration.ConfigOption; import org.janusgraph.diskstorage.configuration.Configuration; import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; import org.janusgraph.graphdb.configuration.PreInitializeConfigOptions; import org.janusgraph.graphdb.database.idassigner.IDPoolExhaustedException; import org.janusgraph.graphdb.idmanagement.IDManager; import org.janusgraph.graphdb.internal.InternalElement; import org.janusgraph.graphdb.internal.InternalVertex; import org.apache.commons.lang3.StringUtils; import java.util.Map; /** * @author Matthias Broecheler (me@matthiasb.com) */ @PreInitializeConfigOptions public class PropertyPlacementStrategy extends SimpleBulkPlacementStrategy { public static final ConfigOption<String> PARTITION_KEY = new ConfigOption<String>(GraphDatabaseConfiguration.IDS_NS, "partition-key","Partitions the graph by properties of this key", ConfigOption.Type.MASKABLE, String.class, key -> StringUtils.isNotBlank(key)); private String key; private IDManager idManager; public PropertyPlacementStrategy(Configuration config) { super(config); setPartitionKey(config.get(PARTITION_KEY)); } public PropertyPlacementStrategy(String key, int concurrentPartitions) { super(concurrentPartitions); setPartitionKey(key); } public void setPartitionKey(String key) { Preconditions.checkArgument(StringUtils.isNotBlank(key),"Invalid key configured: %s",key); this.key=key; } @Override public void injectIDManager(IDManager idManager) { Preconditions.checkNotNull(idManager); this.idManager=idManager; } @Override public int getPartition(InternalElement element) { if (element instanceof JanusGraphVertex) { int pid = getPartitionIDbyKey((JanusGraphVertex)element); if (pid>=0) return pid; } return super.getPartition(element); } @Override public void getPartitions(Map<InternalVertex, PartitionAssignment> vertices) { super.getPartitions(vertices); for (Map.Entry<InternalVertex, PartitionAssignment> entry : vertices.entrySet()) { int pid = getPartitionIDbyKey(entry.getKey()); if (pid>=0) ((SimplePartitionAssignment)entry.getValue()).setPartitionID(pid); } } private int getPartitionIDbyKey(JanusGraphVertex vertex) { Preconditions.checkState(idManager!=null && key!=null,"PropertyPlacementStrategy has not been initialized correctly"); assert idManager.getPartitionBound()<=Integer.MAX_VALUE; int partitionBound = (int)idManager.getPartitionBound(); JanusGraphVertexProperty p = (JanusGraphVertexProperty)Iterables.getFirst(vertex.query().keys(key).properties(),null); if (p==null) return -1; int hashPid = Math.abs(p.value().hashCode())%partitionBound; assert hashPid>=0 && hashPid<partitionBound; if (isExhaustedPartition(hashPid)) { //We keep trying consecutive partition ids until we find a non-exhausted one int newPid=hashPid; do { newPid = (newPid+1)%partitionBound; if (newPid==hashPid) //We have gone full circle - no more ids to try throw new IDPoolExhaustedException("Could not find non-exhausted partition"); } while (isExhaustedPartition(newPid)); return newPid; } else return hashPid; } }