Search in sources :

Example 1 with PutRequest

use of org.hbase.async.PutRequest in project opentsdb by OpenTSDB.

the class IncomingDataPoints method addPointInternal.

/**
   * Implements {@link #addPoint} by storing a value with a specific flag.
   * 
   * @param timestamp
   *          The timestamp to associate with the value.
   * @param value
   *          The value to store.
   * @param flags
   *          Flags to store in the qualifier (size and type of the data point).
   * @return A deferred object that indicates the completion of the request.
   */
private Deferred<Object> addPointInternal(final long timestamp, final byte[] value, final short flags) {
    if (row == null) {
        throw new IllegalStateException("setSeries() never called!");
    }
    final boolean ms_timestamp = (timestamp & Const.SECOND_MASK) != 0;
    // we only accept unix epoch timestamps in seconds or milliseconds
    if (timestamp < 0 || (ms_timestamp && timestamp > 9999999999999L)) {
        throw new IllegalArgumentException((timestamp < 0 ? "negative " : "bad") + " timestamp=" + timestamp + " when trying to add value=" + Arrays.toString(value) + " to " + this);
    }
    // always maintain last_ts in milliseconds
    if ((ms_timestamp ? timestamp : timestamp * 1000) <= last_ts) {
        throw new IllegalArgumentException("New timestamp=" + timestamp + " is less than or equal to previous=" + last_ts + " when trying to add value=" + Arrays.toString(value) + " to " + this);
    }
    /** Callback executed for chaining filter calls to see if the value
     * should be written or not. */
    final class WriteCB implements Callback<Deferred<Object>, Boolean> {

        @Override
        public Deferred<Object> call(final Boolean allowed) throws Exception {
            if (!allowed) {
                return Deferred.fromResult(null);
            }
            last_ts = (ms_timestamp ? timestamp : timestamp * 1000);
            long base_time = baseTime();
            long incoming_base_time;
            if (ms_timestamp) {
                // drop the ms timestamp to seconds to calculate the base timestamp
                incoming_base_time = ((timestamp / 1000) - ((timestamp / 1000) % Const.MAX_TIMESPAN));
            } else {
                incoming_base_time = (timestamp - (timestamp % Const.MAX_TIMESPAN));
            }
            if (incoming_base_time - base_time >= Const.MAX_TIMESPAN) {
                // Need to start a new row as we've exceeded Const.MAX_TIMESPAN.
                base_time = updateBaseTime((ms_timestamp ? timestamp / 1000 : timestamp));
            }
            // Java is so stupid with its auto-promotion of int to float.
            final byte[] qualifier = Internal.buildQualifier(timestamp, flags);
            // TODO(tsuna): Add an errback to handle some error cases here.
            if (tsdb.getConfig().enable_appends()) {
                final AppendDataPoints kv = new AppendDataPoints(qualifier, value);
                final AppendRequest point = new AppendRequest(tsdb.table, row, TSDB.FAMILY, AppendDataPoints.APPEND_COLUMN_QUALIFIER, kv.getBytes());
                point.setDurable(!batch_import);
                return tsdb.client.append(point);
            /* .addBoth(cb) */
            } else {
                final PutRequest point = new PutRequest(tsdb.table, row, TSDB.FAMILY, qualifier, value);
                point.setDurable(!batch_import);
                return tsdb.client.put(point);
            }
        }

        @Override
        public String toString() {
            return "IncomingDataPoints.addPointInternal Write Callback";
        }
    }
    if (tsdb.getTSfilter() != null && tsdb.getTSfilter().filterDataPoints()) {
        return tsdb.getTSfilter().allowDataPoint(metric, timestamp, value, tags, flags).addCallbackDeferring(new WriteCB());
    }
    return Deferred.fromResult(true).addCallbackDeferring(new WriteCB());
}
Also used : Callback(com.stumbleupon.async.Callback) PutRequest(org.hbase.async.PutRequest) AppendRequest(org.hbase.async.AppendRequest)

Example 2 with PutRequest

use of org.hbase.async.PutRequest in project opentsdb by OpenTSDB.

the class Annotation method syncToStorage.

/**
   * Attempts a CompareAndSet storage call, loading the object from storage, 
   * synchronizing changes, and attempting a put.
   * <b>Note:</b> If the local object didn't have any fields set by the caller
   * or there weren't any changes, then the data will not be written and an 
   * exception will be thrown.
   * @param tsdb The TSDB to use for storage access
   * @param overwrite When the RPC method is PUT, will overwrite all user
   * accessible fields
   * 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 required data was missing such as the 
   * {@code #start_time}
   * @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 (start_time < 1) {
        throw new IllegalArgumentException("The start timestamp has not been set");
    }
    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 Annotation data");
    }
    final class StoreCB implements Callback<Deferred<Boolean>, Annotation> {

        @Override
        public Deferred<Boolean> call(final Annotation stored_note) throws Exception {
            final byte[] original_note = stored_note == null ? new byte[0] : stored_note.getStorageJSON();
            if (stored_note != null) {
                Annotation.this.syncNote(stored_note, overwrite);
            }
            final byte[] tsuid_byte = tsuid != null && !tsuid.isEmpty() ? UniqueId.stringToUid(tsuid) : null;
            final PutRequest put = new PutRequest(tsdb.dataTable(), getRowKey(start_time, tsuid_byte), FAMILY, getQualifier(start_time), Annotation.this.getStorageJSON());
            return tsdb.getClient().compareAndSet(put, original_note);
        }
    }
    if (tsuid != null && !tsuid.isEmpty()) {
        return getAnnotation(tsdb, UniqueId.stringToUid(tsuid), start_time).addCallbackDeferring(new StoreCB());
    }
    return getAnnotation(tsdb, start_time).addCallbackDeferring(new StoreCB());
}
Also used : Callback(com.stumbleupon.async.Callback) PutRequest(org.hbase.async.PutRequest) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap)

Example 3 with PutRequest

use of org.hbase.async.PutRequest in project opentsdb by OpenTSDB.

the class UidManager method fsck.

/**
   * Implements the {@code fsck} subcommand.
   * @param client The HBase client to use.
   * @param table The name of the HBase table to use.
   * @return The exit status of the command (0 means success).
   */
