/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.blur.jmeter; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.Random; import org.apache.blur.thirdparty.thrift_0_9_0.TException; import org.apache.blur.thirdparty.thrift_0_9_0.protocol.TBinaryProtocol; import org.apache.blur.thirdparty.thrift_0_9_0.protocol.TProtocol; import org.apache.blur.thirdparty.thrift_0_9_0.transport.TMemoryBuffer; import org.apache.blur.thrift.BlurClient; import org.apache.blur.thrift.generated.Blur.Iface; import org.apache.blur.thrift.generated.BlurQuery; import org.apache.blur.thrift.generated.BlurResult; import org.apache.blur.thrift.generated.BlurResults; import org.apache.blur.thrift.generated.Column; import org.apache.blur.thrift.generated.FetchRecordResult; import org.apache.blur.thrift.generated.FetchResult; import org.apache.blur.thrift.generated.FetchRowResult; import org.apache.blur.thrift.generated.Query; import org.apache.blur.thrift.generated.Record; import org.apache.blur.thrift.generated.Row; import org.apache.blur.thrift.generated.Selector; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; public class BlurSamplerClient extends AbstractJavaSamplerClient implements Serializable { private static final Log LOG = LogFactory.getLog(BlurSamplerClient.class); private static List<Entry<String, String>> _fieldValueCache = new ArrayList<>(); private static final long serialVersionUID = 1L; private String _table; private Iface _client; private int _fieldValueCacheSize = 10000; private ThreadLocal<Random> _random = new ThreadLocal<Random>() { @Override protected Random initialValue() { return new Random(); } }; @Override public Arguments getDefaultParameters() { Arguments arguments = new Arguments(); arguments.addArgument("ZooKeeperConnection", "localhost", "The ZooKeeper connection string to the Blur cluster you want to test."); arguments.addArgument("Table", "test"); return arguments; } @Override public void setupTest(JavaSamplerContext context) { String zkConnectionString = context.getParameter("ZooKeeperConnection"); _table = context.getParameter("Table"); _client = BlurClient.getClientFromZooKeeperConnectionStr(zkConnectionString); } @Override public SampleResult runTest(JavaSamplerContext context) { SampleResult sampleResult = new SampleResult(); BlurResults blurResults = null; try { BlurQuery blurQuery = getBlurQuery(); sampleResult.sampleStart(); blurResults = _client.query(_table, blurQuery); sampleResult.sampleEnd(); int size = getBytes(blurResults); sampleResult.setBytes(size); sampleResult.setSuccessful(true); sampleResult.setResponseOK(); } catch (Throwable t) { sampleResult.setResponseMessage("Exception " + t.getMessage()); sampleResult.setSuccessful(false); LOG.error("Unknown error.", t); } finally { processResults(blurResults); } return sampleResult; } private int getBytes(BlurResults blurResults) { TMemoryBuffer trans = new TMemoryBuffer(1024); TProtocol oprot = new TBinaryProtocol(trans); try { blurResults.write(oprot); } catch (TException t) { LOG.error("Unknown error.", t); } return trans.length(); } private BlurQuery getBlurQuery() { BlurQuery blurQuery = new BlurQuery(); blurQuery.setSelector(new Selector()); Query query = new Query(); query.setQuery(getQueryString()); blurQuery.setQuery(query); return blurQuery; } private String getQueryString() { Random random = getRandom(); int numberOfClauses = random.nextInt(9) + 1; StringBuilder builder = new StringBuilder(); int size = _fieldValueCache.size(); if (size == 0) { return "*"; } for (int i = 0; i < numberOfClauses; i++) { int index = random.nextInt(size); Entry<String, String> entry = _fieldValueCache.get(index); builder.append("<" + entry.getKey() + ":" + entry.getValue() + "> "); } String query = builder.toString(); // LOG.info("Query [" + query + "]"); return query; } private void processResults(BlurResults blurResults) { if (blurResults == null) { return; } for (BlurResult blurResult : blurResults.getResults()) { processResult(blurResult); } } private void processResult(BlurResult blurResult) { if (blurResult == null) { return; } processResult(blurResult.getFetchResult()); } private void processResult(FetchResult fetchResult) { if (fetchResult == null) { return; } processResult(fetchResult.getRowResult()); processResult(fetchResult.getRecordResult()); } private void processResult(FetchRecordResult recordResult) { if (recordResult == null) { return; } addRowId(recordResult.getRowid()); processResult(recordResult.getRecord()); } private void processResult(FetchRowResult rowResult) { if (rowResult == null) { return; } processResult(rowResult.getRow()); } private void processResult(Row row) { if (row == null) { return; } addRowId(row.getId()); processResult(row.getRecords()); } private void processResult(List<Record> records) { if (records == null) { return; } for (Record record : records) { processResult(record); } } private void addRowId(String id) { addFieldToQuery("rowid", id); } private void processResult(Record record) { if (record == null) { return; } addRecordId(record.getRecordId()); List<Column> columns = record.getColumns(); if (columns != null) { for (Column column : columns) { addFieldToQuery(record.getFamily() + "." + column.getName(), column.getValue()); } } } private void addRecordId(String id) { addFieldToQuery("recordid", id); } private void addFieldToQuery(String fieldName, String value) { if (StringUtils.isEmpty(fieldName) || StringUtils.isEmpty(value)) { return; } Entry<String, String> e = toEntry(fieldName, value); // LOG.info("Adding [" + fieldName + "] [" + value + "]"); synchronized (_fieldValueCache) { if (_fieldValueCache.size() >= _fieldValueCacheSize) { Random random = getRandom(); int index = random.nextInt(_fieldValueCache.size()); _fieldValueCache.set(index, e); } else { _fieldValueCache.add(e); } } } private Random getRandom() { return _random.get(); } private static Entry<String, String> toEntry(String fieldName, String value) { return new Entry<String, String>() { @Override public String setValue(String value) { throw new RuntimeException("Not implemented."); } @Override public String getValue() { return value; } @Override public String getKey() { return fieldName; } }; } }