/*
* Copyright 2012 NGDATA nv
*
* 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.lilyproject.util.hbase;
import java.io.IOException;
import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.util.Bytes;
public class LilyHBaseSchema {
private static final HColumnDescriptor DATA_CF;
public static final byte[] TABLE_TYPE_PROPERTY = Bytes.toBytes("lilyTableType");
public static final byte[] TABLE_TYPE_RECORD = Bytes.toBytes("record");
static {
DATA_CF = new HColumnDescriptor(RecordCf.DATA.bytes,
HConstants.ALL_VERSIONS, "none", false, true, HConstants.FOREVER, HColumnDescriptor.DEFAULT_BLOOMFILTER);
DATA_CF.setScope(1); // replication scope: HBase docs: "a scope of 0 (default) means that it won't be
// replicated and a scope of 1 means it's going to be. In the future, different scope can
// be used for routing policies."
}
private static final HTableDescriptor typeTableDescriptor;
static {
typeTableDescriptor = new HTableDescriptor(Table.TYPE.bytes);
typeTableDescriptor.addFamily(new HColumnDescriptor(TypeCf.DATA.bytes, HConstants.ALL_VERSIONS,
"none", false, true, HConstants.FOREVER, HColumnDescriptor.DEFAULT_BLOOMFILTER));
typeTableDescriptor.addFamily(new HColumnDescriptor(TypeCf.FIELDTYPE_ENTRY.bytes, HConstants.ALL_VERSIONS,
"none", false, true, HConstants.FOREVER, HColumnDescriptor.DEFAULT_BLOOMFILTER));
typeTableDescriptor.addFamily(new HColumnDescriptor(TypeCf.SUPERTYPE.bytes, HConstants.ALL_VERSIONS, "none",
false, true, HConstants.FOREVER, HColumnDescriptor.DEFAULT_BLOOMFILTER));
}
private static final HTableDescriptor blobIncubatorDescriptor;
static {
blobIncubatorDescriptor = new HTableDescriptor(Table.BLOBINCUBATOR.bytes);
blobIncubatorDescriptor.addFamily(new HColumnDescriptor(BlobIncubatorCf.REF.bytes));
}
private LilyHBaseSchema() {
}
@VisibleForTesting
static HTableDescriptor createRecordTableDescriptor(String repositoryName, String tableName) {
// We have checks on table name in TableManagerImpl -- probably this can go
if (tableName.contains(".") || tableName.contains(":")) {
throw new IllegalArgumentException("Repository table name cannot contain periods or colons");
}
String hbaseTableName = RepoAndTableUtil.getHBaseTableName(repositoryName, tableName);
HTableDescriptor recordTableDescriptor = new HTableDescriptor(hbaseTableName);
recordTableDescriptor.addFamily(DATA_CF);
recordTableDescriptor.setValue(TABLE_TYPE_PROPERTY, TABLE_TYPE_RECORD);
RepoAndTableUtil.setRepositoryOwnership(recordTableDescriptor, repositoryName);
return recordTableDescriptor;
}
public static boolean isRecordTableDescriptor(HTableDescriptor htableDescriptor) {
byte[] value = htableDescriptor.getValue(TABLE_TYPE_PROPERTY);
return value != null && Bytes.equals(value, TABLE_TYPE_RECORD);
}
public static HTableInterface getRecordTable(HBaseTableFactory tableFactory, String repositoryName, String tableName) throws IOException, InterruptedException {
HTableInterface recordTable = tableFactory.getTable(createRecordTableDescriptor(repositoryName, tableName));
verifyIsRecordTable(recordTable.getTableDescriptor());
return recordTable;
}
public static HTableInterface getRecordTable(HBaseTableFactory tableFactory, String repositoryName, String tableName, byte[][] splitKeys) throws IOException, InterruptedException {
HTableInterface recordTable = tableFactory.getTable(createRecordTableDescriptor(repositoryName, tableName), splitKeys);
verifyIsRecordTable(recordTable.getTableDescriptor());
return recordTable;
}
public static HTableInterface getRecordTable(HBaseTableFactory tableFactory, String repositoryName, String tableName, boolean clientMode) throws IOException, InterruptedException {
HTableInterface recordTable = tableFactory.getTable(createRecordTableDescriptor(repositoryName, tableName), !clientMode);
verifyIsRecordTable(recordTable.getTableDescriptor());
return recordTable;
}
private static void verifyIsRecordTable(HTableDescriptor htableDescriptor) {
if (!isRecordTableDescriptor(htableDescriptor)) {
throw new IllegalArgumentException(htableDescriptor.getNameAsString() + " is not a valid record table");
}
}
public static HTableInterface getTypeTable(HBaseTableFactory tableFactory) throws IOException, InterruptedException {
return tableFactory.getTable(typeTableDescriptor);
}
public static HTableInterface getBlobIncubatorTable(HBaseTableFactory tableFactory, boolean clientMode) throws IOException, InterruptedException {
return tableFactory.getTable(blobIncubatorDescriptor, !clientMode);
}
public static enum Table {
RECORD("record"),
TYPE("type"),
BLOBINCUBATOR("blobincubator");
public final byte[] bytes;
public final String name;
Table(String name) {
this.name = name;
this.bytes = Bytes.toBytes(name);
}
}
/**
* Column families in the record table.
*/
public static enum RecordCf {
DATA("data"); // The actual data fields and system fields of records are stored in the same column family
public final byte[] bytes;
public final String name;
RecordCf(String name) {
this.name = name;
this.bytes = Bytes.toBytes(name);
}
}
/**
* Columns in the record table.
*/
public static enum RecordColumn {
/** occ = optimistic concurrency control (a version counter) */
OCC("occ"),
VERSION("version"),
DELETED("deleted"),
NON_VERSIONED_RT_ID("nv-rt"),
NON_VERSIONED_RT_VERSION("nv-rtv"),
VERSIONED_RT_ID("v-rt"),
VERSIONED_RT_VERSION("v-rtv"),
VERSIONED_MUTABLE_RT_ID("vm-rt"),
VERSIONED_MUTABLE_RT_VERSION("vm-rtv"),
/** payload for the event dispatcher */
PAYLOAD("pl");
public final byte[] bytes;
public final String name;
// The fields and system fields of records are stored in the same column family : DATA
public static final byte SYSTEM_PREFIX = (byte)1; // Prefix for the column-qualifiers of system fields
public static final byte DATA_PREFIX = (byte)2; // Prefix for the column-qualifiers of actual data fields
RecordColumn(String name) {
this.name = name;
this.bytes = Bytes.add(new byte[]{SYSTEM_PREFIX},Bytes.toBytes(name));
}
}
/**
* Column families in the type table.
*/
public static enum TypeCf {
DATA("data"),
FIELDTYPE_ENTRY("fieldtype-entry"),
SUPERTYPE("mixin") /* actual CF name is 'mixin' for backwards compatibility */;
public final byte[] bytes;
public final String name;
TypeCf(String name) {
this.name = name;
this.bytes = Bytes.toBytes(name);
}
}
/**
* Columns in the type table.
*/
public static enum TypeColumn {
VERSION("version"),
RECORDTYPE_NAME("rt"),
FIELDTYPE_NAME("ft"),
FIELDTYPE_VALUETYPE("vt"),
FIELDTYPE_SCOPE("scope"),
CONCURRENT_COUNTER("cc"),
CONCURRENT_TIMESTAMP("cts");
public final byte[] bytes;
public final String name;
TypeColumn(String name) {
this.name = name;
this.bytes = Bytes.toBytes(name);
}
}
/**
* Column families in the blob incubator table.
*/
public static enum BlobIncubatorCf {
REF("ref");
public final byte[] bytes;
public final String name;
BlobIncubatorCf(String name) {
this.name = name;
this.bytes = Bytes.toBytes(name);
}
}
/**
* Columns in the blob incubator table.
*/
public static enum BlobIncubatorColumn {
RECORD("record"), FIELD("field");
public final byte[] bytes;
public final String name;
BlobIncubatorColumn(String name) {
this.name = name;
this.bytes = Bytes.toBytes(name);
}
}
}