private static int fsck(final HBaseClient client, final byte[] table, final boolean fix, final boolean fix_unknowns) {
    if (fix) {
        LOG.info("----------------------------------");
        LOG.info("-    Running fsck in FIX mode    -");
        LOG.info("-      Remove Unknowns: " + fix_unknowns + "     -");
        LOG.info("----------------------------------");
    } else {
        LOG.info("Running in log only mode");
    }
    final class Uids {

        int errors;

        long maxid;

        long max_found_id;

        short width;

        final HashMap<String, String> id2name = new HashMap<String, String>();

        final HashMap<String, String> name2id = new HashMap<String, String>();

        void error(final KeyValue kv, final String msg) {
            error(msg + ".  kv=" + kv);
        }

        void error(final String msg) {
            LOG.error(msg);
            errors++;
        }

        /*
       * Replaces or creates the reverse map in storage and in the local map
       */
        void restoreReverseMap(final String kind, final String name, final String uid) {
            final PutRequest put = new PutRequest(table, UniqueId.stringToUid(uid), CliUtils.NAME_FAMILY, CliUtils.toBytes(kind), CliUtils.toBytes(name));
            client.put(put);
            id2name.put(uid, name);
            LOG.info("FIX: Restoring " + kind + " reverse mapping: " + uid + " -> " + name);
        }

        /*
       * Removes the reverse map from storage only
       */
        void removeReverseMap(final String kind, final String name, final String uid) {
            // clean up meta data too
            final byte[][] qualifiers = new byte[2][];
            qualifiers[0] = CliUtils.toBytes(kind);
            if (Bytes.equals(CliUtils.METRICS, qualifiers[0])) {
                qualifiers[1] = CliUtils.METRICS_META;
            } else if (Bytes.equals(CliUtils.TAGK, qualifiers[0])) {
                qualifiers[1] = CliUtils.TAGK_META;
            } else if (Bytes.equals(CliUtils.TAGV, qualifiers[0])) {
                qualifiers[1] = CliUtils.TAGV_META;
            }
            final DeleteRequest delete = new DeleteRequest(table, UniqueId.stringToUid(uid), CliUtils.NAME_FAMILY, qualifiers);
            client.delete(delete);
            // can't remove from the id2name map as this will be called while looping
            LOG.info("FIX: Removed " + kind + " reverse mapping: " + uid + " -> " + name);
        }
    }
    final long start_time = System.nanoTime();
    final HashMap<String, Uids> name2uids = new HashMap<String, Uids>();
    final Scanner scanner = client.newScanner(table);
    scanner.setMaxNumRows(1024);
    int kvcount = 0;
    try {
        ArrayList<ArrayList<KeyValue>> rows;
        while ((rows = scanner.nextRows().joinUninterruptibly()) != null) {
            for (final ArrayList<KeyValue> row : rows) {
                for (final KeyValue kv : row) {
                    kvcount++;
                    final byte[] qualifier = kv.qualifier();
                    // TODO - validate meta data in the future, for now skip it
                    if (Bytes.equals(qualifier, TSMeta.META_QUALIFIER()) || Bytes.equals(qualifier, TSMeta.COUNTER_QUALIFIER()) || Bytes.equals(qualifier, CliUtils.METRICS_META) || Bytes.equals(qualifier, CliUtils.TAGK_META) || Bytes.equals(qualifier, CliUtils.TAGV_META)) {
                        continue;
                    }
                    if (!Bytes.equals(qualifier, CliUtils.METRICS) && !Bytes.equals(qualifier, CliUtils.TAGK) && !Bytes.equals(qualifier, CliUtils.TAGV)) {
                        LOG.warn("Unknown qualifier " + UniqueId.uidToString(qualifier) + " in row " + UniqueId.uidToString(kv.key()));
                        if (fix && fix_unknowns) {
                            final DeleteRequest delete = new DeleteRequest(table, kv.key(), kv.family(), qualifier);
                            client.delete(delete);
                            LOG.info("FIX: Removed unknown qualifier " + UniqueId.uidToString(qualifier) + " in row " + UniqueId.uidToString(kv.key()));
                        }
                        continue;
                    }
                    final String kind = CliUtils.fromBytes(kv.qualifier());
                    Uids uids = name2uids.get(kind);
                    if (uids == null) {
                        uids = new Uids();
                        name2uids.put(kind, uids);
                    }
                    final byte[] key = kv.key();
                    final byte[] family = kv.family();
                    final byte[] value = kv.value();
                    if (Bytes.equals(key, CliUtils.MAXID_ROW)) {
                        if (value.length != 8) {
                            uids.error(kv, "Invalid maximum ID for " + kind + ": should be on 8 bytes: ");
                        // TODO - a fix would be to find the max used ID for the type 
                        // and store that in the max row.
                        } else {
                            uids.maxid = Bytes.getLong(value);
                            LOG.info("Maximum ID for " + kind + ": " + uids.maxid);
                        }
                    } else {
                        short idwidth = 0;
                        if (Bytes.equals(family, CliUtils.ID_FAMILY)) {
                            idwidth = (short) value.length;
                            final String skey = CliUtils.fromBytes(key);
                            final String svalue = UniqueId.uidToString(value);
                            final long max_found_id;
                            if (Bytes.equals(qualifier, CliUtils.METRICS)) {
                                max_found_id = UniqueId.uidToLong(value, TSDB.metrics_width());
                            } else if (Bytes.equals(qualifier, CliUtils.TAGK)) {
                                max_found_id = UniqueId.uidToLong(value, TSDB.tagk_width());
                            } else {
                                max_found_id = UniqueId.uidToLong(value, TSDB.tagv_width());
                            }
                            if (uids.max_found_id < max_found_id) {
                                uids.max_found_id = max_found_id;
                            }
                            final String id = uids.name2id.put(skey, svalue);
                            if (id != null) {
                                uids.error(kv, "Duplicate forward " + kind + " mapping: " + skey + " -> " + id + " and " + skey + " -> " + svalue);
                            }
                        } else if (Bytes.equals(family, CliUtils.NAME_FAMILY)) {
                            final String skey = UniqueId.uidToString(key);
                            final String svalue = CliUtils.fromBytes(value);
                            idwidth = (short) key.length;
                            final String name = uids.id2name.put(skey, svalue);
                            if (name != null) {
                                uids.error(kv, "Duplicate reverse " + kind + "  mapping: " + svalue + " -> " + name + " and " + svalue + " -> " + skey);
                            }
                        }
                        if (uids.width == 0) {
                            uids.width = idwidth;
                        } else if (uids.width != idwidth) {
                            uids.error(kv, "Invalid " + kind + " ID of length " + idwidth + " (expected: " + uids.width + ')');
                        }
                    }
                }
            }
        }
    } catch (HBaseException e) {
        LOG.error("Error while scanning HBase, scanner=" + scanner, e);
        throw e;
    } catch (Exception e) {
        LOG.error("WTF?  Unexpected exception type, scanner=" + scanner, e);
        throw new AssertionError("Should never happen");
    }
    // Match up all forward mappings with their reverse mappings and vice
    // versa and make sure they agree.
    int errors = 0;
    for (final Map.Entry<String, Uids> entry : name2uids.entrySet()) {
        final String kind = entry.getKey();
        final Uids uids = entry.getValue();
        // This will be used in the event that we run into an inconsistent forward
        // mapping that could mean a single UID was assigned to different names.
        // It SHOULD NEVER HAPPEN, but it could.
        HashMap<String, TreeSet<String>> uid_collisions = null;
        // These are harmful and shouldn't exist.
        for (final Map.Entry<String, String> nameid : uids.name2id.entrySet()) {
            final String name = nameid.getKey();
            final String id = nameid.getValue();
            final String found = uids.id2name.get(id);
            if (found == null) {
                uids.error("Forward " + kind + " mapping is missing reverse" + " mapping: " + name + " -> " + id);
                if (fix) {
                    uids.restoreReverseMap(kind, name, id);
                }
            } else if (!found.equals(name)) {
                uids.error("Forward " + kind + " mapping " + name + " -> " + id + " is different than reverse mapping: " + id + " -> " + found);
                final String id2 = uids.name2id.get(found);
                if (id2 != null) {
                    uids.error("Inconsistent forward " + kind + " mapping " + name + " -> " + id + " vs " + name + " -> " + found + " / " + found + " -> " + id2);
                    //    series.
                    if (fix) {
                        // once, as needed, since it's expensive.
                        if (uid_collisions == null) {
                            uid_collisions = new HashMap<String, TreeSet<String>>(uids.name2id.size());
                            for (final Map.Entry<String, String> row : uids.name2id.entrySet()) {
                                TreeSet<String> names = uid_collisions.get(row.getValue());
                                if (names == null) {
                                    names = new TreeSet<String>();
                                    uid_collisions.put(row.getValue(), names);
                                }
                                names.add(row.getKey());
                            }
                        }
                        // series *should* be OK and we can just fix the reverse map.
                        if (uid_collisions.containsKey(id) && uid_collisions.get(id).size() <= 1) {
                            uids.restoreReverseMap(kind, name, id);
                        }
                    }
                } else {
                    uids.error("Duplicate forward " + kind + " mapping " + name + " -> " + id + " and " + id2 + " -> " + found);
                    if (fix) {
                        uids.restoreReverseMap(kind, name, id);
                    }
                }
            }
        }
        // Scan through the UID collisions map and fix the screw ups
        if (uid_collisions != null) {
            for (Map.Entry<String, TreeSet<String>> collision : uid_collisions.entrySet()) {
                if (collision.getValue().size() <= 1) {
                    continue;
                }
                // The data in any time series with the errant UID is 
                // a mashup of with all of the names. The best thing to do is
                // start over. We'll rename the old time series so the user can
                // still see it if they want to, but delete the forward mappings
                // so that UIDs can be reassigned and clean series started.
                // - concatenate all of the names into 
                //   "fsck.<name1>.<name2>[...<nameN>]"
                // - delete the forward mappings for all of the names
                // - create a mapping with the fsck'd name pointing to the id
                final StringBuilder fsck_builder = new StringBuilder("fsck");
                final String id = collision.getKey();
                // compile the new fsck'd name and remove each of the duplicate keys
                for (String name : collision.getValue()) {
                    fsck_builder.append(".").append(name);
                    final DeleteRequest delete = new DeleteRequest(table, CliUtils.toBytes(name), CliUtils.ID_FAMILY, CliUtils.toBytes(kind));
                    client.delete(delete);
                    uids.name2id.remove(name);
                    LOG.info("FIX: Removed forward " + kind + " mapping for " + name + " -> " + id);
                }
                // write the new forward map
                final String fsck_name = fsck_builder.toString();
                final PutRequest put = new PutRequest(table, CliUtils.toBytes(fsck_name), CliUtils.ID_FAMILY, CliUtils.toBytes(kind), UniqueId.stringToUid(id));
                client.put(put);
                LOG.info("FIX: Created forward " + kind + " mapping for fsck'd UID " + fsck_name + " -> " + collision.getKey());
                // we still need to fix the uids map for the reverse run through below
                uids.name2id.put(fsck_name, collision.getKey());
                uids.restoreReverseMap(kind, fsck_name, id);
                LOG.error("----------------------------------");
                LOG.error("-     UID COLLISION DETECTED     -");
                LOG.error("Corrupted UID [" + collision.getKey() + "] renamed to [" + fsck_name + "]");
                LOG.error("----------------------------------");
            }
        }
        // These are harmless but shouldn't frequently occur.
        for (final Map.Entry<String, String> idname : uids.id2name.entrySet()) {
            final String name = idname.getValue();
            final String id = idname.getKey();
            final String found = uids.name2id.get(name);
            if (found == null) {
                LOG.warn("Reverse " + kind + " mapping is missing forward" + " mapping: " + name + " -> " + id);
                if (fix) {
                    uids.removeReverseMap(kind, name, id);
                }
            } else if (!found.equals(id)) {
                final String name2 = uids.id2name.get(found);
                if (name2 != null) {
                    uids.error("Inconsistent reverse " + kind + " mapping " + id + " -> " + name + " vs " + found + " -> " + name + " / " + name2 + " -> " + found);
                    if (fix) {
                        uids.removeReverseMap(kind, name, id);
                    }
                } else {
                    uids.error("Duplicate reverse " + kind + " mapping " + id + " -> " + name + " and " + found + " -> " + name2);
                    if (fix) {
                        uids.removeReverseMap(kind, name, id);
                    }
                }
            }
        }
        final int maxsize = Math.max(uids.id2name.size(), uids.name2id.size());
        if (uids.maxid > maxsize) {
            LOG.warn("Max ID for " + kind + " is " + uids.maxid + " but only " + maxsize + " entries were found.  Maybe " + (uids.maxid - maxsize) + " IDs were deleted?");
        } else if (uids.maxid < uids.max_found_id) {
            uids.error("We found an ID of " + uids.max_found_id + " for " + kind + " but the max ID is only " + uids.maxid + "!  Future IDs may be double-assigned!");
            if (fix) {
                // IDs than to under-run.
                if (uids.max_found_id == Long.MAX_VALUE) {
                    LOG.error("Ran out of UIDs for " + kind + ". Unable to fix max ID");
                } else {
                    final long diff = uids.max_found_id - uids.maxid;
                    final AtomicIncrementRequest air = new AtomicIncrementRequest(table, CliUtils.MAXID_ROW, CliUtils.ID_FAMILY, CliUtils.toBytes(kind), diff);
                    client.atomicIncrement(air);
                    LOG.info("FIX: Updated max ID for " + kind + " to " + uids.max_found_id);
                }
            }
        }
        if (uids.errors > 0) {
            LOG.error(kind + ": Found " + uids.errors + " errors.");
            errors += uids.errors;
        }
    }
    final long timing = (System.nanoTime() - start_time) / 1000000;
    LOG.info(kvcount + " KVs analyzed in " + timing + "ms (~" + (kvcount * 1000 / timing) + " KV/s)");
    if (errors == 0) {
        LOG.info("No errors found.");
        return 0;
    }
    LOG.warn(errors + " errors found.");
    return errors;
}
Also used : Scanner(org.hbase.async.Scanner) KeyValue(org.hbase.async.KeyValue) HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ArrayList(java.util.ArrayList) TreeSet(java.util.TreeSet) PutRequest(org.hbase.async.PutRequest) AtomicIncrementRequest(org.hbase.async.AtomicIncrementRequest) HBaseException(org.hbase.async.HBaseException) HBaseException(org.hbase.async.HBaseException) DeleteRequest(org.hbase.async.DeleteRequest) HashMap(java.util.HashMap) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 4 with PutRequest

