All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.janusgraph.diskstorage.hbase.HBaseKeyColumnValueStore Maven / Gradle / Ivy

The newest version!
// 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.diskstorage.hbase;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import org.apache.hadoop.hbase.util.Bytes;
import org.janusgraph.diskstorage.*;
import org.janusgraph.diskstorage.keycolumnvalue.*;
import org.janusgraph.diskstorage.util.RecordIterator;
import org.janusgraph.diskstorage.util.StaticArrayBuffer;
import org.janusgraph.diskstorage.util.StaticArrayEntry;
import org.janusgraph.diskstorage.util.StaticArrayEntryList;
import org.janusgraph.util.system.IOUtils;

import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.ColumnPaginationFilter;
import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;

import java.io.Closeable;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.*;

/**
 * Here are some areas that might need work:
 * 

* - batching? (consider HTable#batch, HTable#setAutoFlush(false) * - tuning HTable#setWriteBufferSize (?) * - writing a server-side filter to replace ColumnCountGetFilter, which drops * all columns on the row where it reaches its limit. This requires getSlice, * currently, to impose its limit on the client side. That obviously won't * scale. * - RowMutations for combining Puts+Deletes (need a newer HBase than 0.92 for this) * - (maybe) fiddle with HTable#setRegionCachePrefetch and/or #prewarmRegionCache *

* There may be other problem areas. These are just the ones of which I'm aware. */ public class HBaseKeyColumnValueStore implements KeyColumnValueStore { private static final Logger logger = LoggerFactory.getLogger(HBaseKeyColumnValueStore.class); private final String tableName; private final HBaseStoreManager storeManager; // When using shortened CF names, columnFamily is the shortname and storeName is the longname // When not using shortened CF names, they are the same //private final String columnFamily; private final String storeName; // This is columnFamily.getBytes() private final byte[] columnFamilyBytes; private final HBaseGetter entryGetter; private final ConnectionMask cnx; HBaseKeyColumnValueStore(HBaseStoreManager storeManager, ConnectionMask cnx, String tableName, String columnFamily, String storeName) { this.storeManager = storeManager; this.cnx = cnx; this.tableName = tableName; //this.columnFamily = columnFamily; this.storeName = storeName; this.columnFamilyBytes = Bytes.toBytes(columnFamily); this.entryGetter = new HBaseGetter(storeManager.getMetaDataSchema(storeName)); } @Override public void close() throws BackendException { } @Override public EntryList getSlice(KeySliceQuery query, StoreTransaction txh) throws BackendException { Map result = getHelper(Collections.singletonList(query.getKey()), getFilter(query)); return Iterables.getOnlyElement(result.values(), EntryList.EMPTY_LIST); } @Override public Map getSlice(List keys, SliceQuery query, StoreTransaction txh) throws BackendException { return getHelper(keys, getFilter(query)); } @Override public void mutate(StaticBuffer key, List additions, List deletions, StoreTransaction txh) throws BackendException { Map mutations = ImmutableMap.of(key, new KCVMutation(additions, deletions)); mutateMany(mutations, txh); } @Override public void acquireLock(StaticBuffer key, StaticBuffer column, StaticBuffer expectedValue, StoreTransaction txh) throws BackendException { throw new UnsupportedOperationException(); } @Override public KeyIterator getKeys(KeyRangeQuery query, StoreTransaction txh) throws BackendException { return executeKeySliceQuery(query.getKeyStart().as(StaticBuffer.ARRAY_FACTORY), query.getKeyEnd().as(StaticBuffer.ARRAY_FACTORY), new FilterList(FilterList.Operator.MUST_PASS_ALL), query); } @Override public String getName() { return storeName; } @Override public KeyIterator getKeys(SliceQuery query, StoreTransaction txh) throws BackendException { return executeKeySliceQuery(new FilterList(FilterList.Operator.MUST_PASS_ALL), query); } public static Filter getFilter(SliceQuery query) { byte[] colStartBytes = query.getSliceStart().length() > 0 ? query.getSliceStart().as(StaticBuffer.ARRAY_FACTORY) : null; byte[] colEndBytes = query.getSliceEnd().length() > 0 ? query.getSliceEnd().as(StaticBuffer.ARRAY_FACTORY) : null; Filter filter = new ColumnRangeFilter(colStartBytes, true, colEndBytes, false); if (query.hasLimit()) { filter = new FilterList(FilterList.Operator.MUST_PASS_ALL, filter, new ColumnPaginationFilter(query.getLimit(), colStartBytes)); } logger.debug("Generated HBase Filter {}", filter); return filter; } private Map getHelper(List keys, Filter getFilter) throws BackendException { List requests = new ArrayList<>(keys.size()); { for (StaticBuffer key : keys) { Get g = new Get(key.as(StaticBuffer.ARRAY_FACTORY)).addFamily(columnFamilyBytes).setFilter(getFilter); try { g.setTimeRange(0, Long.MAX_VALUE); } catch (IOException e) { throw new PermanentBackendException(e); } requests.add(g); } } final Map resultMap = new HashMap<>(keys.size()); try { TableMask table = null; final Result[] results; try { table = cnx.getTable(tableName); results = table.get(requests); } finally { IOUtils.closeQuietly(table); } if (results == null) return KCVSUtil.emptyResults(keys); assert results.length==keys.size(); for (int i = 0; i < results.length; i++) { final Result result = results[i]; NavigableMap>> f = result.getMap(); if (f == null) { // no result for this key resultMap.put(keys.get(i), EntryList.EMPTY_LIST); continue; } // actual key with NavigableMap> r = f.get(columnFamilyBytes); resultMap.put(keys.get(i), (r == null) ? EntryList.EMPTY_LIST : StaticArrayEntryList.ofBytes(r.entrySet(), entryGetter)); } return resultMap; } catch (InterruptedIOException e) { // added to support traversal interruption Thread.currentThread().interrupt(); throw new PermanentBackendException(e); } catch (IOException e) { throw new TemporaryBackendException(e); } } private void mutateMany(Map mutations, StoreTransaction txh) throws BackendException { storeManager.mutateMany(ImmutableMap.of(storeName, mutations), txh); } private KeyIterator executeKeySliceQuery(FilterList filters, @Nullable SliceQuery columnSlice) throws BackendException { return executeKeySliceQuery(null, null, filters, columnSlice); } private KeyIterator executeKeySliceQuery(@Nullable byte[] startKey, @Nullable byte[] endKey, FilterList filters, @Nullable SliceQuery columnSlice) throws BackendException { Scan scan = new Scan().addFamily(columnFamilyBytes); try { scan.setTimeRange(0, Long.MAX_VALUE); } catch (IOException e) { throw new PermanentBackendException(e); } if (startKey != null) scan.setStartRow(startKey); if (endKey != null) scan.setStopRow(endKey); if (columnSlice != null) { filters.addFilter(getFilter(columnSlice)); } TableMask table = null; try { table = cnx.getTable(tableName); return new RowIterator(table, table.getScanner(scan.setFilter(filters)), columnFamilyBytes); } catch (IOException e) { IOUtils.closeQuietly(table); throw new PermanentBackendException(e); } } private class RowIterator implements KeyIterator { private final Closeable table; private final Iterator rows; private final byte[] columnFamilyBytes; private Result currentRow; private boolean isClosed; public RowIterator(Closeable table, ResultScanner rows, byte[] columnFamilyBytes) { this.table = table; this.columnFamilyBytes = Arrays.copyOf(columnFamilyBytes, columnFamilyBytes.length); this.rows = Iterators.filter(rows.iterator(), result -> null != result && null != result.getRow()); } @Override public RecordIterator getEntries() { ensureOpen(); return new RecordIterator() { private final Iterator>> kv; { final Map>> map = currentRow.getMap(); Preconditions.checkNotNull(map); kv = map.get(columnFamilyBytes).entrySet().iterator(); } @Override public boolean hasNext() { ensureOpen(); return kv.hasNext(); } @Override public Entry next() { ensureOpen(); return StaticArrayEntry.ofBytes(kv.next(), entryGetter); } @Override public void close() { isClosed = true; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public boolean hasNext() { ensureOpen(); return rows.hasNext(); } @Override public StaticBuffer next() { ensureOpen(); currentRow = rows.next(); return StaticArrayBuffer.of(currentRow.getRow()); } @Override public void close() { IOUtils.closeQuietly(table); isClosed = true; logger.debug("RowIterator closed table {}", table); } @Override public void remove() { throw new UnsupportedOperationException(); } private void ensureOpen() { if (isClosed) throw new IllegalStateException("Iterator has been closed."); } } private static class HBaseGetter implements StaticArrayEntry.GetColVal>, byte[]> { private final EntryMetaData[] schema; private HBaseGetter(EntryMetaData[] schema) { this.schema = schema; } @Override public byte[] getColumn(Map.Entry> element) { return element.getKey(); } @Override public byte[] getValue(Map.Entry> element) { return element.getValue().lastEntry().getValue(); } @Override public EntryMetaData[] getMetaSchema(Map.Entry> element) { return schema; } @Override public Object getMetaData(Map.Entry> element, EntryMetaData meta) { switch(meta) { case TIMESTAMP: return element.getValue().lastEntry().getKey(); default: throw new UnsupportedOperationException("Unsupported meta data: " + meta); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy