/* * Copyright 2010 Outerthought bvba * * 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.repository.impl.test; import java.io.IOException; import java.util.UUID; import org.apache.hadoop.hbase.client.HTableInterface; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.util.Bytes; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.lilyproject.hadooptestfw.HBaseProxy; import org.lilyproject.hadooptestfw.TestHelper; import org.lilyproject.repository.api.FieldType; import org.lilyproject.repository.api.FieldTypeExistsException; import org.lilyproject.repository.api.FieldTypeNotFoundException; import org.lilyproject.repository.api.QName; import org.lilyproject.repository.api.RecordType; import org.lilyproject.repository.api.RecordTypeExistsException; import org.lilyproject.repository.api.RecordTypeNotFoundException; import org.lilyproject.repository.api.SchemaId; import org.lilyproject.repository.api.Scope; import org.lilyproject.repository.api.TypeException; import org.lilyproject.repository.api.TypeManager; import org.lilyproject.repository.api.ValueType; import org.lilyproject.repository.impl.HBaseTypeManager; import org.lilyproject.repository.impl.id.IdGeneratorImpl; import org.lilyproject.repository.impl.id.SchemaIdImpl; import org.lilyproject.util.hbase.HBaseTableFactory; import org.lilyproject.util.hbase.HBaseTableFactoryImpl; import org.lilyproject.util.hbase.LilyHBaseSchema; import org.lilyproject.util.hbase.LilyHBaseSchema.TypeCf; import org.lilyproject.util.hbase.LilyHBaseSchema.TypeColumn; import org.lilyproject.util.io.Closer; import org.lilyproject.util.zookeeper.ZkUtil; import org.lilyproject.util.zookeeper.ZooKeeperItf; import static org.junit.Assert.fail; public class TypeManagerReliableCreateTest { private static HBaseProxy HBASE_PROXY; private static final byte[] DATA_COLUMN_FAMILY = Bytes.toBytes("data"); private static final byte[] CONCURRENT_COUNTER_COLUMN_NAME = Bytes.toBytes("cc"); private static ValueType valueType; private static TypeManager typeManager; private static ZooKeeperItf zooKeeper; private static HBaseTableFactory hbaseTableFactory; private static HTableInterface typeTable; @BeforeClass public static void setUpBeforeClass() throws Exception { TestHelper.setupLogging(); HBASE_PROXY = new HBaseProxy(); HBASE_PROXY.start(); zooKeeper = ZkUtil.connect(HBASE_PROXY.getZkConnectString(), 10000); hbaseTableFactory = new HBaseTableFactoryImpl(HBASE_PROXY.getConf()); typeTable = LilyHBaseSchema.getTypeTable(hbaseTableFactory); typeManager = new HBaseTypeManager(new IdGeneratorImpl(), HBASE_PROXY.getConf(), zooKeeper, hbaseTableFactory); valueType = typeManager.getValueType("LONG"); } @AfterClass public static void tearDownAfterClass() throws Exception { Closer.close(typeManager); Closer.close(zooKeeper); HBASE_PROXY.stop(); } @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { HBASE_PROXY.cleanTables(); } @Test public void testConcurrentRecordCreate() throws Exception { QName qName = new QName("NS", "testConcurrentRecordCreate"); fakeConcurrentUpdate(qName); try { typeManager.createRecordType(typeManager.newRecordType(qName)); fail(); } catch (RecordTypeExistsException expected) { // This will be thrown when the cache of the typeManager was updated as a consequence of the update on basicTypeManager // Through ZooKeeper the cache will have been marked as invalidated } catch (TypeException expected) { } } @Test public void testConcurrentRecordUpdate() throws Exception { QName qName1 = new QName("NS", "testConcurrentRecordUpdate1"); QName qName2 = new QName("NS", "testConcurrentRecordUpdate2"); RecordType recordType = typeManager.createRecordType(typeManager.newRecordType(qName1)); // Fake concurrent update fakeConcurrentUpdate(qName2); recordType.setName(qName2); try { typeManager.updateRecordType(recordType); fail(); } catch (TypeException expected) { } } @Test public void testConcurrentFieldCreate() throws Exception { QName qName = new QName("NS", "testConcurrentFieldCreate"); fakeConcurrentUpdate(qName); try { typeManager.createFieldType(typeManager.newFieldType(valueType, qName, Scope.VERSIONED)); fail(); } catch (FieldTypeExistsException expected) { // This will be thrown when the cache of the typeManager was updated as a consequence of the update on basicTypeManager // Through ZooKeeper the cache will have been marked as invalidated } catch (TypeException expected) { } } @Test public void testConcurrentFieldUpdate() throws Exception { QName qName1 = new QName("NS", "testConcurrentFieldUpdate1"); QName qName2 = new QName("NS", "testConcurrentFieldUpdate2"); FieldType createdFieldType = typeManager.createFieldType(typeManager.newFieldType(valueType, qName1, Scope.VERSIONED)); fakeConcurrentUpdate(qName2); createdFieldType.setName(qName2); try { typeManager.updateFieldType(createdFieldType); fail(); } catch (TypeException expected) { } } private void fakeConcurrentUpdate(QName qName) throws IOException { byte[] nameKey = HBaseTypeManager.encodeName(qName); long now = System.currentTimeMillis(); Put put = new Put(nameKey); put.add(TypeCf.DATA.bytes, TypeColumn.CONCURRENT_TIMESTAMP.bytes, Bytes.toBytes(now + 6000)); typeTable.put(put); } @Test public void testGetTypeIgnoresConcurrentCounterRows() throws Exception { SchemaId id = new SchemaIdImpl(UUID.randomUUID()); byte[] rowId = id.getBytes(); typeTable.incrementColumnValue(rowId, DATA_COLUMN_FAMILY, CONCURRENT_COUNTER_COLUMN_NAME, 1); try { typeManager.getFieldTypeById(id); fail(); } catch (FieldTypeNotFoundException expected) { } try { typeManager.getRecordTypeById(id, null); fail(); } catch (RecordTypeNotFoundException expected) { } typeManager.close(); } }