为了方便研究将tsdb-uid表的修改操作抽取出来,代码如下:
package net.opentsdb.tools;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import net.opentsdb.core.TSDB;
import net.opentsdb.uid.NoSuchUniqueName;
import net.opentsdb.utils.Config;
import org.hbase.async.DeleteRequest;
import org.hbase.async.GetRequest;
import org.hbase.async.HBaseClient;
import org.hbase.async.HBaseException;
import org.hbase.async.KeyValue;
import org.hbase.async.PutRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.stumbleupon.async.Callback;
import com.stumbleupon.async.Deferred;
public class TestTsdbuidModify {
private static final Logger LOG = LoggerFactory.getLogger(TestTsdbuidModify.class);
private static final Charset CHARSET = Charset.forName("ISO-8859-1");
private static final byte[] ID_FAMILY = toBytes("id");
private static final byte[] NAME_FAMILY = toBytes("name");
private static final short MAX_ATTEMPTS_PUT = 6;
private static final short INITIAL_EXP_BACKOFF_DELAY = 800;
private final HBaseClient client;
private final byte[] table;
private final byte[] kind;
private final short id_width;
private final ConcurrentHashMap name_cache = new ConcurrentHashMap();
private final ConcurrentHashMap id_cache = new ConcurrentHashMap();
private volatile int cache_hits;
private volatile int cache_misses;
public TestTsdbuidModify(final HBaseClient client, final byte[] table, final String kind, final int width) {
this.client = client;
this.table = table;
if (kind.isEmpty()) {
throw new IllegalArgumentException("Empty string as 'kind' argument!");
}
this.kind = toBytes(kind);
if (width < 1 || width > 8) {
throw new IllegalArgumentException("Invalid width: " + width);
}
this.id_width = (short) width;
}
public static void main(String[] args) throws Exception {
args = new String[] { "rename", "metrics", "a1", "m1" };
ArgP argp = new ArgP();
CliOptions.addCommon(argp);
CliOptions.addVerbose(argp);
argp.addOption("--idwidth", "N", "Number of bytes on which the UniqueId is encoded.");
argp.addOption("--ignore-case", "Ignore case distinctions when matching a regexp.");
argp.addOption("-i", "Short for --ignore-case.");
args = CliOptions.parse(argp, args);
final byte[] table = argp.get("--uidtable", "tsdb-uid").getBytes();
final short idwidth = (argp.has("--idwidth") ? Short.parseShort(argp.get("--idwidth")) : 3);
if (idwidth <= 0) {
System.exit(3);
}
final boolean ignorecase = argp.has("--ignore-case") || argp.has("-i");
Config config = CliOptions.getConfig(argp);
final TSDB tsdb = new TSDB(config);
tsdb.getClient().ensureTableExists(config.getString("tsd.storage.hbase.uid_table")).joinUninterruptibly();
argp = null;
int rc;
try {
rc = runCommand(tsdb, table, idwidth, ignorecase, args);
} finally {
try {
tsdb.getClient().shutdown().joinUninterruptibly();
LOG.info("Gracefully shutdown the TSD");
} catch (Exception e) {
LOG.error("Unexpected exception while shutting down", e);
rc = 42;
}
}
System.exit(rc);
}
private static int runCommand(final TSDB tsdb, final byte[] table, final short idwidth, final boolean ignorecase, final String[] args) {
/**
* 新增 assign metrics t1
*/
if (args[0].equals("assign")) {
}
/**
* 查找 grep t grep metrics t11
*/
if (args[0].equals("grep")) {
}
/**
* 修改
*/
if (args[0].equals("rename")) {
return rename(tsdb.getClient(), table, idwidth, args);
}
/**
* 删除
*/
if (args[0].equals("delete")) {
}
return 0;
}
private static int rename(final HBaseClient client, final byte[] table, final short idwidth, final String[] args) {
final String kind = args[1];
final String oldname = args[2];
final String newname = args[3];
final TestTsdbuidModify uid = new TestTsdbuidModify(client, table, kind, (int) idwidth);
try {
uid.rename(oldname, newname);
} catch (HBaseException e) {
LOG.error("error while processing renaming " + oldname + " to " + newname, e);
return 3;
} catch (NoSuchUniqueName e) {
LOG.error(e.getMessage());
return 1;
}
System.out.println(kind + ' ' + oldname + " -> " + newname);
return 0;
}
public void rename(final String oldname, final String newname) {
final byte[] row = getId(oldname);
// System.out.println(Arrays.toString(row));
// System.out.println(bytes2Long(row));
// System.exit(0);
{
byte[] id = null;
try {
id = getId(newname);
} catch (NoSuchUniqueName e) {
// OK, we don't want the new name to be assigned.
}
if (id != null) {
throw new IllegalArgumentException("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": new name already"
+ " assigned ID=" + Arrays.toString(id));
}
}
final byte[] newnameb = toBytes(newname);
// Update the reverse mapping first, so that if we die before updating
// the forward mapping we don't run the risk of "publishing" a
// partially assigned ID. The reverse mapping on its own is harmless
// but the forward mapping without reverse mapping is bad.
try {
final PutRequest reverse_mapping = new PutRequest(table, row, NAME_FAMILY, kind, newnameb);
hbasePutWithRetry(reverse_mapping, MAX_ATTEMPTS_PUT, INITIAL_EXP_BACKOFF_DELAY);
} catch (HBaseException e) {
LOG.error("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": Failed to update reverse" + " mapping for ID="
+ Arrays.toString(row), e);
throw e;
}
// Now create the new forward mapping.
try {
final PutRequest forward_mapping = new PutRequest(table, newnameb, ID_FAMILY, kind, row);
hbasePutWithRetry(forward_mapping, MAX_ATTEMPTS_PUT, INITIAL_EXP_BACKOFF_DELAY);
} catch (HBaseException e) {
LOG.error("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": Failed to create the"
+ " new forward mapping with ID=" + Arrays.toString(row), e);
throw e;
}
// Update cache.
addIdToCache(newname, row); // add new name -> ID
id_cache.put(fromBytes(row), newname); // update ID -> new name
name_cache.remove(oldname); // remove old name -> ID
// Delete the old forward mapping.
try {
final DeleteRequest old_forward_mapping = new DeleteRequest(table, toBytes(oldname), ID_FAMILY, kind);
client.delete(old_forward_mapping).joinUninterruptibly();
} catch (HBaseException e) {
LOG.error("When trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this + ": Failed to remove the"
+ " old forward mapping for ID=" + Arrays.toString(row), e);
throw e;
} catch (Exception e) {
final String msg = "Unexpected exception when trying rename(\"" + oldname + "\", \"" + newname + "\") on " + this
+ ": Failed to remove the" + " old forward mapping for ID=" + Arrays.toString(row);
LOG.error("WTF? " + msg, e);
throw new RuntimeException(msg, e);
}
// Success!
}
private void hbasePutWithRetry(final PutRequest put, short attempts, short wait) throws HBaseException {
// TO-DO(tsuna): Remove once this code is async.
put.setBufferable(false);
while (attempts-- > 0) {
try {
client.put(put).joinUninterruptibly();
return;
} catch (HBaseException e) {
if (attempts > 0) {
LOG.error("Put failed, attempts left=" + attempts + " (retrying in " + wait + " ms), put=" + put, e);
try {
Thread.sleep(wait);
} catch (InterruptedException ie) {
throw new RuntimeException("interrupted", ie);
}
wait *= 2;
} else {
throw e;
}
} catch (Exception e) {
LOG.error("WTF? Unexpected exception type, put=" + put, e);
}
}
throw new IllegalStateException("This code should never be reached!");
}
public byte[] getId(final String name) throws NoSuchUniqueName, HBaseException {
try {
return getIdAsync(name).joinUninterruptibly();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Should never be here", e);
}
}
public Deferred getIdAsync(final String name) {
LOG.debug("--先从缓冲区中找---");
/**
* 先从缓冲区中找
*/
final byte[] id = getIdFromCache(name);
if (id != null) {
cache_hits++;
return Deferred.fromResult(id);
}
cache_misses++;
class GetIdCB implements Callback {
public byte[] call(final byte[] id) {
LOG.debug("---处理2---对getIdFromHBase(name)查找结果进行处理:没找到抛出NoSuchUniqueName异常,找到将id,name放入缓存");
if (id == null) {
throw new NoSuchUniqueName(kind(), name);
}
if (id.length != id_width) {
throw new IllegalStateException("Found id.length = " + id.length + " which is != " + id_width + " required for '" + kind() + '\'');
}
addIdToCache(name, id);
addNameToCache(id, name);
return id;
}
}
/**
* 从HBase中查找 没有找到就抛出NoSuchUniqueName异常 找到的话就把(name,id)放入缓冲区 返回id
*/
LOG.debug("---查询2---利用name通过getIdFromHBase(name)查找标签是否存在");
Deferred d = getIdFromHBase(name).addCallback(new GetIdCB());
return d;
}
private byte[] getIdFromCache(final String name) {
return name_cache.get(name);
}
/**
* 查找表中是否已存在标签
*
* @param name
* @return
*/
private Deferred getIdFromHBase(final String name) {
return hbaseGet(toBytes(name), ID_FAMILY);
}
/**
* Returns the cell of the specified row key, using family:kind.
*/
private Deferred hbaseGet(final byte[] key, final byte[] family) {
final GetRequest get = new GetRequest(table, key);
// kind为列簇修饰符metrics
get.family(family).qualifier(kind);
class GetCB implements Callback> {
public byte[] call(final ArrayList row) {
LOG.debug("---处理3---对HBaseClient.get(get)方法进行回调处理");
if (row == null || row.isEmpty()) {
return null;
}
return row.get(0).value();
}
}
LOG.debug("---查询3---先利用HBaseClient.get(get)来在HBase中的tsdb-uid表中查找有没有行键这name.toByets()的行");
return client.get(get).addCallback(new GetCB());
}
private void addIdToCache(final String name, final byte[] id) {
byte[] found = name_cache.get(name);
if (found == null) {
found = name_cache.putIfAbsent(name, Arrays.copyOf(id, id.length));
}
if (found != null && !Arrays.equals(found, id)) {
throw new IllegalStateException("name=" + name + " => id=" + Arrays.toString(id) + ", already mapped to " + Arrays.toString(found));
}
}
private void addNameToCache(final byte[] id, final String name) {
final String key = fromBytes(id);
String found = id_cache.get(key);
if (found == null) {
found = id_cache.putIfAbsent(key, name);
}
if (found != null && !found.equals(name)) {
throw new IllegalStateException("id=" + Arrays.toString(id) + " => name=" + name + ", already mapped to " + found);
}
}
private static byte[] toBytes(final String s) {
return s.getBytes(CHARSET);
}
public String kind() {
return fromBytes(kind);
}
private static String fromBytes(final byte[] b) {
return new String(b, CHARSET);
}
public static long bytes2Long(byte[] byteNum) {
long num = 0;
for (int ix = 0; ix < 3; ++ix) {
num <<= 8;
num |= (byteNum[ix] & 0xff);
}
return num;
}
}