package com.splout.db.common; /* * #%L * Splout SQL commons * %% * Copyright (C) 2012 Datasalt Systems S.L. * %% * 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. * #L% */ import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.type.TypeReference; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * A Splout's partition map definition - The generic type is the type of the Key. For example, if a tablespace is partitioned by an * Integer then T = Integer. * <p/> * A partition map is composed by several {@link PartitionEntry}. The partition map implements the logic of finding a * partition number given a key of type <T> in {@link #findPartition(String)}. */ @SuppressWarnings("serial") @JsonIgnoreProperties(ignoreUnknown = true) // Backwards compatibility in JSON (new fields don't make things break) public class PartitionMap implements Serializable { public String toString() { return partitionEntries.toString(); } public final static TypeReference<PartitionMap> PARTITION_MAP_REF = new TypeReference<PartitionMap>() { }; // When a key can't be found in any partition. // An opened question is whether this shouldn't be contemplated and an exception should be thrown instead public static int NO_PARTITION = -1; private List<PartitionEntry> partitionEntries; public PartitionMap() { } /** * Use this method for creating a PartitionMap that has only one shard (0) and whose range is -Infinity, +Infinity */ public static PartitionMap oneShardOpenedMap() { // For convenience, we create a 1-shard opened map here (shard = 0) List<PartitionEntry> partitionMap = new ArrayList<PartitionEntry>(); PartitionEntry openedEntry = new PartitionEntry(); openedEntry.setMin(null); openedEntry.setMax(null); openedEntry.setShard(0); partitionMap.add(openedEntry); return new PartitionMap(partitionMap); } public PartitionMap(List<PartitionEntry> partitionMap) { if (partitionMap == null || partitionMap.size() == 0) { throw new IllegalArgumentException("Can't create an empty / null partition map. For an opened 1-shard map with shard id = 0 use default constructor."); } this.partitionEntries = partitionMap; } /** * Given a min and a max key, return all the partitions that impact this range, min and max inclusive. * <p/> * Note that (null, null) is a valid input to this method and will be interpreted as the whole key range, regardless * of the key type (that's why we use null for representing opened ranges). */ public List<Integer> findPartitions(String minKey, String maxKey) { List<Integer> partitions = new ArrayList<Integer>(); int minPartitionIndex = 0, maxPartitionIndex = partitionEntries.size() - 1; if (minKey != null) { minPartitionIndex = findPartition(minKey); } if (maxKey != null) { maxPartitionIndex = findPartition(maxKey); } for (int i = minPartitionIndex; i <= maxPartitionIndex; i++) { partitions.add(partitionEntries.get(i).getShard()); } return partitions; } /** * Given a key, return the partition that it belongs to, or {@link #NO_PARTITION} if none matches. Mathematically, * partitions match (min, max]. */ public int findPartition(String keyObj) { if (keyObj == null) { throw new IllegalArgumentException("Key obj can't be null for findPartition()"); } for (PartitionEntry entry : partitionEntries) { // We assume (-Infinity, Infinity) matching since nulls represent Infinity for any type <T> boolean minMatches = true; boolean maxMatches = true; if (entry.getMin() != null) { if ((entry.getMin()).compareTo(keyObj) >= 0) { minMatches = false; } } if (entry.getMax() != null) { if ((entry.getMax()).compareTo(keyObj) < 0) { maxMatches = false; } } if (minMatches && maxMatches) { return entry.getShard(); } } return NO_PARTITION; } public void setPartitionEntries(List<PartitionEntry> partitionEntries) { this.partitionEntries = partitionEntries; } public List<PartitionEntry> getPartitionEntries() { return partitionEntries; } }