use of org.hbase.async.PutRequest in project opentsdb by OpenTSDB.

the class Branch method storeBranch.

/**
   * Attempts to write the branch definition and optionally child leaves to
   * storage via CompareAndSets.
   * Each returned deferred will be a boolean regarding whether the CAS call 
   * was successful or not. This will be a mix of the branch call and leaves.
   * Some of these may be false, which is OK, because if the branch
   * definition already exists, we don't need to re-write it. Leaves will
   * return false if there was a collision.
   * @param tsdb The TSDB to use for access
   * @param tree The tree to record collisions to
   * @param store_leaves Whether or not child leaves should be written to
   * storage
   * @return A list of deferreds to wait on for completion.
   * @throws HBaseException if there was an issue
   * @throws IllegalArgumentException if the tree ID was missing or data was 
   * missing
   */
public Deferred<ArrayList<Boolean>> storeBranch(final TSDB tsdb, final Tree tree, final boolean store_leaves) {
    if (tree_id < 1 || tree_id > 65535) {
        throw new IllegalArgumentException("Missing or invalid tree ID");
    }
    final ArrayList<Deferred<Boolean>> storage_results = new ArrayList<Deferred<Boolean>>(leaves != null ? leaves.size() + 1 : 1);
    // compile the row key by making sure the display_name is in the path set
    // row ID = <treeID>[<parent.display_name.hashCode()>...]
    final byte[] row = this.compileBranchId();
    // compile the object for storage, this will toss exceptions if we are
    // missing anything important
    final byte[] storage_data = toStorageJson();
    final PutRequest put = new PutRequest(tsdb.treeTable(), row, Tree.TREE_FAMILY(), BRANCH_QUALIFIER, storage_data);
    put.setBufferable(true);
    storage_results.add(tsdb.getClient().compareAndSet(put, new byte[0]));
    // store leaves if told to and put the storage calls in our deferred group
    if (store_leaves && leaves != null && !leaves.isEmpty()) {
        for (final Leaf leaf : leaves.values()) {
            storage_results.add(leaf.storeLeaf(tsdb, row, tree));
        }
    }
    return Deferred.group(storage_results);
}
Also used : Deferred(com.stumbleupon.async.Deferred) ArrayList(java.util.ArrayList) PutRequest(org.hbase.async.PutRequest)

