Search in sources :

Example 26 with Deferred

use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.

the class TSMeta method syncToStorage.

/**
   * Attempts a CompareAndSet storage call, loading the object from storage, 
   * synchronizing changes, and attempting a put. Also verifies that associated 
   * UID name mappings exist before merging.
   * <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> We do not store the UIDMeta information with TSMeta's since
   * users may change a single UIDMeta object and we don't want to update every
   * TSUID that includes that object with the new data. Instead, UIDMetas are
   * merged into the TSMeta on retrieval so we always have canonical data. This
   * also saves space in 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 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
   * @throws IllegalArgumentException if parsing failed
   * @throws NoSuchUniqueId If any of the UID name mappings do 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 (tsuid == null || tsuid.isEmpty()) {
        throw new IllegalArgumentException("Missing TSUID");
    }
    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 TSUID meta data");
    }
    /**
     * Callback used to verify that the UID name mappings exist. We don't need
     * to process the actual name, we just want it to throw an error if any
     * of the UIDs don't exist.
     */
    class UidCB implements Callback<Object, String> {

        @Override
        public Object call(String name) throws Exception {
            // nothing to do as missing mappings will throw a NoSuchUniqueId
            return null;
        }
    }
    // parse out the tags from the tsuid
    final List<byte[]> parsed_tags = UniqueId.getTagsFromTSUID(tsuid);
    // Deferred group used to accumulate UidCB callbacks so the next call
    // can wait until all of the UIDs have been verified
    ArrayList<Deferred<Object>> uid_group = new ArrayList<Deferred<Object>>(parsed_tags.size() + 1);
    // calculate the metric UID and fetch it's name mapping
    final byte[] metric_uid = UniqueId.stringToUid(tsuid.substring(0, TSDB.metrics_width() * 2));
    uid_group.add(tsdb.getUidName(UniqueIdType.METRIC, metric_uid).addCallback(new UidCB()));
    int idx = 0;
    for (byte[] tag : parsed_tags) {
        if (idx % 2 == 0) {
            uid_group.add(tsdb.getUidName(UniqueIdType.TAGK, tag).addCallback(new UidCB()));
        } else {
            uid_group.add(tsdb.getUidName(UniqueIdType.TAGV, tag).addCallback(new UidCB()));
        }
        idx++;
    }
    /**
     * Callback executed after all of the UID mappings have been verified. This
     * will then proceed with the CAS call.
     */
    final class ValidateCB implements Callback<Deferred<Boolean>, ArrayList<Object>> {

        private final TSMeta local_meta;

        public ValidateCB(final TSMeta local_meta) {
            this.local_meta = local_meta;
        }

        /**
       * Nested class that executes the CAS after retrieving existing TSMeta
       * from storage.
       */
        final class StoreCB implements Callback<Deferred<Boolean>, TSMeta> {

            /**
         * Executes the CAS if the TSMeta was successfully retrieved
         * @return True if the CAS was successful, false if the stored data
         * was modified in flight
         * @throws IllegalArgumentException if the TSMeta did not exist in
         * storage. Only the TSD should be able to create TSMeta objects.
         */
            @Override
            public Deferred<Boolean> call(TSMeta stored_meta) throws Exception {
                if (stored_meta == null) {
                    throw new IllegalArgumentException("Requested TSMeta did not exist");
                }
                final byte[] original_meta = stored_meta.getStorageJSON();
                local_meta.syncMeta(stored_meta, overwrite);
                final PutRequest put = new PutRequest(tsdb.metaTable(), UniqueId.stringToUid(local_meta.tsuid), FAMILY, META_QUALIFIER, local_meta.getStorageJSON());
                return tsdb.getClient().compareAndSet(put, original_meta);
            }
        }

        /**
       * Called on UID mapping verification and continues executing the CAS 
       * procedure.
       * @return Results from the {@link #StoreCB} callback
       */
        @Override
        public Deferred<Boolean> call(ArrayList<Object> validated) throws Exception {
            return getFromStorage(tsdb, UniqueId.stringToUid(tsuid)).addCallbackDeferring(new StoreCB());
        }
    }
    // Begins the callback chain by validating that the UID mappings exist
    return Deferred.group(uid_group).addCallbackDeferring(new ValidateCB(this));
}
Also used : Deferred(com.stumbleupon.async.Deferred) ArrayList(java.util.ArrayList) PutRequest(org.hbase.async.PutRequest) IOException(java.io.IOException) HBaseException(org.hbase.async.HBaseException) JSONException(net.opentsdb.utils.JSONException) Callback(com.stumbleupon.async.Callback) HashMap(java.util.HashMap) Map(java.util.Map)

