use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.
the class TSDB method shutdown.
/**
* Gracefully shuts down this TSD instance.
* <p>
* The method must call {@code shutdown()} on all plugins as well as flush the
* compaction queue.
* @return A {@link Deferred} that will be called once all the un-committed
* data has been successfully and durably stored, and all resources used by
* this instance have been released. The value of the deferred object
* return is meaningless and unspecified, and can be {@code null}.
* @throws HBaseException (deferred) if there was a problem sending
* un-committed data to HBase. Please refer to the {@link HBaseException}
* hierarchy to handle the possible failures. Some of them are easily
* recoverable by retrying, some are not.
*/
public Deferred<Object> shutdown() {
final ArrayList<Deferred<Object>> deferreds = new ArrayList<Deferred<Object>>();
final class FinalShutdown implements Callback<Object, Object> {
@Override
public Object call(Object result) throws Exception {
if (result instanceof Exception) {
LOG.error("A previous shutdown failed", (Exception) result);
}
final Set<Timeout> timeouts = timer.stop();
// TODO - at some point we should clean these up.
if (timeouts.size() > 0) {
LOG.warn("There were " + timeouts.size() + " timer tasks queued");
}
LOG.info("Completed shutting down the TSDB");
return Deferred.fromResult(null);
}
}
final class SEHShutdown implements Callback<Object, Object> {
@Override
public Object call(Object result) throws Exception {
if (result instanceof Exception) {
LOG.error("Shutdown of the HBase client failed", (Exception) result);
}
LOG.info("Shutting down storage exception handler plugin: " + storage_exception_handler.getClass().getCanonicalName());
return storage_exception_handler.shutdown().addBoth(new FinalShutdown());
}
@Override
public String toString() {
return "SEHShutdown";
}
}
final class HClientShutdown implements Callback<Deferred<Object>, ArrayList<Object>> {
public Deferred<Object> call(final ArrayList<Object> args) {
if (storage_exception_handler != null) {
return client.shutdown().addBoth(new SEHShutdown());
}
return client.shutdown().addBoth(new FinalShutdown());
}
public String toString() {
return "shutdown HBase client";
}
}
final class ShutdownErrback implements Callback<Object, Exception> {
public Object call(final Exception e) {
final Logger LOG = LoggerFactory.getLogger(ShutdownErrback.class);
if (e instanceof DeferredGroupException) {
final DeferredGroupException ge = (DeferredGroupException) e;
for (final Object r : ge.results()) {
if (r instanceof Exception) {
LOG.error("Failed to shutdown the TSD", (Exception) r);
}
}
} else {
LOG.error("Failed to shutdown the TSD", e);
}
return new HClientShutdown().call(null);
}
public String toString() {
return "shutdown HBase client after error";
}
}
final class CompactCB implements Callback<Object, ArrayList<Object>> {
public Object call(ArrayList<Object> compactions) throws Exception {
return null;
}
}
if (config.enable_compactions()) {
LOG.info("Flushing compaction queue");
deferreds.add(compactionq.flush().addCallback(new CompactCB()));
}
if (startup != null) {
LOG.info("Shutting down startup plugin: " + startup.getClass().getCanonicalName());
deferreds.add(startup.shutdown());
}
if (search != null) {
LOG.info("Shutting down search plugin: " + search.getClass().getCanonicalName());
deferreds.add(search.shutdown());
}
if (rt_publisher != null) {
LOG.info("Shutting down RT plugin: " + rt_publisher.getClass().getCanonicalName());
deferreds.add(rt_publisher.shutdown());
}
if (meta_cache != null) {
LOG.info("Shutting down meta cache plugin: " + meta_cache.getClass().getCanonicalName());
deferreds.add(meta_cache.shutdown());
}
if (storage_exception_handler != null) {
LOG.info("Shutting down storage exception handler plugin: " + storage_exception_handler.getClass().getCanonicalName());
deferreds.add(storage_exception_handler.shutdown());
}
if (ts_filter != null) {
LOG.info("Shutting down time series filter plugin: " + ts_filter.getClass().getCanonicalName());
deferreds.add(ts_filter.shutdown());
}
if (uid_filter != null) {
LOG.info("Shutting down UID filter plugin: " + uid_filter.getClass().getCanonicalName());
deferreds.add(uid_filter.shutdown());
}
// wait for plugins to shutdown before we close the client
return deferreds.size() > 0 ? Deferred.group(deferreds).addCallbackDeferring(new HClientShutdown()).addErrback(new ShutdownErrback()) : new HClientShutdown().call(null);
}
use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.
the class TsdbQuery method findSpans.
/**
* Finds all the {@link Span}s that match this query.
* This is what actually scans the HBase table and loads the data into
* {@link Span}s.
* @return A map from HBase row key to the {@link Span} for that row key.
* Since a {@link Span} actually contains multiple HBase rows, the row key
* stored in the map has its timestamp zero'ed out.
* @throws HBaseException if there was a problem communicating with HBase to
* perform the search.
* @throws IllegalArgumentException if bad data was retrieved from HBase.
*/
private Deferred<TreeMap<byte[], Span>> findSpans() throws HBaseException {
final short metric_width = tsdb.metrics.width();
final // The key is a row key from HBase.
TreeMap<byte[], Span> spans = new TreeMap<byte[], Span>(new SpanCmp((short) (Const.SALT_WIDTH() + metric_width)));
// Copy only the filters that should trigger a tag resolution. If this list
// is empty due to literals or a wildcard star, then we'll save a TON of
// UID lookups
final List<TagVFilter> scanner_filters;
if (filters != null) {
scanner_filters = new ArrayList<TagVFilter>(filters.size());
for (final TagVFilter filter : filters) {
if (filter.postScan()) {
scanner_filters.add(filter);
}
}
} else {
scanner_filters = null;
}
if (Const.SALT_WIDTH() > 0) {
final List<Scanner> scanners = new ArrayList<Scanner>(Const.SALT_BUCKETS());
for (int i = 0; i < Const.SALT_BUCKETS(); i++) {
scanners.add(getScanner(i));
}
scan_start_time = DateTime.nanoTime();
return new SaltScanner(tsdb, metric, scanners, spans, scanner_filters, delete, query_stats, query_index).scan();
}
scan_start_time = DateTime.nanoTime();
final Scanner scanner = getScanner();
if (query_stats != null) {
query_stats.addScannerId(query_index, 0, scanner.toString());
}
final Deferred<TreeMap<byte[], Span>> results = new Deferred<TreeMap<byte[], Span>>();
/**
* Scanner callback executed recursively each time we get a set of data
* from storage. This is responsible for determining what columns are
* returned and issuing requests to load leaf objects.
* When the scanner returns a null set of rows, the method initiates the
* final callback.
*/
final class ScannerCB implements Callback<Object, ArrayList<ArrayList<KeyValue>>> {
int nrows = 0;
boolean seenAnnotation = false;
long scanner_start = DateTime.nanoTime();
long timeout = tsdb.getConfig().getLong("tsd.query.timeout");
private final Set<String> skips = new HashSet<String>();
private final Set<String> keepers = new HashSet<String>();
// only used for salted scanners
private final int index = 0;
/** nanosecond timestamps */
// reset each time we send an RPC to HBase
private long fetch_start = 0;
// cumulation of time waiting on HBase
private long fetch_time = 0;
// cumulation of time resolving UIDs
private long uid_resolve_time = 0;
private long uids_resolved = 0;
// cumulation of time compacting
private long compaction_time = 0;
private long dps_pre_filter = 0;
private long rows_pre_filter = 0;
private long dps_post_filter = 0;
private long rows_post_filter = 0;
/** Error callback that will capture an exception from AsyncHBase and store
* it so we can bubble it up to the caller.
*/
class ErrorCB implements Callback<Object, Exception> {
@Override
public Object call(final Exception e) throws Exception {
LOG.error("Scanner " + scanner + " threw an exception", e);
close(e);
return null;
}
}
/**
* Starts the scanner and is called recursively to fetch the next set of
* rows from the scanner.
* @return The map of spans if loaded successfully, null if no data was
* found
*/
public Object scan() {
fetch_start = DateTime.nanoTime();
return scanner.nextRows().addCallback(this).addErrback(new ErrorCB());
}
/**
* Loops through each row of the scanner results and parses out data
* points and optional meta data
* @return null if no rows were found, otherwise the TreeMap with spans
*/
@Override
public Object call(final ArrayList<ArrayList<KeyValue>> rows) throws Exception {
fetch_time += DateTime.nanoTime() - fetch_start;
try {
if (rows == null) {
scanlatency.add((int) DateTime.msFromNano(fetch_time));
LOG.info(TsdbQuery.this + " matched " + nrows + " rows in " + spans.size() + " spans in " + DateTime.msFromNano(fetch_time) + "ms");
close(null);
return null;
}
if (timeout > 0 && DateTime.msFromNanoDiff(DateTime.nanoTime(), scanner_start) > timeout) {
throw new InterruptedException("Query timeout exceeded!");
}
rows_pre_filter += rows.size();
// used for UID resolution if a filter is involved
final List<Deferred<Object>> lookups = filters != null && !filters.isEmpty() ? new ArrayList<Deferred<Object>>(rows.size()) : null;
for (final ArrayList<KeyValue> row : rows) {
final byte[] key = row.get(0).key();
if (Bytes.memcmp(metric, key, 0, metric_width) != 0) {
scanner.close();
throw new IllegalDataException("HBase returned a row that doesn't match" + " our scanner (" + scanner + ")! " + row + " does not start" + " with " + Arrays.toString(metric));
}
// columns.
for (final KeyValue kv : row) {
if (kv.qualifier().length % 2 == 0) {
if (kv.qualifier().length == 2 || kv.qualifier().length == 4) {
++dps_pre_filter;
} else {
// same precision. This is likely incorrect.
if (Internal.inMilliseconds(kv.qualifier())) {
dps_pre_filter += (kv.qualifier().length / 4);
} else {
dps_pre_filter += (kv.qualifier().length / 2);
}
}
} else if (kv.qualifier()[0] == AppendDataPoints.APPEND_COLUMN_PREFIX) {
// with appends we don't have a good rough estimate as the length
// can vary widely with the value length variability. Therefore we
// have to iterate.
int idx = 0;
int qlength = 0;
while (idx < kv.value().length) {
qlength = Internal.getQualifierLength(kv.value(), idx);
idx += qlength + Internal.getValueLengthFromQualifier(kv.value(), idx);
++dps_pre_filter;
}
}
}
// TODO - byte set instead of a string for the uid may be faster
if (scanner_filters != null && !scanner_filters.isEmpty()) {
lookups.clear();
final String tsuid = UniqueId.uidToString(UniqueId.getTSUIDFromKey(key, TSDB.metrics_width(), Const.TIMESTAMP_BYTES));
if (skips.contains(tsuid)) {
continue;
}
if (!keepers.contains(tsuid)) {
final long uid_start = DateTime.nanoTime();
/** CB to called after all of the UIDs have been resolved */
class MatchCB implements Callback<Object, ArrayList<Boolean>> {
@Override
public Object call(final ArrayList<Boolean> matches) throws Exception {
for (final boolean matched : matches) {
if (!matched) {
skips.add(tsuid);
return null;
}
}
// matched all, good data
keepers.add(tsuid);
processRow(key, row);
return null;
}
}
/** Resolves all of the row key UIDs to their strings for filtering */
class GetTagsCB implements Callback<Deferred<ArrayList<Boolean>>, Map<String, String>> {
@Override
public Deferred<ArrayList<Boolean>> call(final Map<String, String> tags) throws Exception {
uid_resolve_time += (DateTime.nanoTime() - uid_start);
uids_resolved += tags.size();
final List<Deferred<Boolean>> matches = new ArrayList<Deferred<Boolean>>(scanner_filters.size());
for (final TagVFilter filter : scanner_filters) {
matches.add(filter.match(tags));
}
return Deferred.group(matches);
}
}
lookups.add(Tags.getTagsAsync(tsdb, key).addCallbackDeferring(new GetTagsCB()).addBoth(new MatchCB()));
} else {
processRow(key, row);
}
} else {
processRow(key, row);
}
}
// if we don't have filters.
if (lookups != null && lookups.size() > 0) {
class GroupCB implements Callback<Object, ArrayList<Object>> {
@Override
public Object call(final ArrayList<Object> group) throws Exception {
return scan();
}
}
return Deferred.group(lookups).addCallback(new GroupCB());
} else {
return scan();
}
} catch (Exception e) {
close(e);
return null;
}
}
/**
* Finds or creates the span for this row, compacts it and stores it.
* @param key The row key to use for fetching the span
* @param row The row to add
*/
void processRow(final byte[] key, final ArrayList<KeyValue> row) {
++rows_post_filter;
if (delete) {
final DeleteRequest del = new DeleteRequest(tsdb.dataTable(), key);
tsdb.getClient().delete(del);
}
// columns.
for (final KeyValue kv : row) {
if (kv.qualifier().length % 2 == 0) {
if (kv.qualifier().length == 2 || kv.qualifier().length == 4) {
++dps_post_filter;
} else {
// same precision. This is likely incorrect.
if (Internal.inMilliseconds(kv.qualifier())) {
dps_post_filter += (kv.qualifier().length / 4);
} else {
dps_post_filter += (kv.qualifier().length / 2);
}
}
} else if (kv.qualifier()[0] == AppendDataPoints.APPEND_COLUMN_PREFIX) {
// with appends we don't have a good rough estimate as the length
// can vary widely with the value length variability. Therefore we
// have to iterate.
int idx = 0;
int qlength = 0;
while (idx < kv.value().length) {
qlength = Internal.getQualifierLength(kv.value(), idx);
idx += qlength + Internal.getValueLengthFromQualifier(kv.value(), idx);
++dps_post_filter;
}
}
}
Span datapoints = spans.get(key);
if (datapoints == null) {
datapoints = new Span(tsdb);
spans.put(key, datapoints);
}
final long compaction_start = DateTime.nanoTime();
final KeyValue compacted = tsdb.compact(row, datapoints.getAnnotations());
compaction_time += (DateTime.nanoTime() - compaction_start);
seenAnnotation |= !datapoints.getAnnotations().isEmpty();
if (compacted != null) {
// Can be null if we ignored all KVs.
datapoints.addRow(compacted);
++nrows;
}
}
void close(final Exception e) {
scanner.close();
if (query_stats != null) {
query_stats.addScannerStat(query_index, index, QueryStat.SCANNER_TIME, DateTime.nanoTime() - scan_start_time);
// Scanner Stats
/* Uncomment when AsyncHBase has this feature:
query_stats.addScannerStat(query_index, index,
QueryStat.ROWS_FROM_STORAGE, scanner.getRowsFetched());
query_stats.addScannerStat(query_index, index,
QueryStat.COLUMNS_FROM_STORAGE, scanner.getColumnsFetched());
query_stats.addScannerStat(query_index, index,
QueryStat.BYTES_FROM_STORAGE, scanner.getBytesFetched()); */
query_stats.addScannerStat(query_index, index, QueryStat.HBASE_TIME, fetch_time);
query_stats.addScannerStat(query_index, index, QueryStat.SUCCESSFUL_SCAN, e == null ? 1 : 0);
// Post Scan stats
query_stats.addScannerStat(query_index, index, QueryStat.ROWS_PRE_FILTER, rows_pre_filter);
query_stats.addScannerStat(query_index, index, QueryStat.DPS_PRE_FILTER, dps_pre_filter);
query_stats.addScannerStat(query_index, index, QueryStat.ROWS_POST_FILTER, rows_post_filter);
query_stats.addScannerStat(query_index, index, QueryStat.DPS_POST_FILTER, dps_post_filter);
query_stats.addScannerStat(query_index, index, QueryStat.SCANNER_UID_TO_STRING_TIME, uid_resolve_time);
query_stats.addScannerStat(query_index, index, QueryStat.UID_PAIRS_RESOLVED, uids_resolved);
query_stats.addScannerStat(query_index, index, QueryStat.COMPACTION_TIME, compaction_time);
}
if (e != null) {
results.callback(e);
} else if (nrows < 1 && !seenAnnotation) {
results.callback(null);
} else {
results.callback(spans);
}
}
}
new ScannerCB().scan();
return results;
}
use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.
the class TsdbQuery method configureFromQuery.
@Override
public Deferred<Object> configureFromQuery(final TSQuery query, final int index) {
if (query.getQueries() == null || query.getQueries().isEmpty()) {
throw new IllegalArgumentException("Missing sub queries");
}
if (index < 0 || index > query.getQueries().size()) {
throw new IllegalArgumentException("Query index was out of range");
}
final TSSubQuery sub_query = query.getQueries().get(index);
setStartTime(query.startTime());
setEndTime(query.endTime());
setDelete(query.getDelete());
query_index = index;
query_stats = query.getQueryStats();
// set common options
aggregator = sub_query.aggregator();
rate = sub_query.getRate();
rate_options = sub_query.getRateOptions();
if (rate_options == null) {
rate_options = new RateOptions();
}
downsampler = sub_query.downsamplingSpecification();
filters = sub_query.getFilters();
explicit_tags = sub_query.getExplicitTags();
// if we have tsuids set, that takes precedence
if (sub_query.getTsuids() != null && !sub_query.getTsuids().isEmpty()) {
tsuids = new ArrayList<String>(sub_query.getTsuids());
String first_metric = "";
for (final String tsuid : tsuids) {
if (first_metric.isEmpty()) {
first_metric = tsuid.substring(0, TSDB.metrics_width() * 2).toUpperCase();
continue;
}
final String metric = tsuid.substring(0, TSDB.metrics_width() * 2).toUpperCase();
if (!first_metric.equals(metric)) {
throw new IllegalArgumentException("One or more TSUIDs did not share the same metric [" + first_metric + "] [" + metric + "]");
}
}
return Deferred.fromResult(null);
} else {
/** Triggers the group by resolution if we had filters to resolve */
class FilterCB implements Callback<Object, ArrayList<byte[]>> {
@Override
public Object call(final ArrayList<byte[]> results) throws Exception {
findGroupBys();
return null;
}
}
/** Resolve and group by tags after resolving the metric */
class MetricCB implements Callback<Deferred<Object>, byte[]> {
@Override
public Deferred<Object> call(final byte[] uid) throws Exception {
metric = uid;
if (filters != null) {
final List<Deferred<byte[]>> deferreds = new ArrayList<Deferred<byte[]>>(filters.size());
for (final TagVFilter filter : filters) {
deferreds.add(filter.resolveTagkName(tsdb));
}
return Deferred.group(deferreds).addCallback(new FilterCB());
} else {
return Deferred.fromResult(null);
}
}
}
// fire off the callback chain by resolving the metric first
return tsdb.metrics.getIdAsync(sub_query.getMetric()).addCallbackDeferring(new MetricCB());
}
}
use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.
the class AddDataExample method main.
public static void main(final String[] args) throws Exception {
processArgs(args);
// Create a config object with a path to the file for parsing. Or manually
// override settings.
// e.g. config.overrideConfig("tsd.storage.hbase.zk_quorum", "localhost");
final Config config;
if (pathToConfigFile != null && !pathToConfigFile.isEmpty()) {
config = new Config(pathToConfigFile);
} else {
// Search for a default config from /etc/opentsdb/opentsdb.conf, etc.
config = new Config(true);
}
final TSDB tsdb = new TSDB(config);
// Declare new metric
String metricName = "my.tsdb.test.metric";
// First check to see it doesn't already exist
// we don't actually need this for the first
byte[] byteMetricUID;
// addPoint().
try {
byteMetricUID = tsdb.getUID(UniqueIdType.METRIC, metricName);
} catch (IllegalArgumentException iae) {
System.out.println("Metric name not valid.");
iae.printStackTrace();
System.exit(1);
} catch (NoSuchUniqueName nsune) {
// If not, great. Create it.
byteMetricUID = tsdb.assignUid("metric", metricName);
}
// Make a single datum
long timestamp = System.currentTimeMillis() / 1000;
long value = 314159;
// Make key-val
Map<String, String> tags = new HashMap<String, String>(1);
tags.put("script", "example1");
// Start timer
long startTime1 = System.currentTimeMillis();
// Write a number of data points at 30 second intervals. Each write will
// return a deferred (similar to a Java Future or JS Promise) that will
// be called on completion with either a "null" value on success or an
// exception.
int n = 100;
ArrayList<Deferred<Object>> deferreds = new ArrayList<Deferred<Object>>(n);
for (int i = 0; i < n; i++) {
Deferred<Object> deferred = tsdb.addPoint(metricName, timestamp, value + i, tags);
deferreds.add(deferred);
timestamp += 30;
}
// Add the callbacks to the deferred object. (They might have already
// returned, btw)
// This will cause the calling thread to wait until the add has completed.
System.out.println("Waiting for deferred result to return...");
Deferred.groupInOrder(deferreds).addErrback(new AddDataExample().new errBack()).addCallback(new AddDataExample().new succBack()).join();
// Alternatively you can add another callback here or use a join with a
// timeout argument.
// End timer.
long elapsedTime1 = System.currentTimeMillis() - startTime1;
System.out.println("\nAdding " + n + " points took: " + elapsedTime1 + " milliseconds.\n");
// Gracefully shutdown connection to TSDB. This is CRITICAL as it will
// flush any pending operations to HBase.
tsdb.shutdown().join();
}
use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.
the class Annotation method deleteRange.
/**
* Deletes global or TSUID associated annotiations for the given time range.
* @param tsdb The TSDB object to use for storage access
* @param tsuid An optional TSUID. If set to null, then global annotations for
* the given range will be deleted
* @param start_time A start timestamp in milliseconds
* @param end_time An end timestamp in millseconds
* @return The number of annotations deleted
* @throws IllegalArgumentException if the timestamps are invalid
* @since 2.1
*/
public static Deferred<Integer> deleteRange(final TSDB tsdb, final byte[] tsuid, final long start_time, final long end_time) {
if (end_time < 1) {
throw new IllegalArgumentException("The end timestamp has not been set");
}
if (end_time < start_time) {
throw new IllegalArgumentException("The end timestamp cannot be less than the start timestamp");
}
final List<Deferred<Object>> delete_requests = new ArrayList<Deferred<Object>>();
int width = tsuid != null ? Const.SALT_WIDTH() + tsuid.length + Const.TIMESTAMP_BYTES : Const.SALT_WIDTH() + TSDB.metrics_width() + Const.TIMESTAMP_BYTES;
final byte[] start_row = new byte[width];
final byte[] end_row = new byte[width];
// downsample to seconds for the row keys
final long start = start_time / 1000;
final long end = end_time / 1000;
final long normalized_start = (start - (start % Const.MAX_TIMESPAN));
final long normalized_end = (end - (end % Const.MAX_TIMESPAN) + Const.MAX_TIMESPAN);
Bytes.setInt(start_row, (int) normalized_start, Const.SALT_WIDTH() + TSDB.metrics_width());
Bytes.setInt(end_row, (int) normalized_end, Const.SALT_WIDTH() + TSDB.metrics_width());
if (tsuid != null) {
// first copy the metric UID then the tags
System.arraycopy(tsuid, 0, start_row, Const.SALT_WIDTH(), TSDB.metrics_width());
System.arraycopy(tsuid, 0, end_row, Const.SALT_WIDTH(), TSDB.metrics_width());
width = Const.SALT_WIDTH() + TSDB.metrics_width() + Const.TIMESTAMP_BYTES;
final int remainder = tsuid.length - TSDB.metrics_width();
System.arraycopy(tsuid, TSDB.metrics_width(), start_row, width, remainder);
System.arraycopy(tsuid, TSDB.metrics_width(), end_row, width, remainder);
}
/**
* Iterates through the scanner results in an asynchronous manner, returning
* once the scanner returns a null result set.
*/
final class ScannerCB implements Callback<Deferred<List<Deferred<Object>>>, ArrayList<ArrayList<KeyValue>>> {
final Scanner scanner;
public ScannerCB() {
scanner = tsdb.getClient().newScanner(tsdb.dataTable());
scanner.setStartKey(start_row);
scanner.setStopKey(end_row);
scanner.setFamily(FAMILY);
if (tsuid != null) {
final List<String> tsuids = new ArrayList<String>(1);
tsuids.add(UniqueId.uidToString(tsuid));
Internal.createAndSetTSUIDFilter(scanner, tsuids);
}
}
public Deferred<List<Deferred<Object>>> scan() {
return scanner.nextRows().addCallbackDeferring(this);
}
@Override
public Deferred<List<Deferred<Object>>> call(final ArrayList<ArrayList<KeyValue>> rows) throws Exception {
if (rows == null || rows.isEmpty()) {
return Deferred.fromResult(delete_requests);
}
for (final ArrayList<KeyValue> row : rows) {
final long base_time = Internal.baseTime(tsdb, row.get(0).key());
for (KeyValue column : row) {
if ((column.qualifier().length == 3 || column.qualifier().length == 5) && column.qualifier()[0] == PREFIX()) {
final long timestamp = timeFromQualifier(column.qualifier(), base_time);
if (timestamp < start_time || timestamp > end_time) {
continue;
}
final DeleteRequest delete = new DeleteRequest(tsdb.dataTable(), column.key(), FAMILY, column.qualifier());
delete_requests.add(tsdb.getClient().delete(delete));
}
}
}
return scan();
}
}
/** Called when the scanner is done. Delete requests may still be pending */
final class ScannerDoneCB implements Callback<Deferred<ArrayList<Object>>, List<Deferred<Object>>> {
@Override
public Deferred<ArrayList<Object>> call(final List<Deferred<Object>> deletes) throws Exception {
return Deferred.group(delete_requests);
}
}
/** Waits on the group of deferreds to complete before returning the count */
final class GroupCB implements Callback<Deferred<Integer>, ArrayList<Object>> {
@Override
public Deferred<Integer> call(final ArrayList<Object> deletes) throws Exception {
return Deferred.fromResult(deletes.size());
}
}
Deferred<ArrayList<Object>> scanner_done = new ScannerCB().scan().addCallbackDeferring(new ScannerDoneCB());
return scanner_done.addCallbackDeferring(new GroupCB());
}
Aggregations