use of org.hbase.async.HBaseException in project opentsdb by OpenTSDB.
the class UIDMeta method syncToStorage.
/**
* Attempts a CompareAndSet storage call, loading the object from storage,
* synchronizing changes, and attempting a put.
* <b>Note:</b> If the local object didn't have any fields set by the caller
* then the data will not be written.
* @param tsdb The TSDB to use for storage access
* @param overwrite When the RPC method is PUT, will overwrite all user
* accessible fields
* @return True if the storage call was successful, false if the object was
* modified in storage during the CAS call. If false, retry the call. Other
* failures will result in an exception being thrown.
* @throws HBaseException if there was an issue fetching
* @throws IllegalArgumentException if parsing failed
* @throws NoSuchUniqueId If the UID does not exist
* @throws IllegalStateException if the data hasn't changed. This is OK!
* @throws JSONException if the object could not be serialized
*/
public Deferred<Boolean> syncToStorage(final TSDB tsdb, final boolean overwrite) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException("Missing UID");
}
if (type == null) {
throw new IllegalArgumentException("Missing type");
}
boolean has_changes = false;
for (Map.Entry<String, Boolean> entry : changed.entrySet()) {
if (entry.getValue()) {
has_changes = true;
break;
}
}
if (!has_changes) {
LOG.debug(this + " does not have changes, skipping sync to storage");
throw new IllegalStateException("No changes detected in UID meta data");
}
/**
* Callback used to verify that the UID to name mapping exists. Uses the TSD
* for verification so the name may be cached. If the name does not exist
* it will throw a NoSuchUniqueId and the meta data will not be saved to
* storage
*/
final class NameCB implements Callback<Deferred<Boolean>, String> {
private final UIDMeta local_meta;
public NameCB(final UIDMeta meta) {
local_meta = meta;
}
/**
* Nested callback used to merge and store the meta data after verifying
* that the UID mapping exists. It has to access the {@code local_meta}
* object so that's why it's nested within the NameCB class
*/
final class StoreUIDMeta implements Callback<Deferred<Boolean>, ArrayList<KeyValue>> {
/**
* Executes the CompareAndSet after merging changes
* @return True if the CAS was successful, false if the stored data
* was modified during flight.
*/
@Override
public Deferred<Boolean> call(final ArrayList<KeyValue> row) throws Exception {
final UIDMeta stored_meta;
if (row == null || row.isEmpty()) {
stored_meta = null;
} else {
stored_meta = JSON.parseToObject(row.get(0).value(), UIDMeta.class);
stored_meta.initializeChangedMap();
}
final byte[] original_meta = row == null || row.isEmpty() ? new byte[0] : row.get(0).value();
if (stored_meta != null) {
local_meta.syncMeta(stored_meta, overwrite);
}
// verify the name is set locally just to be safe
if (name == null || name.isEmpty()) {
local_meta.name = name;
}
final PutRequest put = new PutRequest(tsdb.uidTable(), UniqueId.stringToUid(uid), FAMILY, (type.toString().toLowerCase() + "_meta").getBytes(CHARSET), local_meta.getStorageJSON());
return tsdb.getClient().compareAndSet(put, original_meta);
}
}
/**
* NameCB method that fetches the object from storage for merging and
* use in the CAS call
* @return The results of the {@link #StoreUIDMeta} callback
*/
@Override
public Deferred<Boolean> call(final String name) throws Exception {
final GetRequest get = new GetRequest(tsdb.uidTable(), UniqueId.stringToUid(uid));
get.family(FAMILY);
get.qualifier((type.toString().toLowerCase() + "_meta").getBytes(CHARSET));
// #2 deferred
return tsdb.getClient().get(get).addCallbackDeferring(new StoreUIDMeta());
}
}
// start the callback chain by veryfing that the UID name mapping exists
return tsdb.getUidName(type, UniqueId.stringToUid(uid)).addCallbackDeferring(new NameCB(this));
}
use of org.hbase.async.HBaseException in project opentsdb by OpenTSDB.
the class QueryRpc method handleQuery.
/**
* Processing for a data point query
* @param tsdb The TSDB to which we belong
* @param query The HTTP query to parse/respond
* @param allow_expressions Whether or not expressions should be parsed
* (based on the endpoint)
*/
private void handleQuery(final TSDB tsdb, final HttpQuery query, final boolean allow_expressions) {
final long start = DateTime.currentTimeMillis();
final TSQuery data_query;
final List<ExpressionTree> expressions;
if (query.method() == HttpMethod.POST) {
switch(query.apiVersion()) {
case 0:
case 1:
data_query = query.serializer().parseQueryV1();
break;
default:
query_invalid.incrementAndGet();
throw new BadRequestException(HttpResponseStatus.NOT_IMPLEMENTED, "Requested API version not implemented", "Version " + query.apiVersion() + " is not implemented");
}
expressions = null;
} else {
expressions = new ArrayList<ExpressionTree>();
data_query = parseQuery(tsdb, query, expressions);
}
if (query.getAPIMethod() == HttpMethod.DELETE && tsdb.getConfig().getBoolean("tsd.http.query.allow_delete")) {
data_query.setDelete(true);
}
// validate and then compile the queries
try {
LOG.debug(data_query.toString());
data_query.validateAndSetQuery();
} catch (Exception e) {
throw new BadRequestException(HttpResponseStatus.BAD_REQUEST, e.getMessage(), data_query.toString(), e);
}
// if the user tried this query multiple times from the same IP and src port
// they'll be rejected on subsequent calls
final QueryStats query_stats = new QueryStats(query.getRemoteAddress(), data_query, query.getPrintableHeaders());
data_query.setQueryStats(query_stats);
query.setStats(query_stats);
final int nqueries = data_query.getQueries().size();
final ArrayList<DataPoints[]> results = new ArrayList<DataPoints[]>(nqueries);
final List<Annotation> globals = new ArrayList<Annotation>();
/** This has to be attached to callbacks or we may never respond to clients */
class ErrorCB implements Callback<Object, Exception> {
public Object call(final Exception e) throws Exception {
Throwable ex = e;
try {
LOG.error("Query exception: ", e);
if (ex instanceof DeferredGroupException) {
ex = e.getCause();
while (ex != null && ex instanceof DeferredGroupException) {
ex = ex.getCause();
}
if (ex == null) {
LOG.error("The deferred group exception didn't have a cause???");
}
}
if (ex instanceof RpcTimedOutException) {
query_stats.markSerialized(HttpResponseStatus.REQUEST_TIMEOUT, ex);
query.badRequest(new BadRequestException(HttpResponseStatus.REQUEST_TIMEOUT, ex.getMessage()));
query_exceptions.incrementAndGet();
} else if (ex instanceof HBaseException) {
query_stats.markSerialized(HttpResponseStatus.FAILED_DEPENDENCY, ex);
query.badRequest(new BadRequestException(HttpResponseStatus.FAILED_DEPENDENCY, ex.getMessage()));
query_exceptions.incrementAndGet();
} else if (ex instanceof QueryException) {
query_stats.markSerialized(((QueryException) ex).getStatus(), ex);
query.badRequest(new BadRequestException(((QueryException) ex).getStatus(), ex.getMessage()));
query_exceptions.incrementAndGet();
} else if (ex instanceof BadRequestException) {
query_stats.markSerialized(((BadRequestException) ex).getStatus(), ex);
query.badRequest((BadRequestException) ex);
query_invalid.incrementAndGet();
} else if (ex instanceof NoSuchUniqueName) {
query_stats.markSerialized(HttpResponseStatus.BAD_REQUEST, ex);
query.badRequest(new BadRequestException(ex));
query_invalid.incrementAndGet();
} else {
query_stats.markSerialized(HttpResponseStatus.INTERNAL_SERVER_ERROR, ex);
query.badRequest(new BadRequestException(ex));
query_exceptions.incrementAndGet();
}
} catch (RuntimeException ex2) {
LOG.error("Exception thrown during exception handling", ex2);
query_stats.markSerialized(HttpResponseStatus.INTERNAL_SERVER_ERROR, ex2);
query.sendReply(HttpResponseStatus.INTERNAL_SERVER_ERROR, ex2.getMessage().getBytes());
query_exceptions.incrementAndGet();
}
return null;
}
}
/**
* After all of the queries have run, we get the results in the order given
* and add dump the results in an array
*/
class QueriesCB implements Callback<Object, ArrayList<DataPoints[]>> {
public Object call(final ArrayList<DataPoints[]> query_results) throws Exception {
if (allow_expressions) {
// process each of the expressions into a new list, then merge it
// with the original. This avoids possible recursion loops.
final List<DataPoints[]> expression_results = new ArrayList<DataPoints[]>(expressions.size());
// let exceptions bubble up
for (final ExpressionTree expression : expressions) {
expression_results.add(expression.evaluate(query_results));
}
results.addAll(expression_results);
} else {
results.addAll(query_results);
}
/** Simply returns the buffer once serialization is complete and logs it */
class SendIt implements Callback<Object, ChannelBuffer> {
public Object call(final ChannelBuffer buffer) throws Exception {
query.sendReply(buffer);
query_success.incrementAndGet();
return null;
}
}
switch(query.apiVersion()) {
case 0:
case 1:
query.serializer().formatQueryAsyncV1(data_query, results, globals).addCallback(new SendIt()).addErrback(new ErrorCB());
break;
default:
query_invalid.incrementAndGet();
throw new BadRequestException(HttpResponseStatus.NOT_IMPLEMENTED, "Requested API version not implemented", "Version " + query.apiVersion() + " is not implemented");
}
return null;
}
}
/**
* Callback executed after we have resolved the metric, tag names and tag
* values to their respective UIDs. This callback then runs the actual
* queries and fetches their results.
*/
class BuildCB implements Callback<Deferred<Object>, Query[]> {
@Override
public Deferred<Object> call(final Query[] queries) {
final ArrayList<Deferred<DataPoints[]>> deferreds = new ArrayList<Deferred<DataPoints[]>>(queries.length);
for (final Query query : queries) {
deferreds.add(query.runAsync());
}
return Deferred.groupInOrder(deferreds).addCallback(new QueriesCB());
}
}
/** Handles storing the global annotations after fetching them */
class GlobalCB implements Callback<Object, List<Annotation>> {
public Object call(final List<Annotation> annotations) throws Exception {
globals.addAll(annotations);
return data_query.buildQueriesAsync(tsdb).addCallback(new BuildCB());
}
}
// when complete
if (!data_query.getNoAnnotations() && data_query.getGlobalAnnotations()) {
Annotation.getGlobalAnnotations(tsdb, data_query.startTime() / 1000, data_query.endTime() / 1000).addCallback(new GlobalCB()).addErrback(new ErrorCB());
} else {
data_query.buildQueriesAsync(tsdb).addCallback(new BuildCB()).addErrback(new ErrorCB());
}
}
use of org.hbase.async.HBaseException in project opentsdb by OpenTSDB.
the class TestUniqueId method getOrCreateIdWithICVFailure.
// ICV throws an exception, we can't get an ID.
@Test
public void getOrCreateIdWithICVFailure() {
uid = new UniqueId(client, table, METRIC, 3);
final Config config = mock(Config.class);
when(config.enable_realtime_uid()).thenReturn(false);
final TSDB tsdb = mock(TSDB.class);
when(tsdb.getConfig()).thenReturn(config);
uid.setTSDB(tsdb);
// null => ID doesn't exist.
when(client.get(anyGet())).thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(null));
// Watch this! ______,^ I'm writing C++ in Java!
// Update once HBASE-2292 is fixed:
HBaseException hbe = fakeHBaseException();
when(client.atomicIncrement(incrementForRow(MAXID))).thenReturn(Deferred.<Long>fromError(hbe)).thenReturn(Deferred.fromResult(5L));
when(client.compareAndSet(anyPut(), emptyArray())).thenReturn(Deferred.fromResult(true)).thenReturn(Deferred.fromResult(true));
final byte[] id = { 0, 0, 5 };
assertArrayEquals(id, uid.getOrCreateId("foo"));
// Initial Get.
verify(client, times(1)).get(anyGet());
// First increment (failed) + retry.
verify(client, times(2)).atomicIncrement(incrementForRow(MAXID));
// Reverse + forward mappings.
verify(client, times(2)).compareAndSet(anyPut(), emptyArray());
}
use of org.hbase.async.HBaseException in project opentsdb by OpenTSDB.
the class TestUniqueId method fakeHBaseException.
private static HBaseException fakeHBaseException() {
final HBaseException hbe = mock(HBaseException.class);
when(hbe.getStackTrace()).thenReturn(Arrays.copyOf(new RuntimeException().getStackTrace(), 3));
when(hbe.getMessage()).thenReturn("fake exception");
return hbe;
}
use of org.hbase.async.HBaseException in project opentsdb by OpenTSDB.
the class TestUniqueId method getNameWithErrorDuringHBaseLookup.
@Test
public void getNameWithErrorDuringHBaseLookup() {
uid = new UniqueId(client, table, METRIC, 3);
final byte[] id = { 0, 'a', 0x42 };
final byte[] byte_name = { 'f', 'o', 'o' };
HBaseException hbe = mock(HBaseException.class);
ArrayList<KeyValue> kvs = new ArrayList<KeyValue>(1);
kvs.add(new KeyValue(id, ID, METRIC_ARRAY, byte_name));
when(client.get(anyGet())).thenThrow(hbe).thenReturn(Deferred.fromResult(kvs));
// 1st calls fails.
try {
uid.getName(id);
fail("HBaseException should have been thrown.");
} catch (HBaseException e) {
// OK.
assertSame(hbe, e);
}
// 2nd call succeeds.
assertEquals("foo", uid.getName(id));
assertEquals(0, uid.cacheHits());
// 1st (failed) attempt + 2nd.
assertEquals(2, uid.cacheMisses());
assertEquals(2, uid.cacheSize());
verify(client, times(2)).get(anyGet());
}
Aggregations