/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.data.table; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Default implementation for a {@link Table} which contains raw data that was loaded from input files. * * @author Bastian Gloeckle */ public class DefaultTable implements AdjustableTable { private String name; private Collection<TableShard> shards; private ReentrantReadWriteLock shardsLock = new ReentrantReadWriteLock(); /* package */ DefaultTable(String name, Collection<TableShard> shards) { this.name = name; this.shards = shards; } @Override public String getName() { return name; } @Override public Collection<TableShard> getShards() { shardsLock.readLock().lock(); try { return Collections.unmodifiableCollection(new ArrayList<>(shards)); } finally { shardsLock.readLock().unlock(); } } @Override public void addTableShard(TableShard tableShard) throws TableShardsOverlappingException { shardsLock.writeLock().lock(); try { // exchange list to make sure that lists already returned by getShards() will be iterable. List<TableShard> newShards = new ArrayList<>(shards); newShards.add(tableShard); NavigableMap<Long, Long> rowIdMap = new TreeMap<>(); for (TableShard shard : newShards) { if (rowIdMap.containsKey(shard.getLowestRowId())) throw new TableShardsOverlappingException("Two TableShards with first row ID " + shard.getLowestRowId()); rowIdMap.put(shard.getLowestRowId(), shard.getLowestRowId() + shard.getNumberOfRowsInShard() - 1); } for (Long lowestRowId : rowIdMap.keySet()) { Long nextKey = rowIdMap.ceilingKey(lowestRowId + 1); if (nextKey != null && nextKey <= rowIdMap.get(lowestRowId)) throw new TableShardsOverlappingException( "There are overlapping TableShards. One contains data for rowIds " + lowestRowId + "-" + rowIdMap.get(lowestRowId) + " and another for rowIds " + nextKey + "-" + rowIdMap.get(nextKey)); } shards = newShards; } finally { shardsLock.writeLock().unlock(); } } @Override public boolean removeTableShard(TableShard tableShard) { shardsLock.writeLock().lock(); try { // exchange list to make sure that lists already returned by getShards() will be iterable. List<TableShard> newShards = new ArrayList<>(shards); if (!newShards.remove(tableShard)) return false; shards = newShards; return true; } finally { shardsLock.writeLock().unlock(); } } @Override public long calculateApproximateSizeInBytes() { long shardsSize = 0L; for (TableShard shard : shards) shardsSize += shard.calculateApproximateSizeInBytes(); return 16 + // name.length() + // shardsSize; } }