use of net.opentsdb.utils.JSONException 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));
}
use of net.opentsdb.utils.JSONException in project opentsdb by OpenTSDB.
the class AnnotationRpc method executeBulkUpdate.
/**
* Performs CRU methods on a list of annotation objects to reduce calls to
* the API. Only supports body content and adding or updating annotation
* objects. Deletions are separate.
* @param tsdb The TSD to which we belong
* @param method The request method
* @param query The query to parse and respond to
*/
void executeBulkUpdate(final TSDB tsdb, final HttpMethod method, HttpQuery query) {
final List<Annotation> notes;
try {
notes = query.serializer().parseAnnotationsV1();
} catch (IllegalArgumentException e) {
throw new BadRequestException(e);
} catch (JSONException e) {
throw new BadRequestException(e);
}
final List<Deferred<Annotation>> callbacks = new ArrayList<Deferred<Annotation>>(notes.size());
/**
* Storage callback used to determine if the storage call was successful
* or not. Also returns the updated object from storage.
*/
class SyncCB implements Callback<Deferred<Annotation>, Boolean> {
private final Annotation note;
public SyncCB(final Annotation note) {
this.note = note;
}
@Override
public Deferred<Annotation> call(Boolean success) throws Exception {
if (!success) {
throw new BadRequestException(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Failed to save an Annotation to storage", "This may be caused by another process modifying storage data: " + note);
}
return Annotation.getAnnotation(tsdb, note.getTSUID(), note.getStartTime());
}
}
/**
* Simple callback that will index the updated annotation
*/
class IndexCB implements Callback<Deferred<Annotation>, Annotation> {
@Override
public Deferred<Annotation> call(final Annotation note) throws Exception {
tsdb.indexAnnotation(note);
return Deferred.fromResult(note);
}
}
for (Annotation note : notes) {
try {
Deferred<Annotation> deferred = note.syncToStorage(tsdb, method == HttpMethod.PUT).addCallbackDeferring(new SyncCB(note));
Deferred<Annotation> indexer = deferred.addCallbackDeferring(new IndexCB());
callbacks.add(indexer);
} catch (IllegalStateException e) {
LOG.info("No changes for annotation: " + note);
} catch (IllegalArgumentException e) {
throw new BadRequestException(HttpResponseStatus.BAD_REQUEST, e.getMessage(), "Annotation error: " + note, e);
}
}
try {
// wait untill all of the syncs have completed, then rebuild the list
// of annotations using the data synced from storage.
Deferred.group(callbacks).joinUninterruptibly();
notes.clear();
for (Deferred<Annotation> note : callbacks) {
notes.add(note.joinUninterruptibly());
}
query.sendReply(query.serializer().formatAnnotationsV1(notes));
} catch (IllegalArgumentException e) {
throw new BadRequestException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
use of net.opentsdb.utils.JSONException 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));
}
use of net.opentsdb.utils.JSONException in project opentsdb by OpenTSDB.
the class Branch method fetchBranch.
/**
* Attempts to fetch the branch, it's leaves and all child branches.
* The UID names for each leaf may also be loaded if configured.
* @param tsdb The TSDB to use for storage access
* @param branch_id ID of the branch to retrieve
* @param load_leaf_uids Whether or not to load UID names for each leaf
* @return A branch if found, null if it did not exist
* @throws JSONException if the object could not be deserialized
*/
public static Deferred<Branch> fetchBranch(final TSDB tsdb, final byte[] branch_id, final boolean load_leaf_uids) {
final Deferred<Branch> result = new Deferred<Branch>();
final Scanner scanner = setupBranchScanner(tsdb, branch_id);
// This is the branch that will be loaded with data from the scanner and
// returned at the end of the process.
final Branch branch = new Branch();
// A list of deferreds to wait on for child leaf processing
final ArrayList<Deferred<Object>> leaf_group = new ArrayList<Deferred<Object>>();
/**
* Exception handler to catch leaves with an invalid UID name due to a
* possible deletion. This will allow the scanner to keep loading valid
* leaves and ignore problems. The fsck tool can be used to clean up
* orphaned leaves. If we catch something other than an NSU, it will
* re-throw the exception
*/
final class LeafErrBack implements Callback<Object, Exception> {
final byte[] qualifier;
public LeafErrBack(final byte[] qualifier) {
this.qualifier = qualifier;
}
@Override
public Object call(final Exception e) throws Exception {
Throwable ex = e;
while (ex.getClass().equals(DeferredGroupException.class)) {
ex = ex.getCause();
}
if (ex.getClass().equals(NoSuchUniqueId.class)) {
LOG.debug("Invalid UID for leaf: " + idToString(qualifier) + " in branch: " + idToString(branch_id), ex);
} else {
throw (Exception) ex;
}
return null;
}
}
/**
* Called after a leaf has been loaded successfully and adds the leaf
* to the branch's leaf set. Also lazily initializes the leaf set if it
* hasn't been.
*/
final class LeafCB implements Callback<Object, Leaf> {
@Override
public Object call(final Leaf leaf) throws Exception {
if (leaf != null) {
if (branch.leaves == null) {
branch.leaves = new HashMap<Integer, Leaf>();
}
branch.leaves.put(leaf.hashCode(), leaf);
}
return null;
}
}
/**
* Scanner callback executed recursively each time we get a set of data
* from storage. This is responsible for determining what columns are
* returned and issuing requests to load leaf objects.
* When the scanner returns a null set of rows, the method initiates the
* final callback.
*/
final class FetchBranchCB 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 branch if loaded successfully, null if the branch was not
* found.
*/
public Object fetchBranch() {
return scanner.nextRows().addCallback(this);
}
/**
* Loops through each row of the scanner results and parses out branch
* definitions and child leaves.
* @return The final branch callback if the scanner returns a null set
*/
@Override
public Object call(final ArrayList<ArrayList<KeyValue>> rows) throws Exception {
if (rows == null) {
if (branch.tree_id < 1 || branch.path == null) {
result.callback(null);
} else {
result.callback(branch);
}
return null;
}
for (final ArrayList<KeyValue> row : rows) {
for (KeyValue column : row) {
// matched a branch column
if (Bytes.equals(BRANCH_QUALIFIER, column.qualifier())) {
if (Bytes.equals(branch_id, column.key())) {
// it's *this* branch. We deserialize to a new object and copy
// since the columns could be in any order and we may get a
// leaf before the branch
final Branch local_branch = JSON.parseToObject(column.value(), Branch.class);
branch.path = local_branch.path;
branch.display_name = local_branch.display_name;
branch.tree_id = Tree.bytesToId(column.key());
} else {
// it's a child branch
final Branch child = JSON.parseToObject(column.value(), Branch.class);
child.tree_id = Tree.bytesToId(column.key());
branch.addChild(child);
}
// parse out a leaf
} else if (Bytes.memcmp(Leaf.LEAF_PREFIX(), column.qualifier(), 0, Leaf.LEAF_PREFIX().length) == 0) {
if (Bytes.equals(branch_id, column.key())) {
// process a leaf and skip if the UIDs for the TSUID can't be
// found. Add an errback to catch NoSuchUniqueId exceptions
leaf_group.add(Leaf.parseFromStorage(tsdb, column, load_leaf_uids).addCallbacks(new LeafCB(), new LeafErrBack(column.qualifier())));
} else {
// TODO - figure out an efficient way to increment a counter in
// the child branch with the # of leaves it has
}
}
}
}
// recursively call ourself to fetch more results from the scanner
return fetchBranch();
}
}
// start scanning
new FetchBranchCB().fetchBranch();
return result;
}
Aggregations