/** * 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.executionenv.querystats; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableSet; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import org.diqube.data.column.ColumnPage; import org.diqube.data.column.ColumnShard; import org.diqube.data.column.ColumnType; import org.diqube.data.column.StandardColumnShard; import org.diqube.data.dictionary.Dictionary; import org.diqube.queries.QueryRegistry; import org.diqube.queries.QueryUuid; import org.diqube.queries.QueryUuid.QueryUuidThreadState; import org.diqube.util.DiqubeCollectors; import org.diqube.util.Pair; /** * Abstract base implementation of {@link QueryableColumnShard}. * * @author Bastian Gloeckle */ public abstract class AbstractQueryableColumnShardFacade implements QueryableColumnShard { private ColumnShard delegate; private QueryRegistry queryRegistry; private boolean isTempColumn; /** * @param isTempColumn * is ignored if queryRegistry == null. * @param queryRegistry * Can be <code>null</code>, then no stats will be collected. */ public AbstractQueryableColumnShardFacade(ColumnShard delegate, boolean isTempColumn, QueryRegistry queryRegistry) { this.delegate = delegate; this.isTempColumn = isTempColumn; this.queryRegistry = queryRegistry; } @Override public long calculateApproximateSizeInBytes() { return delegate.calculateApproximateSizeInBytes() + // 9 // some constant for the other fields. ; } @Override public Dictionary<?> getColumnShardDictionary() { return delegate.getColumnShardDictionary(); } @Override public String getName() { return delegate.getName(); } @Override public ColumnType getColumnType() { return delegate.getColumnType(); } @Override public long getFirstRowId() { return delegate.getFirstRowId(); } @Override public Map<Long, Long> resolveColumnValueIdsForRows(Collection<Long> rowIds) { if (delegate instanceof StandardColumnShard) { QueryUuidThreadState uuidState = QueryUuid.getCurrentThreadState(); try { Map<ColumnPage, NavigableSet<Long>> rowIdsByPage = rowIds.stream().parallel().collect( // Collectors.groupingByConcurrent(rowId -> { QueryUuid.setCurrentThreadState(uuidState); try { Entry<Long, ColumnPage> e = ((StandardColumnShard) delegate).getPages().floorEntry(rowId); if (e == null) // filter out too low row IDs. This might happen if this column shard has a firstRowId that is > than // a provided rowId. return null; return e.getValue(); } finally { QueryUuid.clearCurrent(); } } , DiqubeCollectors.toNavigableSet())); if (queryRegistry != null) { QueryUuid.setCurrentThreadState(uuidState); for (ColumnPage page : rowIdsByPage.keySet()) queryRegistry.getOrCreateCurrentStatsManager().registerPageAccess(page, isTempColumn); } Map<Long, Long> rowIdToColumnValueId = rowIdsByPage.entrySet().stream().parallel().filter(e -> e.getKey() != null) .flatMap(new Function<Entry<ColumnPage, NavigableSet<Long>>, Stream<Pair<Long, Long>>>() { @Override public Stream<Pair<Long, Long>> apply(Entry<ColumnPage, NavigableSet<Long>> entry) { QueryUuid.setCurrentThreadState(uuidState); try { List<Pair<Long, Long>> res = new ArrayList<>(); ColumnPage page = entry.getKey(); // take only those rowIDs that are inside the page - if there were row IDs provided that we do not // have any values of, do not return those entries. This may happen for the last page of a column. NavigableSet<Long> rowIds = entry.getValue().headSet(page.getFirstRowId() + page.getValues().size(), false); List<Long> columnPageValueIds = page.getValues().getMultiple(rowIds.stream() .map(rowId -> (int) (rowId - page.getFirstRowId())).collect(Collectors.toList())); Long[] columnValueIds = page.getColumnPageDict() .decompressValues(columnPageValueIds.toArray(new Long[columnPageValueIds.size()])); Iterator<Long> rowIdIt = rowIds.iterator(); for (int i = 0; i < columnValueIds.length; i++) res.add(new Pair<Long, Long>(rowIdIt.next(), columnValueIds[i])); return res.stream(); } finally { QueryUuid.clearCurrent(); } } }).collect(() -> new HashMap<Long, Long>(), (map, pair) -> map.put(pair.getLeft(), pair.getRight()), (map1, map2) -> map1.putAll(map2)); return rowIdToColumnValueId; } finally { QueryUuid.setCurrentThreadState(uuidState); } } Map<Long, Long> res = new HashMap<>(); rowIds.forEach(rowId -> res.put(rowId, 0L)); return res; } @Override public Long[] resolveColumnValueIdsForRowsFlat(List<Long> rowIds) { if (delegate instanceof StandardColumnShard) { Map<Long, Long> rowIdToColumnValueId = resolveColumnValueIdsForRows(rowIds); Long[] res = new Long[rowIds.size()]; for (int i = 0; i < rowIds.size(); i++) res[i] = (rowIdToColumnValueId.containsKey(rowIds.get(i))) ? rowIdToColumnValueId.get(rowIds.get(i)) : -1L; return res; } Long[] res = new Long[rowIds.size()]; Arrays.fill(res, 0L); return res; } @Override public long resolveColumnValueIdForRow(Long rowId) { if (delegate instanceof StandardColumnShard) { Entry<Long, ColumnPage> floorEntry = ((StandardColumnShard) delegate).getPages().floorEntry(rowId); if (floorEntry == null) return -1; ColumnPage page = floorEntry.getValue(); if (rowId >= page.getFirstRowId() + page.getValues().size()) return -1; if (queryRegistry != null) queryRegistry.getOrCreateCurrentStatsManager().registerPageAccess(page, isTempColumn); return page.getColumnPageDict().decompressValue(page.getValues().get((int) (rowId - page.getFirstRowId()))); } return 0; } @Override public Set<Pair<Long, Integer>> getGoodResolutionPairs() { if (delegate instanceof StandardColumnShard) { Set<Pair<Long, Integer>> res = ((StandardColumnShard) delegate).getPages().entrySet().stream(). // map(entry -> new Pair<Long, Integer>(entry.getKey(), entry.getValue().size())). // collect(Collectors.toSet()); return res; } return new HashSet<>(Arrays.asList(new Pair<>(delegate.getFirstRowId(), 1))); } @Override public ColumnShard getDelegate() { return delegate; } }