add python bindings and revise test code

This commit is contained in:
Jon Fu 2022-04-12 16:34:54 -04:00
parent 718119af83
commit c683795f6b
6 changed files with 190 additions and 11 deletions

91
TenantTest.java Normal file
View File

@ -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<KeyValue> 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<KeyValue> 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();
}
}

View File

@ -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<byte[]> listTenants(Database db, byte[] begin, byte[] end, int limit) {
public static CloseableAsyncIterator<KeyValue> listTenants(Database db, byte[] begin, byte[] end, int limit) {
return listTenants_internal(db.createTransaction(), begin, end, limit);
}
public static CloseableAsyncIterator<byte[]> 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<KeyValue> listTenants(Database db, Tuple begin, Tuple end, int limit) {
return listTenants_internal(db.createTransaction(), begin.pack(), end.pack(), limit);
}
private static CloseableAsyncIterator<byte[]> listTenants_internal(Transaction tr, byte[] begin, byte[] end,
private static CloseableAsyncIterator<KeyValue> 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<byte[]> {
static class TenantAsyncIterator implements CloseableAsyncIterator<KeyValue> {
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<byte[]> parts = Arrays.asList(tenant, value);
byte[] separator = ": ".getBytes();
byte[] result = ByteArrayUtil.join(separator, parts);
KeyValue result = new KeyValue(tenant, value);
return result;
}

View File

@ -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)

View File

@ -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)

View File

@ -980,7 +980,7 @@ ThreadFuture<MappedRangeResult> MultiVersionTransaction::getMappedRange(const Ke
auto tr = getTransaction();
auto f = tr.transaction ? tr.transaction->getMappedRange(begin, end, mapper, limits, snapshot, reverse)
: makeTimeout<MappedRangeResult>();
return abortableFuture(f, tr.onChange, cluster_version_changed());
return abortableFuture(f, tr.onChange);
}
ThreadFuture<Standalone<StringRef>> MultiVersionTransaction::getVersionstamp() {

33
test_tenant.py Executable file
View File

@ -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)