diff --git a/TenantTest.java b/TenantTest.java new file mode 100644 index 0000000000..d9bb02acdd --- /dev/null +++ b/TenantTest.java @@ -0,0 +1,91 @@ +import java.io.UnsupportedEncodingException; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ThreadLocalRandom; + +import com.apple.foundationdb.Database; +import com.apple.foundationdb.FDB; +import com.apple.foundationdb.KeyValue; +import com.apple.foundationdb.Tenant; +import com.apple.foundationdb.Transaction; +import com.apple.foundationdb.tuple.Tuple; +import com.apple.foundationdb.KeyArrayResult; +import com.apple.foundationdb.TenantManagement; +import com.apple.foundationdb.async.AsyncUtil; +import static com.apple.foundationdb.async.AsyncUtil.collectRemaining; +import com.apple.foundationdb.async.CloseableAsyncIterator; + +public class TenantTest { + private FDB fdb; + private Database db; + CloseableAsyncIterator tenants; + + public TenantTest() { + try { + fdb = FDB.selectAPIVersion(710); + fdb.options().setTraceEnable(null); + db = fdb.open(); +///* + Tuple t1 = Tuple.from("tenant"); + Tuple t2 = Tuple.from("tenant2"); + Tuple t3 = Tuple.from("tenant3"); +//*/ +/* + byte[] t1 = Tuple.from("tenant").pack(); + byte[] t2 = Tuple.from("tenant2").pack(); + byte[] t3 = Tuple.from("tenant3").pack(); +*/ + System.out.println(t1); + System.out.println(t2); + System.out.println(t3); + + TenantManagement.createTenant(db, t1).join(); + TenantManagement.createTenant(db, t2).join(); + TenantManagement.createTenant(db, t3).join(); + + tenants = TenantManagement.listTenants(db, Tuple.from("a").pack(), Tuple.from("z").pack(), 100); + + try { +/* + List result = AsyncUtil.collectRemaining(tenants).join(); + System.out.println("Size: " + result.size()); + for(int i = 0; i < result.size(); i++) { + System.out.println(i); + KeyValue res = result.get(i); + System.out.println(new String(res.getKey())); + System.out.println(new String(res.getValue())); + } +*/ +// /* + while (tenants.hasNext()) { + KeyValue res = tenants.next(); + System.out.println(new String(res.getKey())); + System.out.println(new String(res.getValue())); + } +// */ + } + finally { + tenants.close(); + } + TenantManagement.deleteTenant(db, t1).join(); + TenantManagement.deleteTenant(db, t2).join(); + TenantManagement.deleteTenant(db, t3).join(); + } + catch(Exception e) { + e.printStackTrace(); + } + } + + public void close() { + db.close(); + } + + public static void main(String[] args) { + new TenantTest().close(); + } +} + diff --git a/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java b/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java index 3ae786f464..d226d8c044 100644 --- a/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java +++ b/bindings/java/src/main/com/apple/foundationdb/TenantManagement.java @@ -219,23 +219,35 @@ public class TenantManagement { * @param begin The beginning of the range of tenants to list. * @param end The end of the range of the tenants to list. * @param limit The maximum number of tenants to return from this request. - * @return an iterator where each item is a byte array with the tenant name and value. + * @return an iterator where each item is a KeyValue object where the key is the tenant name + * and the value is the unprocessed JSON string containing the tenant's metadata */ - public static CloseableAsyncIterator listTenants(Database db, byte[] begin, byte[] end, int limit) { + public static CloseableAsyncIterator listTenants(Database db, byte[] begin, byte[] end, int limit) { return listTenants_internal(db.createTransaction(), begin, end, limit); } - public static CloseableAsyncIterator listTenants(Database db, Tuple begin, Tuple end, int limit) { + /** + * Lists all tenants in between the range specified. The number of tenants listed can be restricted. + * This is a convenience method that generates the begin and end ranges by packing two {@code Tuple}s. + * + * @param db The database used to create a transaction for listing the tenants. + * @param begin The beginning of the range of tenants to list. + * @param end The end of the range of the tenants to list. + * @param limit The maximum number of tenants to return from this request. + * @return an iterator where each item is a KeyValue object where the key is the tenant name + * and the value is the unprocessed JSON string containing the tenant's metadata + */ + public static CloseableAsyncIterator listTenants(Database db, Tuple begin, Tuple end, int limit) { return listTenants_internal(db.createTransaction(), begin.pack(), end.pack(), limit); } - private static CloseableAsyncIterator listTenants_internal(Transaction tr, byte[] begin, byte[] end, + private static CloseableAsyncIterator listTenants_internal(Transaction tr, byte[] begin, byte[] end, int limit) { return new TenantAsyncIterator(tr, begin, end, limit); } // Templates taken from BoundaryIterator LocalityUtil.java - static class TenantAsyncIterator implements CloseableAsyncIterator { + static class TenantAsyncIterator implements CloseableAsyncIterator { Transaction tr; final byte[] begin; final byte[] end; @@ -268,15 +280,12 @@ public class TenantManagement { return iter.hasNext(); } @Override - public byte[] next() { + public KeyValue next() { KeyValue kv = iter.next(); byte[] tenant = ByteArrayUtil.replace(kv.getKey(), 0, kv.getKey().length, TENANT_MAP_PREFIX, null); byte[] value = kv.getValue(); - List parts = Arrays.asList(tenant, value); - byte[] separator = ": ".getBytes(); - - byte[] result = ByteArrayUtil.join(separator, parts); + KeyValue result = new KeyValue(tenant, value); return result; } diff --git a/bindings/python/fdb/impl.py b/bindings/python/fdb/impl.py index 7c32b07e9b..51d67e5162 100644 --- a/bindings/python/fdb/impl.py +++ b/bindings/python/fdb/impl.py @@ -71,6 +71,11 @@ import types import struct +def remove_prefix(text, prefix): + if text.startswith(prefix): + return text[len(prefix):] + return text + def option_wrap(code): def setfunc(self): self._parent._set_option(code, None, 0) diff --git a/bindings/python/fdb/tenant_management.py b/bindings/python/fdb/tenant_management.py index b371a34226..3ee43326e4 100644 --- a/bindings/python/fdb/tenant_management.py +++ b/bindings/python/fdb/tenant_management.py @@ -78,6 +78,41 @@ def _delete_tenant_impl(tr, tenant_name, existence_check_marker, force_existence del tr[key] +class FDBTenantList(object): + """Iterates over the results of list_tenants query. Returns + KeyValue objects. + + """ + + def __init__(self, rangeresult): + self._range = rangeresult + self._iter = iter(self._range) + + def to_list(self): + return list(self.__iter__()) + + def __iter__(self, mode=None): + while True: + result = self._iter.__next__() + + tenant_name = _impl.remove_prefix(result.key, _tenant_map_prefix) + yield _impl.KeyValue(tenant_name, result.value) + +# Lists the tenants created in the cluster, specified by the begin and end range. +# Also limited in number of results by the limit parameter. +# Returns an iterable object that yields KeyValue objects +# where the keys are the tenant names and the values are the unprocessed +# JSON strings of the tenant metadata +@_impl.transactional +def _list_tenants_impl(tr, begin, end, limit): + tr.options.set_read_system_keys() + begin_key = b'%s%s' % (_tenant_map_prefix, begin) + end_key = b'%s%s' % (_tenant_map_prefix, end) + + rangeresult = tr.get_range(begin_key, end_key, limit) + + return FDBTenantList(rangeresult) + def create_tenant(db_or_tr, tenant_name): tenant_name = _impl.process_tenant_name(tenant_name) @@ -93,3 +128,9 @@ def delete_tenant(db_or_tr, tenant_name): # Callers using a transaction are expected to check existence themselves if required existence_check_marker = [] if not isinstance(db_or_tr, _impl.TransactionRead) else [None] _delete_tenant_impl(db_or_tr, tenant_name, existence_check_marker) + +def list_tenants(db_or_tr, begin, end, limit): + begin = _impl.process_tenant_name(begin) + end = _impl.process_tenant_name(end) + + return _list_tenants_impl(db_or_tr, begin, end, limit) \ No newline at end of file diff --git a/fdbclient/MultiVersionTransaction.actor.cpp b/fdbclient/MultiVersionTransaction.actor.cpp index 5db860b92e..20517fc264 100644 --- a/fdbclient/MultiVersionTransaction.actor.cpp +++ b/fdbclient/MultiVersionTransaction.actor.cpp @@ -980,7 +980,7 @@ ThreadFuture MultiVersionTransaction::getMappedRange(const Ke auto tr = getTransaction(); auto f = tr.transaction ? tr.transaction->getMappedRange(begin, end, mapper, limits, snapshot, reverse) : makeTimeout(); - return abortableFuture(f, tr.onChange, cluster_version_changed()); + return abortableFuture(f, tr.onChange); } ThreadFuture> MultiVersionTransaction::getVersionstamp() { diff --git a/test_tenant.py b/test_tenant.py new file mode 100755 index 0000000000..5f3dd9abd6 --- /dev/null +++ b/test_tenant.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +import fdb +import sys + +fdb.api_version(710) +db=fdb.open() + +db.options.set_transaction_timeout(2000) + +#tenant = b'tenant' +#tenant2 = b'tenant2' +#tenant3 = b'tenant3' + +tenant = (u"tenant",) +tenant2 = (u"tenant2",) +tenant3 = (u"tenant3",) + +fdb.tenant_management.create_tenant(db, tenant) +fdb.tenant_management.create_tenant(db, tenant2) +fdb.tenant_management.create_tenant(db, tenant3) + +res = fdb.tenant_management.list_tenants(db, (u"a",), (u"z",), 10) +#res = fdb.tenant_management.list_tenants(db, b'a', b'z', 10) +for t in res: + print(t.key.decode()) + print(t.value.decode()) + +fdb.tenant_management.delete_tenant(db, tenant) +fdb.tenant_management.delete_tenant(db, tenant2) +fdb.tenant_management.delete_tenant(db, tenant3) + +sys.exit(0)