docs: public Deferred<Boolean> syncToStorage(final TSDB tsdb, 
        final boolean overwrite) {
      if (tree_id < 1 || tree_id > 65535) {
        throw new IllegalArgumentException("Invalid Tree ID");
      }
      
      // if there aren't any changes, save time and bandwidth by not writing to
      // storage
      boolean has_changes = false;
      for (Map.Entry<String, Boolean> entry : changed.entrySet()) {
        if (entry.getValue()) {
          has_changes = true;
          break;
        }
      }
      
      if (!has_changes) {
        LOG.trace(this + " does not have changes, skipping sync to storage");
        throw new IllegalStateException("No changes detected in the rule");
      }
      
      /**
       * Executes the CAS after retrieving existing rule from storage, if it
       * exists.
       */
      final class StoreCB implements Callback<Deferred<Boolean>, TreeRule> {  
        final TreeRule local_rule;
        
        public StoreCB(final TreeRule local_rule) {
          this.local_rule = local_rule;
        }
        
        /**
         * @return True if the CAS was successful, false if not
         */
        @Override
        public Deferred<Boolean> call(final TreeRule fetched_rule) {
          
          TreeRule stored_rule = fetched_rule;
          final byte[] original_rule = stored_rule == null ? new byte[0] :
            JSON.serializeToBytes(stored_rule);
          if (stored_rule == null) {
            stored_rule = local_rule;
          } else {
            if (!stored_rule.copyChanges(local_rule, overwrite)) {
              LOG.debug(this + " does not have changes, skipping sync to storage");
              throw new IllegalStateException("No changes detected in the rule");
            }
          }
          
          // reset the local change map so we don't keep writing on subsequent
          // requests
          initializeChangedMap();
          
          // validate before storing
          stored_rule.validateRule();
          
          final PutRequest put = new PutRequest(tsdb.treeTable(), 
              Tree.idToBytes(tree_id), Tree.TREE_FAMILY(), 
              getQualifier(level, order), JSON.serializeToBytes(stored_rule));
          return tsdb.getClient().compareAndSet(put, original_rule);
        }
        
      }
      
      // start the callback chain by fetching from storage
      return fetchRule(tsdb, tree_id, level, order)
        .addCallbackDeferring(new StoreCB(this));
    }