sql >> データベース >  >> NoSQL >> HBase

ハウツー:一般的なツールを使用してHBaseアプリケーションをテストする

    エンドユーザーアプリケーションを構築するためのApacheHBaseの採用は急増していますが、それらのアプリケーションの多く(および一般的には多くのアプリ)は十分にテストされていません。この投稿では、このテストを簡単に実行できるいくつかの方法を学びます。

    JUnitを介した単体テストから始め、次にMockitoとApache MRUnitの使用に移り、統合テストにHBaseミニクラスターを使用します。 (HBaseコードベース自体はミニクラスターを介してテストされているので、アップストリームアプリケーションでもそれを利用してみませんか?)

    議論の基礎として、HBaseへの次の挿入を行うHBaseデータアクセスオブジェクト(DAO)があると仮定します。もちろん、ロジックはもっと複雑になる可能性がありますが、例として、これでうまくいきます。

    public class MyHBaseDAO {
    
            	public static void insertRecord(HTableInterface table, HBaseTestObj obj)
           	throws Exception {
      	              	Put put = createPut(obj);
      	              	table.put(put);
            	}
    
            	private static Put createPut(HBaseTestObj obj) {
      	              	Put put = new Put(Bytes.toBytes(obj.getRowKey()));
      	              	put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1"),
                 	               	Bytes.toBytes(obj.getData1()));
      	              	put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2"),
                 	               	Bytes.toBytes(obj.getData2()));
      	              	return put;
            	}
    }
    

    HBaseTestObjは、rowkey、data1、およびdata2のゲッターとセッターを備えた基本的なデータオブジェクトです。

    insertRecordは、CQ-1とCQ-2を修飾子として、CFの列ファミリーに対してHBaseテーブルに挿入します。 createPutメソッドは、Putにデータを入力し、それを呼び出し元のメソッドに返すだけです。

    JUnitの使用

    この時点でほとんどのJava開発者によく知られているJUnitは、多くのHBaseアプリケーションに簡単に適用できます。まず、pomに依存関係を追加します:

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    

    さて、テストクラス内:

    public class TestMyHbaseDAOData {
              	@Test
              	public void testCreatePut() throws Exception {
        	  	HBaseTestObj obj = new HBaseTestObj();
        	  	obj.setRowKey("ROWKEY-1");
        	  	obj.setData1("DATA-1");
        	  	obj.setData2("DATA-2");
        	  	Put put = MyHBaseDAO.createPut(obj);
        	  	assertEquals(obj.getRowKey(), Bytes.toString(put.getRow()));
        	  	assertEquals(obj.getData1(), Bytes.toString(put.get(Bytes.toBytes("CF"),
      Bytes.toBytes("CQ-1")).get(0).getValue()));
        	  	assertEquals(obj.getData2(), Bytes.toString(put.get(Bytes.toBytes("CF"),
      Bytes.toBytes("CQ-2")).get(0).getValue()));
              	}
      }
    

    ここで行ったことは、createPutメソッドが期待値を含むPutオブジェクトを作成、入力、および返すことを確認することでした。

    Mockitoの使用

    では、上記のinsertRecordメソッドの単体テストをどのように行いますか?非常に効果的なアプローチの1つは、Mockitoを使用することです。

    まず、pomへの依存関係としてMockitoを追加します:

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.9.5</version>
        <scope>test</scope>
    </dependency>
    

    次に、テストクラスで:

    @RunWith(MockitoJUnitRunner.class)
    public class TestMyHBaseDAO{
      @Mock 
      private HTableInterface table;
      @Mock
      private HTablePool hTablePool;
      @Captor
      private ArgumentCaptor putCaptor;
    
      @Test
      public void testInsertRecord() throws Exception {
        //return mock table when getTable is called
        when(hTablePool.getTable("tablename")).thenReturn(table);
        //create test object and make a call to the DAO that needs testing
        HBaseTestObj obj = new HBaseTestObj();
        obj.setRowKey("ROWKEY-1");
        obj.setData1("DATA-1");
        obj.setData2("DATA-2");
        MyHBaseDAO.insertRecord(table, obj);
        verify(table).put(putCaptor.capture());
        Put put = putCaptor.getValue();
      
        assertEquals(Bytes.toString(put.getRow()), obj.getRowKey());
        assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1")));
        assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2")));
        assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),
    Bytes.toBytes("CQ-1")).get(0).getValue()), "DATA-1");
        assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),
    Bytes.toBytes("CQ-2")).get(0).getValue()), "DATA-2");
      }
    }
    

    ここでは、HBaseTestObjに「ROWKEY-1」、「DATA-1」、「DATA-2」を値として入力しました。次に、モックテーブルとDAOを使用してレコードを挿入しました。 DAOが挿入したであろうプットをキャプチャし、行キー、data1、およびdata2が期待どおりであることを確認しました。

    ここで重要なのは、DAOの外部でhtableプールとhtableインスタンスの作成を管理することです。これにより、それらをきれいにモックして、上記のようにPutをテストできます。同様に、Get、Scan、Deleteなどの他のすべての操作に拡張できるようになりました。

    MRUnitの使用

    定期的なデータアクセスユニットテストについて説明したので、HBaseテーブルに反するMapReduceジョブに目を向けましょう。

    HBaseに反するMRジョブのテストは、通常のMapReduceジョブのテストと同じくらい簡単です。 MRUnitを使用すると、HBaseジョブを含むMapReduceジョブを非常に簡単にテストできます。

    1つの列ファミリー「CF」を持つHBaseテーブル「MyTest」に書き込むMRジョブがあるとします。そのような仕事のレデューサーは次のようになります:

    public class MyReducer extends TableReducer<Text, Text, ImmutableBytesWritable> {
       public static final byte[] CF = "CF".getBytes();
       public static final byte[] QUALIFIER = "CQ-1".getBytes();
      public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
         //bunch of processing to extract data to be inserted, in our case, lets say we are simply
         //appending all the records we receive from the mapper for this particular
         //key and insert one record into HBase
         StringBuffer data = new StringBuffer();
         Put put = new Put(Bytes.toBytes(key.toString()));
         for (Text val : values) {
             data = data.append(val);
         }
         put.add(CF, QUALIFIER, Bytes.toBytes(data.toString()));
         //write to HBase
         context.write(new ImmutableBytesWritable(Bytes.toBytes(key.toString())), put);
       }
     }
    

    では、MRUnitで上記のレデューサーのユニットテストをどのように行いますか?まず、MRUnitを依存関係としてpomに追加します。

    <dependency>
       <groupId>org.apache.mrunit</groupId>
       <artifactId>mrunit</artifactId>
       <version>1.0.0 </version>
       <scope>test</scope>
    </dependency>
    

    次に、テストクラス内で、MRUnitが以下のように提供するReduceDriverを使用します。

    public class MyReducerTest {
        ReduceDriver<Text, Text, ImmutableBytesWritable, Writable> reduceDriver;
        byte[] CF = "CF".getBytes();
        byte[] QUALIFIER = "CQ-1".getBytes();
    
        @Before
        public void setUp() {
          MyReducer reducer = new MyReducer();
          reduceDriver = ReduceDriver.newReduceDriver(reducer);
        }
      
       @Test
       public void testHBaseInsert() throws IOException {
          String strKey = "RowKey-1", strValue = "DATA", strValue1 = "DATA1", 
    strValue2 = "DATA2";
          List<Text> list = new ArrayList<Text>();
          list.add(new Text(strValue));
          list.add(new Text(strValue1));
          list.add(new Text(strValue2));
          //since in our case all that the reducer is doing is appending the records that the mapper   
          //sends it, we should get the following back
          String expectedOutput = strValue + strValue1 + strValue2;
         //Setup Input, mimic what mapper would have passed
          //to the reducer and run test
          reduceDriver.withInput(new Text(strKey), list);
          //run the reducer and get its output
          List<Pair<ImmutableBytesWritable, Writable>> result = reduceDriver.run();
        
          //extract key from result and verify
          assertEquals(Bytes.toString(result.get(0).getFirst().get()), strKey);
        
          //extract value for CF/QUALIFIER and verify
          Put a = (Put)result.get(0).getSecond();
          String c = Bytes.toString(a.get(CF, QUALIFIER).get(0).getValue());
          assertEquals(expectedOutput,c );
       }
    
    }
    

    基本的に、MyReducerで一連の処理を行った後、次のことを確認しました。

    • 出力は期待どおりです。
    • HBaseに挿入されるPutには、行キーとして「RowKey-1」があります。
    • 「DATADATA1DATA2」は、CF列ファミリーとCQ列修飾子の値です。

    MapperDriverを使用して同様の方法でHBaseからデータを取得するMappersをテストしたり、HBaseから読み取り、データを処理し、HDFSに書き込むMRジョブをテストしたりすることもできます。

    HBaseミニクラスターの使用

    次に、統合テストを行う方法を見ていきます。 HBaseにはHBaseTestingUtilityが付属しているため、HBaseミニクラスターを使用した統合テストの記述が簡単になります。正しいライブラリを取得するには、pomに次の依存関係が必要です。

    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.0.0-cdh4.2.0</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.apache.hbase</groupId>
        <artifactId>hbase</artifactId>
        <version>0.94.2-cdh4.2.0</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
            
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.0.0-cdh4.2.0</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.0.0-cdh4.2.0</version>
        <scope>test</scope>
    </dependency>
    

    それでは、はじめに説明したMyDAOインサートの統合テストを実行する方法を見てみましょう。

    public class MyHBaseIntegrationTest {
    private static HBaseTestingUtility utility;
    byte[] CF = "CF".getBytes();
    byte[] QUALIFIER = "CQ-1".getBytes();
    
    @Before
    public void setup() throws Exception {
    	utility = new HBaseTestingUtility();
    	utility.startMiniCluster();
    }
    
    @Test
        public void testInsert() throws Exception {
       	 HTableInterface table = utility.createTable(Bytes.toBytes("MyTest"),
       			 Bytes.toBytes("CF"));
       	 HBaseTestObj obj = new HBaseTestObj();
       	 obj.setRowKey("ROWKEY-1");
       	 obj.setData1("DATA-1");
       	 obj.setData2("DATA-2");
       	 MyHBaseDAO.insertRecord(table, obj);
       	 Get get1 = new Get(Bytes.toBytes(obj.getRowKey()));
       	 get1.addColumn(CF, CQ1);
       	 Result result1 = table.get(get1);
       	 assertEquals(Bytes.toString(result1.getRow()), obj.getRowKey());
       	 assertEquals(Bytes.toString(result1.value()), obj.getData1());
       	 Get get2 = new Get(Bytes.toBytes(obj.getRowKey()));
       	 get2.addColumn(CF, CQ2);
       	 Result result2 = table.get(get2);
       	 assertEquals(Bytes.toString(result2.getRow()), obj.getRowKey());
       	 assertEquals(Bytes.toString(result2.value()), obj.getData2());
        }}
    

    ここでは、HBaseミニクラスターを作成して開始しました。次に、1つの列ファミリー「CF」を持つ「MyTest」というテーブルを作成しました。テストに必要なDAOを使用してレコードを挿入し、同じテーブルからGetを実行し、DAOがレコードを正しく挿入したことを確認しました。

    上記のようなMRジョブとともに、はるかに複雑なユースケースでも同じことができます。また、HBaseの作成中に作成されたHDFSおよびZooKeeperミニクラスターにアクセスし、MRジョブを実行し、それをHBaseに出力して、挿入されたレコードを確認することもできます。

    注意点として、ミニクラスターの起動には20〜30秒かかり、CygwinがないWindowsでは実行できません。ただし、定期的に実行する必要があるため、実行時間は長くてもかまいません。

    上記の例のサンプルコードは、https://github.com/sitaula/HBaseTestにあります。ハッピーテスト!


    1. CLIを使用してRedisキーをCSVとしてエクスポートする方法

    2. Railsアプリでmongoidを使用したMongoクエリにより、カーソルタイムアウトエラーが発生します

    3. Redistcp-バックログ

    4. MongoDBがシェルでデータベースを作成しない