Example 5 with PutRequest

use of org.hbase.async.PutRequest in project opentsdb by OpenTSDB.

the class UIDMeta method syncToStorage.

/**
   * Attempts a CompareAndSet storage call, loading the object from storage, 
   * synchronizing changes, and attempting a put.
   * <b>Note:</b> If the local object didn't have any fields set by the caller
   * then the data will not be written.
   * @param tsdb The TSDB to use for storage access
   * @param overwrite When the RPC method is PUT, will overwrite all user
   * accessible fields
   * @return True if the storage call was successful, false if the object was
   * modified in storage during the CAS call. If false, retry the call. Other 
   * failures will result in an exception being thrown.
   * @throws HBaseException if there was an issue fetching
   * @throws IllegalArgumentException if parsing failed
   * @throws NoSuchUniqueId If the UID does not exist
   * @throws IllegalStateException if the data hasn't changed. This is OK!
   * @throws JSONException if the object could not be serialized
   */
public Deferred<Boolean> syncToStorage(final TSDB tsdb, final boolean overwrite) {
    if (uid == null || uid.isEmpty()) {
        throw new IllegalArgumentException("Missing UID");
    }
    if (type == null) {
        throw new IllegalArgumentException("Missing type");
    }
    boolean has_changes = false;
    for (Map.Entry<String, Boolean> entry : changed.entrySet()) {
        if (entry.getValue()) {
            has_changes = true;
            break;
        }
    }
    if (!has_changes) {
        LOG.debug(this + " does not have changes, skipping sync to storage");
        throw new IllegalStateException("No changes detected in UID meta data");
    }
    /**
     * Callback used to verify that the UID to name mapping exists. Uses the TSD
     * for verification so the name may be cached. If the name does not exist
     * it will throw a NoSuchUniqueId and the meta data will not be saved to
     * storage
     */
    final class NameCB implements Callback<Deferred<Boolean>, String> {

        private final UIDMeta local_meta;

        public NameCB(final UIDMeta meta) {
            local_meta = meta;
        }

        /**
       *  Nested callback used to merge and store the meta data after verifying
       *  that the UID mapping exists. It has to access the {@code local_meta} 
       *  object so that's why it's nested within the NameCB class
       */
        final class StoreUIDMeta implements Callback<Deferred<Boolean>, ArrayList<KeyValue>> {

            /**
         * Executes the CompareAndSet after merging changes
         * @return True if the CAS was successful, false if the stored data
         * was modified during flight.
         */
            @Override
            public Deferred<Boolean> call(final ArrayList<KeyValue> row) throws Exception {
                final UIDMeta stored_meta;
                if (row == null || row.isEmpty()) {
                    stored_meta = null;
                } else {
                    stored_meta = JSON.parseToObject(row.get(0).value(), UIDMeta.class);
                    stored_meta.initializeChangedMap();
                }
                final byte[] original_meta = row == null || row.isEmpty() ? new byte[0] : row.get(0).value();
                if (stored_meta != null) {
                    local_meta.syncMeta(stored_meta, overwrite);
                }
                // verify the name is set locally just to be safe
                if (name == null || name.isEmpty()) {
                    local_meta.name = name;
                }
                final PutRequest put = new PutRequest(tsdb.uidTable(), UniqueId.stringToUid(uid), FAMILY, (type.toString().toLowerCase() + "_meta").getBytes(CHARSET), local_meta.getStorageJSON());
                return tsdb.getClient().compareAndSet(put, original_meta);
            }
        }

        /**
       * NameCB method that fetches the object from storage for merging and
       * use in the CAS call
       * @return The results of the {@link #StoreUIDMeta} callback
       */
        @Override
        public Deferred<Boolean> call(final String name) throws Exception {
            final GetRequest get = new GetRequest(tsdb.uidTable(), UniqueId.stringToUid(uid));
            get.family(FAMILY);
            get.qualifier((type.toString().toLowerCase() + "_meta").getBytes(CHARSET));
            // #2 deferred
            return tsdb.getClient().get(get).addCallbackDeferring(new StoreUIDMeta());
        }
    }
    // start the callback chain by veryfing that the UID name mapping exists
    return tsdb.getUidName(type, UniqueId.stringToUid(uid)).addCallbackDeferring(new NameCB(this));
}
Also used : KeyValue(org.hbase.async.KeyValue) 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) GetRequest(org.hbase.async.GetRequest) HashMap(java.util.HashMap) Map(java.util.Map)

Aggregations

PutRequest (org.hbase.async.PutRequest)17 Callback (com.stumbleupon.async.Callback)11 HashMap (java.util.HashMap)10 Map (java.util.Map)8 IOException (java.io.IOException)5 TreeMap (java.util.TreeMap)5 Deferred (com.stumbleupon.async.Deferred)4 ArrayList (java.util.ArrayList)4 HBaseException (org.hbase.async.HBaseException)4 JSONException (net.opentsdb.utils.JSONException)2 AppendRequest (org.hbase.async.AppendRequest)2 DeleteRequest (org.hbase.async.DeleteRequest)2 KeyValue (org.hbase.async.KeyValue)2 ByteArrayByteIterator (com.yahoo.ycsb.ByteArrayByteIterator)1 ByteIterator (com.yahoo.ycsb.ByteIterator)1 DBException (com.yahoo.ycsb.DBException)1 BufferedReader (java.io.BufferedReader)1 TreeSet (java.util.TreeSet)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 Cell (net.opentsdb.core.Internal.Cell)1