use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class HttpJsonSerializer method formatQueryAsyncV1.
/**
* Format the results from a timeseries data query
* @param data_query The TSQuery object used to fetch the results
* @param results The data fetched from storage
* @param globals An optional list of global annotation objects
* @return A Deferred<ChannelBuffer> object to pass on to the caller
* @throws IOException if serialization failed
* @since 2.2
*/
public Deferred<ChannelBuffer> formatQueryAsyncV1(final TSQuery data_query, final List<DataPoints[]> results, final List<Annotation> globals) throws IOException {
final long start = DateTime.currentTimeMillis();
final boolean as_arrays = this.query.hasQueryStringParam("arrays");
final String jsonp = this.query.getQueryStringParam("jsonp");
// buffers and an array list to stored the deferreds
final ChannelBuffer response = ChannelBuffers.dynamicBuffer();
final OutputStream output = new ChannelBufferOutputStream(response);
// too bad an inner class can't modify a primitive. This is a work around
final List<Boolean> timeout_flag = new ArrayList<Boolean>(1);
timeout_flag.add(false);
// start with JSONp if we're told to
if (jsonp != null && !jsonp.isEmpty()) {
output.write((jsonp + "(").getBytes(query.getCharset()));
}
// start the JSON generator and write the opening array
final JsonGenerator json = JSON.getFactory().createGenerator(output);
json.writeStartArray();
/**
* Every individual data point set (the result of a query and possibly a
* group by) will initiate an asynchronous metric/tag UID to name resolution
* and then print to the buffer.
* NOTE that because this is asynchronous, the order of results is
* indeterminate.
*/
class DPsResolver implements Callback<Deferred<Object>, Object> {
/** Has to be final to be shared with the nested classes */
final StringBuilder metric = new StringBuilder(256);
/** Resolved tags */
final Map<String, String> tags = new HashMap<String, String>();
/** Resolved aggregated tags */
final List<String> agg_tags = new ArrayList<String>();
/** A list storing the metric and tag resolve calls */
final List<Deferred<Object>> resolve_deferreds = new ArrayList<Deferred<Object>>();
/** The data points to serialize */
final DataPoints dps;
/** Starting time in nanos when we sent the UID resolution queries off */
long uid_start;
public DPsResolver(final DataPoints dps) {
this.dps = dps;
}
/** Resolves the metric UID to a name*/
class MetricResolver implements Callback<Object, String> {
public Object call(final String metric) throws Exception {
DPsResolver.this.metric.append(metric);
return null;
}
}
/** Resolves the tag UIDs to a key/value string set */
class TagResolver implements Callback<Object, Map<String, String>> {
public Object call(final Map<String, String> tags) throws Exception {
DPsResolver.this.tags.putAll(tags);
return null;
}
}
/** Resolves aggregated tags */
class AggTagResolver implements Callback<Object, List<String>> {
public Object call(final List<String> tags) throws Exception {
DPsResolver.this.agg_tags.addAll(tags);
return null;
}
}
/** After the metric and tags have been resolved, this will print the
* results to the output buffer in the proper format.
*/
class WriteToBuffer implements Callback<Object, ArrayList<Object>> {
final DataPoints dps;
/**
* Default ctor that takes a data point set
* @param dps Datapoints to print
*/
public WriteToBuffer(final DataPoints dps) {
this.dps = dps;
}
/**
* Handles writing the data to the output buffer. The results of the
* deferreds don't matter as they will be stored in the class final
* variables.
*/
public Object call(final ArrayList<Object> deferreds) throws Exception {
data_query.getQueryStats().addStat(dps.getQueryIndex(), QueryStat.UID_TO_STRING_TIME, (DateTime.nanoTime() - uid_start));
final long local_serialization_start = DateTime.nanoTime();
final TSSubQuery orig_query = data_query.getQueries().get(dps.getQueryIndex());
json.writeStartObject();
json.writeStringField("metric", metric.toString());
json.writeFieldName("tags");
json.writeStartObject();
if (dps.getTags() != null) {
for (Map.Entry<String, String> tag : tags.entrySet()) {
json.writeStringField(tag.getKey(), tag.getValue());
}
}
json.writeEndObject();
json.writeFieldName("aggregateTags");
json.writeStartArray();
if (dps.getAggregatedTags() != null) {
for (String atag : agg_tags) {
json.writeString(atag);
}
}
json.writeEndArray();
if (data_query.getShowQuery()) {
json.writeObjectField("query", orig_query);
}
if (data_query.getShowTSUIDs()) {
json.writeFieldName("tsuids");
json.writeStartArray();
final List<String> tsuids = dps.getTSUIDs();
Collections.sort(tsuids);
for (String tsuid : tsuids) {
json.writeString(tsuid);
}
json.writeEndArray();
}
if (!data_query.getNoAnnotations()) {
final List<Annotation> annotations = dps.getAnnotations();
if (annotations != null) {
Collections.sort(annotations);
json.writeArrayFieldStart("annotations");
for (Annotation note : annotations) {
json.writeObject(note);
}
json.writeEndArray();
}
if (globals != null && !globals.isEmpty()) {
Collections.sort(globals);
json.writeArrayFieldStart("globalAnnotations");
for (Annotation note : globals) {
json.writeObject(note);
}
json.writeEndArray();
}
}
// now the fun stuff, dump the data and time just the iteration over
// the data points
final long dps_start = DateTime.nanoTime();
json.writeFieldName("dps");
long counter = 0;
// default is to write a map, otherwise write arrays
if (!timeout_flag.get(0) && as_arrays) {
json.writeStartArray();
for (final DataPoint dp : dps) {
if (dp.timestamp() < data_query.startTime() || dp.timestamp() > data_query.endTime()) {
continue;
}
final long timestamp = data_query.getMsResolution() ? dp.timestamp() : dp.timestamp() / 1000;
json.writeStartArray();
json.writeNumber(timestamp);
if (dp.isInteger()) {
json.writeNumber(dp.longValue());
} else {
// Report missing intervals as null or NaN.
final double value = dp.doubleValue();
if (Double.isNaN(value) && orig_query.fillPolicy() == FillPolicy.NULL) {
json.writeNull();
} else {
json.writeNumber(dp.doubleValue());
}
}
json.writeEndArray();
++counter;
}
json.writeEndArray();
} else if (!timeout_flag.get(0)) {
json.writeStartObject();
for (final DataPoint dp : dps) {
if (dp.timestamp() < (data_query.startTime()) || dp.timestamp() > (data_query.endTime())) {
continue;
}
final long timestamp = data_query.getMsResolution() ? dp.timestamp() : dp.timestamp() / 1000;
if (dp.isInteger()) {
json.writeNumberField(Long.toString(timestamp), dp.longValue());
} else {
// Report missing intervals as null or NaN.
final double value = dp.doubleValue();
if (Double.isNaN(value) && orig_query.fillPolicy() == FillPolicy.NULL) {
json.writeNumberField(Long.toString(timestamp), null);
} else {
json.writeNumberField(Long.toString(timestamp), dp.doubleValue());
}
}
++counter;
}
json.writeEndObject();
} else {
// skipping data points all together due to timeout
json.writeStartObject();
json.writeEndObject();
}
final long agg_time = DateTime.nanoTime() - dps_start;
data_query.getQueryStats().addStat(dps.getQueryIndex(), QueryStat.AGGREGATION_TIME, agg_time);
data_query.getQueryStats().addStat(dps.getQueryIndex(), QueryStat.AGGREGATED_SIZE, counter);
// yeah, it's a little early but we need to dump it out with the results.
data_query.getQueryStats().addStat(dps.getQueryIndex(), QueryStat.SERIALIZATION_TIME, DateTime.nanoTime() - local_serialization_start);
if (!timeout_flag.get(0) && data_query.getShowStats()) {
int query_index = (dps == null) ? -1 : dps.getQueryIndex();
QueryStats stats = data_query.getQueryStats();
if (query_index >= 0) {
json.writeFieldName("stats");
final Map<String, Object> s = stats.getQueryStats(query_index, false);
if (s != null) {
json.writeObject(s);
} else {
json.writeStringField("ERROR", "NO STATS FOUND");
}
}
}
// close the results for this particular query
json.writeEndObject();
return null;
}
}
/**
* When called, initiates a resolution of metric and tag UIDs to names,
* then prints to the output buffer once they are completed.
*/
public Deferred<Object> call(final Object obj) throws Exception {
this.uid_start = DateTime.nanoTime();
resolve_deferreds.add(dps.metricNameAsync().addCallback(new MetricResolver()));
resolve_deferreds.add(dps.getTagsAsync().addCallback(new TagResolver()));
resolve_deferreds.add(dps.getAggregatedTagsAsync().addCallback(new AggTagResolver()));
return Deferred.group(resolve_deferreds).addCallback(new WriteToBuffer(dps));
}
}
// We want the serializer to execute serially so we need to create a callback
// chain so that when one DPsResolver is finished, it triggers the next to
// start serializing.
final Deferred<Object> cb_chain = new Deferred<Object>();
for (DataPoints[] separate_dps : results) {
for (DataPoints dps : separate_dps) {
try {
cb_chain.addCallback(new DPsResolver(dps));
} catch (Exception e) {
throw new RuntimeException("Unexpected error durring resolution", e);
}
}
}
/** Final callback to close out the JSON array and return our results */
class FinalCB implements Callback<ChannelBuffer, Object> {
public ChannelBuffer call(final Object obj) throws Exception {
// Call this here so we rollup sub metrics into a summary. It's not
// completely accurate, of course, because we still have to write the
// summary and close the writer. But it's close.
data_query.getQueryStats().markSerializationSuccessful();
// TODO - yeah, I've heard this sucks, we need to figure out a better way.
if (data_query.getShowSummary()) {
final QueryStats stats = data_query.getQueryStats();
json.writeStartObject();
json.writeFieldName("statsSummary");
json.writeObject(stats.getStats(true, true));
json.writeEndObject();
}
// IMPORTANT Make sure the close the JSON array and the generator
json.writeEndArray();
json.close();
if (jsonp != null && !jsonp.isEmpty()) {
output.write(")".getBytes());
}
return response;
}
}
// trigger the callback chain here
cb_chain.callback(null);
return cb_chain.addCallback(new FinalCB());
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class GraphHandler method doGraph.
// TODO(HugoMFernandes): Most of this (query-related) logic is implemented in
// net.opentsdb.tsd.QueryRpc.java (which actually does this asynchronously),
// so we should refactor both classes to split the actual logic used to
// generate the data from the actual visualization (removing all duped code).
private void doGraph(final TSDB tsdb, final HttpQuery query) throws IOException {
final String basepath = getGnuplotBasePath(tsdb, query);
long start_time = DateTime.parseDateTimeString(query.getRequiredQueryStringParam("start"), query.getQueryStringParam("tz"));
final boolean nocache = query.hasQueryStringParam("nocache");
if (start_time == -1) {
throw BadRequestException.missingParameter("start");
} else {
// temp fixup to seconds from ms until the rest of TSDB supports ms
// Note you can't append this to the DateTime.parseDateTimeString() call as
// it clobbers -1 results
start_time /= 1000;
}
long end_time = DateTime.parseDateTimeString(query.getQueryStringParam("end"), query.getQueryStringParam("tz"));
final long now = System.currentTimeMillis() / 1000;
if (end_time == -1) {
end_time = now;
} else {
// temp fixup to seconds from ms until the rest of TSDB supports ms
// Note you can't append this to the DateTime.parseDateTimeString() call as
// it clobbers -1 results
end_time /= 1000;
}
final int max_age = computeMaxAge(query, start_time, end_time, now);
if (!nocache && isDiskCacheHit(query, end_time, max_age, basepath)) {
return;
}
// Parse TSQuery from HTTP query
final TSQuery tsquery = QueryRpc.parseQuery(tsdb, query);
tsquery.validateAndSetQuery();
// Build the queries for the parsed TSQuery
Query[] tsdbqueries = tsquery.buildQueries(tsdb);
List<String> options = query.getQueryStringParams("o");
if (options == null) {
options = new ArrayList<String>(tsdbqueries.length);
for (int i = 0; i < tsdbqueries.length; i++) {
options.add("");
}
} else if (options.size() != tsdbqueries.length) {
throw new BadRequestException(options.size() + " `o' parameters, but " + tsdbqueries.length + " `m' parameters.");
}
for (final Query tsdbquery : tsdbqueries) {
try {
tsdbquery.setStartTime(start_time);
} catch (IllegalArgumentException e) {
throw new BadRequestException("start time: " + e.getMessage());
}
try {
tsdbquery.setEndTime(end_time);
} catch (IllegalArgumentException e) {
throw new BadRequestException("end time: " + e.getMessage());
}
}
final Plot plot = new Plot(start_time, end_time, DateTime.timezones.get(query.getQueryStringParam("tz")));
setPlotDimensions(query, plot);
setPlotParams(query, plot);
final int nqueries = tsdbqueries.length;
@SuppressWarnings("unchecked") final HashSet<String>[] aggregated_tags = new HashSet[nqueries];
int npoints = 0;
for (int i = 0; i < nqueries; i++) {
try {
// execute the TSDB query!
// XXX This is slow and will block Netty. TODO(tsuna): Don't block.
// TODO(tsuna): Optimization: run each query in parallel.
final DataPoints[] series = tsdbqueries[i].run();
for (final DataPoints datapoints : series) {
plot.add(datapoints, options.get(i));
aggregated_tags[i] = new HashSet<String>();
aggregated_tags[i].addAll(datapoints.getAggregatedTags());
npoints += datapoints.aggregatedSize();
}
} catch (RuntimeException e) {
logInfo(query, "Query failed (stack trace coming): " + tsdbqueries[i]);
throw e;
}
// free()
tsdbqueries[i] = null;
}
// free()
tsdbqueries = null;
if (query.hasQueryStringParam("ascii")) {
respondAsciiQuery(query, max_age, basepath, plot);
return;
}
final RunGnuplot rungnuplot = new RunGnuplot(query, max_age, plot, basepath, aggregated_tags, npoints);
class ErrorCB implements Callback<Object, Exception> {
public Object call(final Exception e) throws Exception {
LOG.warn("Failed to retrieve global annotations: ", e);
throw e;
}
}
class GlobalCB implements Callback<Object, List<Annotation>> {
public Object call(final List<Annotation> global_annotations) throws Exception {
rungnuplot.plot.setGlobals(global_annotations);
execGnuplot(rungnuplot, query);
return null;
}
}
// Fetch global annotations, if needed
if (!tsquery.getNoAnnotations() && tsquery.getGlobalAnnotations()) {
Annotation.getGlobalAnnotations(tsdb, start_time, end_time).addCallback(new GlobalCB()).addErrback(new ErrorCB());
} else {
execGnuplot(rungnuplot, query);
}
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class MetaPurge method purgeUIDMeta.
/**
* Scans the entire UID table and removes any UIDMeta objects found.
* @return The total number of columns deleted
*/
public Deferred<Long> purgeUIDMeta() {
// a list to store all pending deletes so we don't exit before they've
// completed
final ArrayList<Deferred<Object>> delete_calls = new ArrayList<Deferred<Object>>();
final Deferred<Long> result = new Deferred<Long>();
/**
* Scanner callback that will recursively call itself and loop through the
* rows of the UID table, issuing delete requests for all of the columns in
* a row that match a meta qualifier.
*/
final class MetaScanner implements Callback<Deferred<Long>, ArrayList<ArrayList<KeyValue>>> {
final Scanner scanner;
public MetaScanner() {
scanner = getScanner(tsdb.uidTable());
}
/**
* Fetches the next group of rows from the scanner and sets this class as
* a callback
* @return The total number of columns deleted after completion
*/
public Deferred<Long> scan() {
return scanner.nextRows().addCallbackDeferring(this);
}
@Override
public Deferred<Long> call(ArrayList<ArrayList<KeyValue>> rows) throws Exception {
if (rows == null) {
result.callback(columns);
return null;
}
for (final ArrayList<KeyValue> row : rows) {
// one delete request per row. We'll almost always delete the whole
// row, so preallocate some ram.
ArrayList<byte[]> qualifiers = new ArrayList<byte[]>(row.size());
for (KeyValue column : row) {
if (Bytes.equals(TSMeta.META_QUALIFIER(), column.qualifier())) {
qualifiers.add(column.qualifier());
} else if (Bytes.equals("metric_meta".getBytes(CHARSET), column.qualifier())) {
qualifiers.add(column.qualifier());
} else if (Bytes.equals("tagk_meta".getBytes(CHARSET), column.qualifier())) {
qualifiers.add(column.qualifier());
} else if (Bytes.equals("tagv_meta".getBytes(CHARSET), column.qualifier())) {
qualifiers.add(column.qualifier());
}
}
if (qualifiers.size() > 0) {
columns += qualifiers.size();
final DeleteRequest delete = new DeleteRequest(tsdb.uidTable(), row.get(0).key(), NAME_FAMILY, qualifiers.toArray(new byte[qualifiers.size()][]));
delete_calls.add(tsdb.getClient().delete(delete));
}
}
/**
* Buffer callback used to wait on all of the delete calls for the
* last set of rows returned from the scanner so we don't fill up the
* deferreds array and OOM out.
*/
final class ContinueCB implements Callback<Deferred<Long>, ArrayList<Object>> {
@Override
public Deferred<Long> call(ArrayList<Object> deletes) throws Exception {
LOG.debug("[" + thread_id + "] Processed [" + deletes.size() + "] delete calls");
delete_calls.clear();
return scan();
}
}
// fetch the next set of rows after waiting for current set of delete
// requests to complete
Deferred.group(delete_calls).addCallbackDeferring(new ContinueCB());
return null;
}
}
// start the scan
new MetaScanner().scan();
return result;
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class MetaPurge method purgeTSMeta.
/**
* Scans the entire UID table and removes any UIDMeta objects found.
* @return The total number of columns deleted
*/
public Deferred<Long> purgeTSMeta() {
// a list to store all pending deletes so we don't exit before they've
// completed
final ArrayList<Deferred<Object>> delete_calls = new ArrayList<Deferred<Object>>();
final Deferred<Long> result = new Deferred<Long>();
/**
* Scanner callback that will recursively call itself and loop through the
* rows of the UID table, issuing delete requests for all of the columns in
* a row that match a meta qualifier.
*/
final class MetaScanner implements Callback<Deferred<Long>, ArrayList<ArrayList<KeyValue>>> {
final Scanner scanner;
public MetaScanner() {
scanner = getScanner(tsdb.metaTable());
}
/**
* Fetches the next group of rows from the scanner and sets this class as
* a callback
* @return The total number of columns deleted after completion
*/
public Deferred<Long> scan() {
return scanner.nextRows().addCallbackDeferring(this);
}
@Override
public Deferred<Long> call(ArrayList<ArrayList<KeyValue>> rows) throws Exception {
if (rows == null) {
result.callback(columns);
return null;
}
for (final ArrayList<KeyValue> row : rows) {
// one delete request per row. We'll almost always delete the whole
// row, so preallocate some ram.
ArrayList<byte[]> qualifiers = new ArrayList<byte[]>(row.size());
for (KeyValue column : row) {
if (Bytes.equals(TSMeta.META_QUALIFIER(), column.qualifier())) {
qualifiers.add(column.qualifier());
} else if (Bytes.equals(TSMeta.COUNTER_QUALIFIER(), column.qualifier())) {
qualifiers.add(column.qualifier());
}
}
if (qualifiers.size() > 0) {
columns += qualifiers.size();
final DeleteRequest delete = new DeleteRequest(tsdb.metaTable(), row.get(0).key(), NAME_FAMILY, qualifiers.toArray(new byte[qualifiers.size()][]));
delete_calls.add(tsdb.getClient().delete(delete));
}
}
/**
* Buffer callback used to wait on all of the delete calls for the
* last set of rows returned from the scanner so we don't fill up the
* deferreds array and OOM out.
*/
final class ContinueCB implements Callback<Deferred<Long>, ArrayList<Object>> {
@Override
public Deferred<Long> call(ArrayList<Object> deletes) throws Exception {
LOG.debug("[" + thread_id + "] Processed [" + deletes.size() + "] delete calls");
delete_calls.clear();
return scan();
}
}
// fetch the next set of rows after waiting for current set of delete
// requests to complete
Deferred.group(delete_calls).addCallbackDeferring(new ContinueCB());
return null;
}
}
// start the scan
new MetaScanner().scan();
return result;
}
use of com.stumbleupon.async.Callback in project opentsdb by OpenTSDB.
the class TreeSync method run.
/**
* Performs a tree synchronization using a table scanner across the UID table
* @return 0 if completed successfully, something else if an error occurred
*/
public void run() {
final Scanner scanner = getScanner();
// start the process by loading all of the trees in the system
final List<Tree> trees;
try {
trees = Tree.fetchAllTrees(tsdb).joinUninterruptibly();
LOG.info("[" + thread_id + "] Complete");
} catch (Exception e) {
LOG.error("[" + thread_id + "] Unexpected Exception", e);
throw new RuntimeException("[" + thread_id + "] Unexpected exception", e);
}
if (trees == null) {
LOG.warn("No tree definitions were found");
return;
} else {
boolean has_enabled_tree = false;
for (Tree tree : trees) {
if (tree.getEnabled()) {
has_enabled_tree = true;
break;
}
}
if (!has_enabled_tree) {
LOG.warn("No enabled trees were found");
return;
}
LOG.info("Found [" + trees.size() + "] trees");
}
// setup an array for storing the tree processing calls so we can block
// until each call has completed
final ArrayList<Deferred<Boolean>> tree_calls = new ArrayList<Deferred<Boolean>>();
final Deferred<Boolean> completed = new Deferred<Boolean>();
/**
* Scanner callback that loops through the UID table recursively until
* the scanner returns a null row set.
*/
final class TsuidScanner implements Callback<Deferred<Boolean>, ArrayList<ArrayList<KeyValue>>> {
/**
* Fetches the next set of rows from the scanner, adding this class as a
* callback
* @return A meaningless deferred used to wait on until processing has
* completed
*/
public Deferred<Boolean> scan() {
return scanner.nextRows().addCallbackDeferring(this);
}
@Override
public Deferred<Boolean> call(ArrayList<ArrayList<KeyValue>> rows) throws Exception {
if (rows == null) {
completed.callback(true);
return null;
}
for (final ArrayList<KeyValue> row : rows) {
// convert to a string one time
final String tsuid = UniqueId.uidToString(row.get(0).key());
/**
* A throttling callback used to wait for the current TSMeta to
* complete processing through the trees before continuing on with
* the next set.
*/
final class TreeBuilderBufferCB implements Callback<Boolean, ArrayList<ArrayList<Boolean>>> {
@Override
public Boolean call(ArrayList<ArrayList<Boolean>> builder_calls) throws Exception {
//LOG.debug("Processed [" + builder_calls.size() + "] tree_calls");
return true;
}
}
/**
* Executed after parsing a TSMeta object and loading all of the
* associated UIDMetas. Once the meta has been loaded, this callback
* runs it through each of the configured TreeBuilder objects and
* stores the resulting deferred in an array. Once processing of all
* of the rules has completed, we group the deferreds and call
* BufferCB() to wait for their completion.
*/
final class ParseCB implements Callback<Deferred<Boolean>, TSMeta> {
final ArrayList<Deferred<ArrayList<Boolean>>> builder_calls = new ArrayList<Deferred<ArrayList<Boolean>>>();
@Override
public Deferred<Boolean> call(TSMeta meta) throws Exception {
if (meta != null) {
LOG.debug("Processing TSMeta: " + meta + " w value: " + JSON.serializeToString(meta));
// copy the trees into a tree builder object and iterate through
// each builder. We need to do this as a builder is not thread
// safe and cannot be used asynchronously.
final ArrayList<TreeBuilder> tree_builders = new ArrayList<TreeBuilder>(trees.size());
for (Tree tree : trees) {
if (!tree.getEnabled()) {
continue;
}
final TreeBuilder builder = new TreeBuilder(tsdb, tree);
tree_builders.add(builder);
}
for (TreeBuilder builder : tree_builders) {
builder_calls.add(builder.processTimeseriesMeta(meta));
}
return Deferred.group(builder_calls).addCallback(new TreeBuilderBufferCB());
} else {
return Deferred.fromResult(false);
}
}
}
/**
* An error handler used to catch issues when loading the TSMeta such
* as a missing UID name. In these situations we want to log that the
* TSMeta had an issue and continue on.
*/
final class ErrBack implements Callback<Deferred<Boolean>, Exception> {
@Override
public Deferred<Boolean> call(Exception e) throws Exception {
if (e.getClass().equals(IllegalStateException.class)) {
LOG.error("Invalid data when processing TSUID [" + tsuid + "]", e);
} else if (e.getClass().equals(IllegalArgumentException.class)) {
LOG.error("Invalid data when processing TSUID [" + tsuid + "]", e);
} else if (e.getClass().equals(NoSuchUniqueId.class)) {
LOG.warn("Timeseries [" + tsuid + "] includes a non-existant UID: " + e.getMessage());
} else {
LOG.error("[" + thread_id + "] Exception while processing TSUID [" + tsuid + "]", e);
}
return Deferred.fromResult(false);
}
}
// matched a TSMeta column, so request a parsing and loading of
// associated UIDMeta objects, then pass it off to callbacks for
// parsing through the trees.
final Deferred<Boolean> process_tsmeta = TSMeta.parseFromColumn(tsdb, row.get(0), true).addCallbackDeferring(new ParseCB());
process_tsmeta.addErrback(new ErrBack());
tree_calls.add(process_tsmeta);
}
/**
* Another buffer callback that waits for the current set of TSMetas to
* complete their tree calls before we fetch another set of rows from
* the scanner. This necessary to avoid OOM issues.
*/
final class ContinueCB implements Callback<Deferred<Boolean>, ArrayList<Boolean>> {
@Override
public Deferred<Boolean> call(ArrayList<Boolean> tsuids) throws Exception {
LOG.debug("Processed [" + tsuids.size() + "] tree_calls, continuing");
tree_calls.clear();
return scan();
}
}
// request the next set of rows from the scanner, but wait until the
// current set of TSMetas has been processed so we don't slaughter our
// host
Deferred.group(tree_calls).addCallback(new ContinueCB());
return Deferred.fromResult(null);
}
}
/**
* Used to capture unhandled exceptions from the scanner callbacks and
* exit the thread properly
*/
final class ErrBack implements Callback<Deferred<Boolean>, Exception> {
@Override
public Deferred<Boolean> call(Exception e) throws Exception {
LOG.error("Unexpected exception", e);
completed.callback(false);
return Deferred.fromResult(false);
}
}
final TsuidScanner tree_scanner = new TsuidScanner();
tree_scanner.scan().addErrback(new ErrBack());
try {
completed.joinUninterruptibly();
LOG.info("[" + thread_id + "] Complete");
} catch (Exception e) {
LOG.error("[" + thread_id + "] Scanner Exception", e);
throw new RuntimeException("[" + thread_id + "] Scanner exception", e);
}
return;
}
Aggregations