use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class TreeRule method fetchRule.
/**
* Attempts to retrieve the specified tree rule from storage.
* @param tsdb The TSDB to use for storage access
* @param tree_id ID of the tree the rule belongs to
* @param level Level where the rule resides
* @param order Order where the rule resides
* @return A TreeRule object if found, null if it does not exist
* @throws HBaseException if there was an issue
* @throws IllegalArgumentException if the one of the required parameters was
* missing
* @throws JSONException if the object could not be serialized
*/
public static Deferred<TreeRule> fetchRule(final TSDB tsdb, final int tree_id, final int level, final int order) {
if (tree_id < 1 || tree_id > 65535) {
throw new IllegalArgumentException("Invalid Tree ID");
}
if (level < 0) {
throw new IllegalArgumentException("Invalid rule level");
}
if (order < 0) {
throw new IllegalArgumentException("Invalid rule order");
}
// fetch the whole row
final GetRequest get = new GetRequest(tsdb.treeTable(), Tree.idToBytes(tree_id));
get.family(Tree.TREE_FAMILY());
get.qualifier(getQualifier(level, order));
/**
* Called after fetching to parse the results
*/
final class FetchCB implements Callback<Deferred<TreeRule>, ArrayList<KeyValue>> {
@Override
public Deferred<TreeRule> call(final ArrayList<KeyValue> row) {
if (row == null || row.isEmpty()) {
return Deferred.fromResult(null);
}
return Deferred.fromResult(parseFromStorage(row.get(0)));
}
}
return tsdb.getClient().get(get).addCallbackDeferring(new FetchCB());
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class TreeRule method deleteAllRules.
/**
* Attempts to delete all rules belonging to the given tree.
* @param tsdb The TSDB to use for storage access
* @param tree_id ID of the tree the rules belongs to
* @return A deferred to wait on for completion. The value has no meaning and
* may be null.
* @throws HBaseException if there was an issue
* @throws IllegalArgumentException if the one of the required parameters was
* missing
*/
public static Deferred<Object> deleteAllRules(final TSDB tsdb, final int tree_id) {
if (tree_id < 1 || tree_id > 65535) {
throw new IllegalArgumentException("Invalid Tree ID");
}
// fetch the whole row
final GetRequest get = new GetRequest(tsdb.treeTable(), Tree.idToBytes(tree_id));
get.family(Tree.TREE_FAMILY());
/**
* Called after fetching the requested row. If the row is empty, we just
* return, otherwise we compile a list of qualifiers to delete and submit
* a single delete request to storage.
*/
final class GetCB implements Callback<Deferred<Object>, ArrayList<KeyValue>> {
@Override
public Deferred<Object> call(final ArrayList<KeyValue> row) throws Exception {
if (row == null || row.isEmpty()) {
return Deferred.fromResult(null);
}
final ArrayList<byte[]> qualifiers = new ArrayList<byte[]>(row.size());
for (KeyValue column : row) {
if (column.qualifier().length > RULE_PREFIX.length && Bytes.memcmp(RULE_PREFIX, column.qualifier(), 0, RULE_PREFIX.length) == 0) {
qualifiers.add(column.qualifier());
}
}
final DeleteRequest delete = new DeleteRequest(tsdb.treeTable(), Tree.idToBytes(tree_id), Tree.TREE_FAMILY(), qualifiers.toArray(new byte[qualifiers.size()][]));
return tsdb.getClient().delete(delete);
}
}
return tsdb.getClient().get(get).addCallbackDeferring(new GetCB());
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class TreeRule method syncToStorage.
/**
* Attempts to write the rule to storage via CompareAndSet, merging changes
* with an existing rule.
* <b>Note:</b> If the local object didn't have any fields set by the caller
* or there weren't any changes, then the data will not be written and an
* exception will be thrown.
* <b>Note:</b> This method also validates the rule, making sure that proper
* combinations of data exist before writing to storage.
* @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 CAS call succeeded, false if the stored data was
* modified in flight. This should be retried if that happens.
* @throws HBaseException if there was an issue
* @throws IllegalArgumentException if parsing failed or the tree ID was
* invalid or validation failed
* @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 (tree_id < 1 || tree_id > 65535) {
throw new IllegalArgumentException("Invalid Tree ID");
}
// if there aren't any changes, save time and bandwidth by not writing to
// storage
boolean has_changes = false;
for (Map.Entry<String, Boolean> entry : changed.entrySet()) {
if (entry.getValue()) {
has_changes = true;
break;
}
}
if (!has_changes) {
LOG.trace(this + " does not have changes, skipping sync to storage");
throw new IllegalStateException("No changes detected in the rule");
}
/**
* Executes the CAS after retrieving existing rule from storage, if it
* exists.
*/
final class StoreCB implements Callback<Deferred<Boolean>, TreeRule> {
final TreeRule local_rule;
public StoreCB(final TreeRule local_rule) {
this.local_rule = local_rule;
}
/**
* @return True if the CAS was successful, false if not
*/
@Override
public Deferred<Boolean> call(final TreeRule fetched_rule) {
TreeRule stored_rule = fetched_rule;
final byte[] original_rule = stored_rule == null ? new byte[0] : JSON.serializeToBytes(stored_rule);
if (stored_rule == null) {
stored_rule = local_rule;
} else {
if (!stored_rule.copyChanges(local_rule, overwrite)) {
LOG.debug(this + " does not have changes, skipping sync to storage");
throw new IllegalStateException("No changes detected in the rule");
}
}
// reset the local change map so we don't keep writing on subsequent
// requests
initializeChangedMap();
// validate before storing
stored_rule.validateRule();
final PutRequest put = new PutRequest(tsdb.treeTable(), Tree.idToBytes(tree_id), Tree.TREE_FAMILY(), getQualifier(level, order), JSON.serializeToBytes(stored_rule));
return tsdb.getClient().compareAndSet(put, original_rule);
}
}
// start the callback chain by fetching from storage
return fetchRule(tsdb, tree_id, level, order).addCallbackDeferring(new StoreCB(this));
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class AnnotationRpc method executeBulkUpdate.
/**
* Performs CRU methods on a list of annotation objects to reduce calls to
* the API. Only supports body content and adding or updating annotation
* objects. Deletions are separate.
* @param tsdb The TSD to which we belong
* @param method The request method
* @param query The query to parse and respond to
*/
void executeBulkUpdate(final TSDB tsdb, final HttpMethod method, HttpQuery query) {
final List<Annotation> notes;
try {
notes = query.serializer().parseAnnotationsV1();
} catch (IllegalArgumentException e) {
throw new BadRequestException(e);
} catch (JSONException e) {
throw new BadRequestException(e);
}
final List<Deferred<Annotation>> callbacks = new ArrayList<Deferred<Annotation>>(notes.size());
/**
* Storage callback used to determine if the storage call was successful
* or not. Also returns the updated object from storage.
*/
class SyncCB implements Callback<Deferred<Annotation>, Boolean> {
private final Annotation note;
public SyncCB(final Annotation note) {
this.note = note;
}
@Override
public Deferred<Annotation> call(Boolean success) throws Exception {
if (!success) {
throw new BadRequestException(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Failed to save an Annotation to storage", "This may be caused by another process modifying storage data: " + note);
}
return Annotation.getAnnotation(tsdb, note.getTSUID(), note.getStartTime());
}
}
/**
* Simple callback that will index the updated annotation
*/
class IndexCB implements Callback<Deferred<Annotation>, Annotation> {
@Override
public Deferred<Annotation> call(final Annotation note) throws Exception {
tsdb.indexAnnotation(note);
return Deferred.fromResult(note);
}
}
for (Annotation note : notes) {
try {
Deferred<Annotation> deferred = note.syncToStorage(tsdb, method == HttpMethod.PUT).addCallbackDeferring(new SyncCB(note));
Deferred<Annotation> indexer = deferred.addCallbackDeferring(new IndexCB());
callbacks.add(indexer);
} catch (IllegalStateException e) {
LOG.info("No changes for annotation: " + note);
} catch (IllegalArgumentException e) {
throw new BadRequestException(HttpResponseStatus.BAD_REQUEST, e.getMessage(), "Annotation error: " + note, e);
}
}
try {
// wait untill all of the syncs have completed, then rebuild the list
// of annotations using the data synced from storage.
Deferred.group(callbacks).joinUninterruptibly();
notes.clear();
for (Deferred<Annotation> note : callbacks) {
notes.add(note.joinUninterruptibly());
}
query.sendReply(query.serializer().formatAnnotationsV1(notes));
} catch (IllegalArgumentException e) {
throw new BadRequestException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class TimeSeriesLookup method lookupAsync.
/**
* Lookup time series associated with the given metric, tagk, tagv or tag
* pairs. Either the meta table or the data table will be scanned. If no
* metric is given, a full table scan must be performed and this call may take
* a long time to complete.
* When dumping to stdout, if an ID can't be looked up, it will be logged and
* skipped.
* @return A list of TSUIDs matching the given lookup query.
* @throws NoSuchUniqueName if any of the given names fail to resolve to a
* UID.
* @since 2.2
*/
public Deferred<List<byte[]>> lookupAsync() {
final Pattern tagv_regex = tagv_filter != null ? Pattern.compile(tagv_filter) : null;
// we don't really know what size the UIDs will resolve to so just grab
// a decent amount.
final StringBuffer buf = to_stdout ? new StringBuffer(2048) : null;
final long start = System.currentTimeMillis();
final int limit;
if (query.getLimit() > 0) {
if (query.useMeta() || Const.SALT_WIDTH() < 1) {
limit = query.getLimit();
} else if (query.getLimit() < Const.SALT_BUCKETS()) {
limit = 1;
} else {
limit = query.getLimit() / Const.SALT_BUCKETS();
}
} else {
limit = 0;
}
class ScannerCB implements Callback<Deferred<List<byte[]>>, ArrayList<ArrayList<KeyValue>>> {
private final Scanner scanner;
// used to avoid dupes when scanning the data table
private byte[] last_tsuid = null;
private int rows_read;
ScannerCB(final Scanner scanner) {
this.scanner = scanner;
}
Deferred<List<byte[]>> scan() {
return scanner.nextRows().addCallbackDeferring(this);
}
@Override
public Deferred<List<byte[]>> call(final ArrayList<ArrayList<KeyValue>> rows) throws Exception {
if (rows == null) {
scanner.close();
if (query.useMeta() || Const.SALT_WIDTH() < 1) {
LOG.debug("Lookup query matched " + tsuids.size() + " time series in " + (System.currentTimeMillis() - start) + " ms");
}
return Deferred.fromResult(tsuids);
}
for (final ArrayList<KeyValue> row : rows) {
if (limit > 0 && rows_read >= limit) {
// little recursion to close the scanner and log above.
return call(null);
}
final byte[] tsuid = query.useMeta() ? row.get(0).key() : UniqueId.getTSUIDFromKey(row.get(0).key(), TSDB.metrics_width(), Const.TIMESTAMP_BYTES);
// string objects.
if (tagv_regex != null && !tagv_regex.matcher(new String(tsuid, CHARSET)).find()) {
continue;
}
if (to_stdout) {
if (last_tsuid != null && Bytes.memcmp(last_tsuid, tsuid) == 0) {
continue;
}
last_tsuid = tsuid;
try {
buf.append(UniqueId.uidToString(tsuid)).append(" ");
buf.append(RowKey.metricNameAsync(tsdb, tsuid).joinUninterruptibly());
buf.append(" ");
final List<byte[]> tag_ids = UniqueId.getTagPairsFromTSUID(tsuid);
final Map<String, String> resolved_tags = Tags.resolveIdsAsync(tsdb, tag_ids).joinUninterruptibly();
for (final Map.Entry<String, String> tag_pair : resolved_tags.entrySet()) {
buf.append(tag_pair.getKey()).append("=").append(tag_pair.getValue()).append(" ");
}
} catch (NoSuchUniqueId nsui) {
LOG.error("Unable to resolve UID in TSUID (" + UniqueId.uidToString(tsuid) + ") " + nsui.getMessage());
}
// reset the buffer so we can re-use it
buf.setLength(0);
} else {
tsuids.add(tsuid);
}
++rows_read;
}
return scan();
}
@Override
public String toString() {
return "Scanner callback";
}
}
class CompleteCB implements Callback<List<byte[]>, ArrayList<List<byte[]>>> {
@Override
public List<byte[]> call(final ArrayList<List<byte[]>> unused) throws Exception {
LOG.debug("Lookup query matched " + tsuids.size() + " time series in " + (System.currentTimeMillis() - start) + " ms");
return tsuids;
}
@Override
public String toString() {
return "Final async lookup callback";
}
}
class UIDCB implements Callback<Deferred<List<byte[]>>, Object> {
@Override
public Deferred<List<byte[]>> call(Object arg0) throws Exception {
if (!query.useMeta() && Const.SALT_WIDTH() > 0 && metric_uid != null) {
final ArrayList<Deferred<List<byte[]>>> deferreds = new ArrayList<Deferred<List<byte[]>>>(Const.SALT_BUCKETS());
for (int i = 0; i < Const.SALT_BUCKETS(); i++) {
deferreds.add(new ScannerCB(getScanner(i)).scan());
}
return Deferred.group(deferreds).addCallback(new CompleteCB());
} else {
return new ScannerCB(getScanner(0)).scan();
}
}
@Override
public String toString() {
return "UID resolution callback";
}
}
return resolveUIDs().addCallbackDeferring(new UIDCB());
}
Aggregations