Example 27 with Deferred

use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.

the class TSUIDQuery method getLastWriteTimes.

/**
   * Fetches a list of TSUIDs given the metric and optional tag pairs. The query
   * format is similar to TsdbQuery but doesn't support grouping operators for 
   * tags. Only TSUIDs that had "ts_counter" qualifiers will be returned.
   * <p>
   * NOTE: If you called {@link #setQuery(String, Map)} successfully this will
   * immediately scan the meta table. But if you used the CTOR to set the
   * metric and tags it will attempt to resolve those and may return an exception.
   * @return A map of TSUIDs to the last timestamp (in milliseconds) when the
   * "ts_counter" was updated. Note that the timestamp will be the time stored
   * by HBase, not the actual timestamp of the data point. If nothing was
   * found, the map will be empty but not null.
   * @throws IllegalArgumentException if the metric was not set or the tag map
   * was null
   */
public Deferred<ByteMap<Long>> getLastWriteTimes() {
    class ResolutionCB implements Callback<Deferred<ByteMap<Long>>, Object> {

        @Override
        public Deferred<ByteMap<Long>> call(Object arg0) throws Exception {
            final Scanner scanner = getScanner();
            scanner.setQualifier(TSMeta.COUNTER_QUALIFIER());
            final Deferred<ByteMap<Long>> results = new Deferred<ByteMap<Long>>();
            final ByteMap<Long> tsuids = new ByteMap<Long>();
            final class ErrBack implements Callback<Object, Exception> {

                @Override
                public Object call(final Exception e) throws Exception {
                    results.callback(e);
                    return null;
                }

                @Override
                public String toString() {
                    return "Error callback";
                }
            }
            /**
         * Scanner callback that will call itself while iterating through the 
         * tsdb-meta table
         */
            final class ScannerCB implements Callback<Object, ArrayList<ArrayList<KeyValue>>> {

                /**
           * 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() {
                    return scanner.nextRows().addCallback(this).addErrback(new ErrBack());
                }

                /**
           * 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 {
                    try {
                        if (rows == null) {
                            results.callback(tsuids);
                            return null;
                        }
                        for (final ArrayList<KeyValue> row : rows) {
                            final byte[] tsuid = row.get(0).key();
                            tsuids.put(tsuid, row.get(0).timestamp());
                        }
                        return scan();
                    } catch (Exception e) {
                        results.callback(e);
                        return null;
                    }
                }
            }
            new ScannerCB().scan();
            return results;
        }

        @Override
        public String toString() {
            return "Last counter time callback";
        }
    }
    if (metric_uid == null) {
        return resolveMetric().addCallbackDeferring(new ResolutionCB());
    }
    try {
        return new ResolutionCB().call(null);
    } catch (Exception e) {
        return Deferred.fromError(e);
    }
}
Also used : Scanner(org.hbase.async.Scanner) KeyValue(org.hbase.async.KeyValue) Deferred(com.stumbleupon.async.Deferred) ArrayList(java.util.ArrayList) ByteMap(org.hbase.async.Bytes.ByteMap) Callback(com.stumbleupon.async.Callback)

Example 28 with Deferred

use of com.stumbleupon.async.Deferred 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());
}
Also used : ChannelBufferOutputStream(org.jboss.netty.buffer.ChannelBufferOutputStream) OutputStream(java.io.OutputStream) Deferred(com.stumbleupon.async.Deferred) ArrayList(java.util.ArrayList) DataPoints(net.opentsdb.core.DataPoints) ChannelBuffer(org.jboss.netty.buffer.ChannelBuffer) DataPoint(net.opentsdb.core.DataPoint) IncomingDataPoint(net.opentsdb.core.IncomingDataPoint) JsonGenerator(com.fasterxml.jackson.core.JsonGenerator) ArrayList(java.util.ArrayList) List(java.util.List) ChannelBufferOutputStream(org.jboss.netty.buffer.ChannelBufferOutputStream) TSSubQuery(net.opentsdb.core.TSSubQuery) QueryException(net.opentsdb.core.QueryException) IOException(java.io.IOException) Annotation(net.opentsdb.meta.Annotation) Callback(com.stumbleupon.async.Callback) QueryStats(net.opentsdb.stats.QueryStats) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap)

Example 29 with Deferred

use of com.stumbleupon.async.Deferred in project opentsdb by OpenTSDB.

the class AnnotationRpc method execute.

/**
   * Performs CRUD methods on individual annotation objects.
   * @param tsdb The TSD to which we belong
   * @param query The query to parse and respond to
   */
public void execute(final TSDB tsdb, HttpQuery query) throws IOException {
    final HttpMethod method = query.getAPIMethod();
    final String[] uri = query.explodeAPIPath();
    final String endpoint = uri.length > 1 ? uri[1] : "";
    if (endpoint != null && endpoint.toLowerCase().endsWith("bulk")) {
        executeBulk(tsdb, method, query);
        return;
    }
    final Annotation note;
    if (query.hasContent()) {
        note = query.serializer().parseAnnotationV1();
    } else {
        note = parseQS(query);
    }
    // GET
    if (method == HttpMethod.GET) {
        try {
            if ("annotations".toLowerCase().equals(uri[0])) {
                fetchMultipleAnnotations(tsdb, note, query);
            } else {
                fetchSingleAnnotation(tsdb, note, query);
            }
        } catch (BadRequestException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    // POST
    } else if (method == HttpMethod.POST || method == HttpMethod.PUT) {
        /**
       * 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> {

            @Override
            public Deferred<Annotation> call(Boolean success) throws Exception {
                if (!success) {
                    throw new BadRequestException(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Failed to save the Annotation to storage", "This may be caused by another process modifying storage data");
                }
                return Annotation.getAnnotation(tsdb, note.getTSUID(), note.getStartTime());
            }
        }
        try {
            final Deferred<Annotation> process_meta = note.syncToStorage(tsdb, method == HttpMethod.PUT).addCallbackDeferring(new SyncCB());
            final Annotation updated_meta = process_meta.joinUninterruptibly();
            tsdb.indexAnnotation(note);
            query.sendReply(query.serializer().formatAnnotationV1(updated_meta));
        } catch (IllegalStateException e) {
            query.sendStatusOnly(HttpResponseStatus.NOT_MODIFIED);
        } catch (IllegalArgumentException e) {
            throw new BadRequestException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    // DELETE    
    } else if (method == HttpMethod.DELETE) {
        try {
            note.delete(tsdb).joinUninterruptibly();
            tsdb.deleteAnnotation(note);
        } catch (IllegalArgumentException e) {
            throw new BadRequestException("Unable to delete Annotation information", e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        query.sendStatusOnly(HttpResponseStatus.NO_CONTENT);
    } else {
        throw new BadRequestException(HttpResponseStatus.METHOD_NOT_ALLOWED, "Method not allowed", "The HTTP method [" + method.getName() + "] is not permitted for this endpoint");
    }
}
Also used : Deferred(com.stumbleupon.async.Deferred) Annotation(net.opentsdb.meta.Annotation) IOException(java.io.IOException) JSONException(net.opentsdb.utils.JSONException) HttpMethod(org.jboss.netty.handler.codec.http.HttpMethod)

Example 30 with Deferred

use of com.stumbleupon.async.Deferred 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;
}
Also used : Scanner(org.hbase.async.Scanner) KeyValue(org.hbase.async.KeyValue) Deferred(com.stumbleupon.async.Deferred) ArrayList(java.util.ArrayList) Callback(com.stumbleupon.async.Callback) DeleteRequest(org.hbase.async.DeleteRequest)

Aggregations

Deferred (com.stumbleupon.async.Deferred)40 ArrayList (java.util.ArrayList)33 Callback (com.stumbleupon.async.Callback)29 IOException (java.io.IOException)14 KeyValue (org.hbase.async.KeyValue)14 HashMap (java.util.HashMap)12 Map (java.util.Map)11 Scanner (org.hbase.async.Scanner)11 HBaseException (org.hbase.async.HBaseException)9 DeferredGroupException (com.stumbleupon.async.DeferredGroupException)8 List (java.util.List)8 NoSuchUniqueName (net.opentsdb.uid.NoSuchUniqueName)8 DeleteRequest (org.hbase.async.DeleteRequest)6 TSSubQuery (net.opentsdb.core.TSSubQuery)5 Annotation (net.opentsdb.meta.Annotation)5 NoSuchUniqueId (net.opentsdb.uid.NoSuchUniqueId)5 JSONException (net.opentsdb.utils.JSONException)5 InvocationOnMock (org.mockito.invocation.InvocationOnMock)5 DataPoints (net.opentsdb.core.DataPoints)4 IncomingDataPoint (net.opentsdb.core.IncomingDataPoint)4