Move tenant creation/deletion into a TenantManagement class
This commit is contained in:
parent
6570ae44c6
commit
be7315473a
|
@ -66,6 +66,7 @@ set(JAVA_BINDING_SRCS
|
|||
src/main/com/apple/foundationdb/subspace/package-info.java
|
||||
src/main/com/apple/foundationdb/subspace/Subspace.java
|
||||
src/main/com/apple/foundationdb/Tenant.java
|
||||
src/main/com/apple/foundationdb/TenantManagement.java
|
||||
src/main/com/apple/foundationdb/Transaction.java
|
||||
src/main/com/apple/foundationdb/TransactionContext.java
|
||||
src/main/com/apple/foundationdb/EventKeeper.java
|
||||
|
|
|
@ -41,50 +41,6 @@ import com.apple.foundationdb.tuple.Tuple;
|
|||
* in use in order to free any associated resources.
|
||||
*/
|
||||
public interface Database extends AutoCloseable, TransactionContext {
|
||||
/**
|
||||
* Creates a new tenant in the cluster.
|
||||
*
|
||||
* @param tenantName The name of the tenant. Can be any byte string that does not begin a 0xFF byte.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been created.
|
||||
*/
|
||||
CompletableFuture<Void> allocateTenant(byte[] tenantName);
|
||||
|
||||
/**
|
||||
* Creates a new tenant in the cluster. This is a convenience method that generates the tenant name by packing a
|
||||
* {@code Tuple}.
|
||||
*
|
||||
* @param tenantName The name of the tenant, as a Tuple.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been created.
|
||||
*/
|
||||
CompletableFuture<Void> allocateTenant(Tuple tenantName);
|
||||
|
||||
/**
|
||||
* Deletes a tenant from the cluster.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> A tenant cannot be deleted if it has any data in it. To delete a non-empty tenant, you must
|
||||
* first use a clear operation to delete all of its keys.
|
||||
*
|
||||
* @param tenantName The name of the tenant being deleted.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been deleted.
|
||||
*/
|
||||
CompletableFuture<Void> deleteTenant(byte[] tenantName);
|
||||
|
||||
/**
|
||||
* Deletes a tenant from the cluster. This is a convenience method that generates the tenant name by packing a
|
||||
* {@code Tuple}.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> A tenant cannot be deleted if it has any data in it. To delete a non-empty
|
||||
* tenant, you must first use a clear operation to delete all of its keys.
|
||||
*
|
||||
* @param tenantName The name of the tenant being deleted, as a Tuple.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been deleted.
|
||||
*/
|
||||
CompletableFuture<Void> deleteTenant(Tuple tenantName);
|
||||
|
||||
/**
|
||||
* Opens an existing tenant to be used for running transactions.
|
||||
*
|
||||
|
|
|
@ -23,7 +23,6 @@ package com.apple.foundationdb;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -119,60 +118,6 @@ class FDBDatabase extends NativeObjectWrapper implements Database, OptionConsume
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> allocateTenant(byte[] tenantName) {
|
||||
final AtomicBoolean checkedExistence = new AtomicBoolean(false);
|
||||
final byte[] key = ByteArrayUtil.join(FDBTenant.TENANT_MAP_PREFIX, tenantName);
|
||||
return runAsync(tr -> {
|
||||
tr.options().setSpecialKeySpaceEnableWrites();
|
||||
if(checkedExistence.get()) {
|
||||
tr.set(key, new byte[0]);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
else {
|
||||
return tr.get(key).thenAcceptAsync(result -> {
|
||||
checkedExistence.set(true);
|
||||
if(result != null) {
|
||||
throw new FDBException("A tenant with the given name already exists", 2132);
|
||||
}
|
||||
tr.set(key, new byte[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> allocateTenant(Tuple tenantName) {
|
||||
return allocateTenant(tenantName.pack());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteTenant(byte[] tenantName) {
|
||||
final AtomicBoolean checkedExistence = new AtomicBoolean(false);
|
||||
final byte[] key = ByteArrayUtil.join(FDBTenant.TENANT_MAP_PREFIX, tenantName);
|
||||
return runAsync(tr -> {
|
||||
tr.options().setSpecialKeySpaceEnableWrites();
|
||||
if(checkedExistence.get()) {
|
||||
tr.clear(key);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
else {
|
||||
return tr.get(key).thenAcceptAsync(result -> {
|
||||
checkedExistence.set(true);
|
||||
if (result == null) {
|
||||
throw new FDBException("Tenant does not exist", 2131);
|
||||
}
|
||||
tr.clear(key);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteTenant(Tuple tenantName) {
|
||||
return deleteTenant(tenantName.pack());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tenant openTenant(byte[] tenantName, Executor e) {
|
||||
return openTenant(tenantName, e, eventKeeper);
|
||||
|
@ -265,8 +210,6 @@ class FDBDatabase extends NativeObjectWrapper implements Database, OptionConsume
|
|||
Database_dispose(cPtr);
|
||||
}
|
||||
|
||||
private native long Database_allocateTenant(long cPtr, byte[] tenantName);
|
||||
private native long Database_deleteTenant(long cPtr, byte[] tenantName);
|
||||
private native long Database_openTenant(long cPtr, byte[] tenantName);
|
||||
private native long Database_createTransaction(long cPtr);
|
||||
private native void Database_dispose(long cPtr);
|
||||
|
|
|
@ -35,8 +35,6 @@ class FDBTenant extends NativeObjectWrapper implements Tenant {
|
|||
private final Executor executor;
|
||||
private final EventKeeper eventKeeper;
|
||||
|
||||
static final byte[] TENANT_MAP_PREFIX = ByteArrayUtil.join(new byte[] { (byte)255, (byte)255 }, "/management/tenant_map/".getBytes());
|
||||
|
||||
protected FDBTenant(long cPtr, Database database, byte[] name, Executor executor) {
|
||||
this(cPtr, database, name, executor, null);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* TenantManagement.java
|
||||
*
|
||||
* This source file is part of the FoundationDB open source project
|
||||
*
|
||||
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.apple.foundationdb;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.apple.foundationdb.async.AsyncIterable;
|
||||
import com.apple.foundationdb.async.AsyncIterator;
|
||||
import com.apple.foundationdb.async.AsyncUtil;
|
||||
import com.apple.foundationdb.async.CloseableAsyncIterator;
|
||||
import com.apple.foundationdb.tuple.ByteArrayUtil;
|
||||
import com.apple.foundationdb.tuple.Tuple;
|
||||
|
||||
/**
|
||||
* The FoundationDB API includes function to manage the set of tenants in a cluster.
|
||||
*/
|
||||
public class TenantManagement {
|
||||
static final byte[] TENANT_MAP_PREFIX = ByteArrayUtil.join(new byte[] { (byte)255, (byte)255 },
|
||||
"/management/tenant_map/".getBytes());
|
||||
|
||||
/**
|
||||
* Creates a new tenant in the cluster. If the tenant already exists, this operation will complete
|
||||
* successfully without changing anything. The transaction must be committed for the creation to take
|
||||
* effect or to observe any errors.
|
||||
*
|
||||
* @param tr The transaction used to create the tenant.
|
||||
* @param tenantName The name of the tenant. Can be any byte string that does not begin a 0xFF byte.
|
||||
*/
|
||||
public static void createTenant(Transaction tr, byte[] tenantName) {
|
||||
tr.options().setSpecialKeySpaceEnableWrites();
|
||||
tr.set(ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName), new byte[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tenant in the cluster. If the tenant already exists, this operation will complete
|
||||
* successfully without changing anything. The transaction must be committed for the creation to take
|
||||
* effect or to observe any errors.<br>
|
||||
* <br>
|
||||
* This is a convenience method that generates the tenant name by packing a {@code Tuple}.
|
||||
*
|
||||
* @param tr The transaction used to create the tenant.
|
||||
* @param tenantName The name of the tenant, as a Tuple.
|
||||
*/
|
||||
public static void createTenant(Transaction tr, Tuple tenantName) {
|
||||
createTenant(tr, tenantName.pack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tenant in the cluster using a transaction created on the specified {@code Database}.
|
||||
* This operation will first check whether the tenant exists, and if it does it will set the
|
||||
* {@code CompletableFuture} to a tenant_already_exists error. Otherwise, it will attempt to create
|
||||
* the tenant in a retry loop. If the tenant is created concurrently by another transaction, this
|
||||
* function may still return successfully.
|
||||
*
|
||||
* @param db The database used to create a transaction for creating the tenant.
|
||||
* @param tenantName The name of the tenant. Can be any byte string that does not begin a 0xFF byte.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been created.
|
||||
*/
|
||||
public static CompletableFuture<Void> createTenant(Database db, byte[] tenantName) {
|
||||
final AtomicBoolean checkedExistence = new AtomicBoolean(false);
|
||||
final byte[] key = ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName);
|
||||
return db.runAsync(tr -> {
|
||||
tr.options().setSpecialKeySpaceEnableWrites();
|
||||
if(checkedExistence.get()) {
|
||||
tr.set(key, new byte[0]);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
else {
|
||||
return tr.get(key).thenAcceptAsync(result -> {
|
||||
checkedExistence.set(true);
|
||||
if(result != null) {
|
||||
throw new FDBException("A tenant with the given name already exists", 2132);
|
||||
}
|
||||
tr.set(key, new byte[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tenant in the cluster using a transaction created on the specified {@code Database}.
|
||||
* This operation will first check whether the tenant exists, and if it does it will set the
|
||||
* {@code CompletableFuture} to a tenant_already_exists error. Otherwise, it will attempt to create
|
||||
* the tenant in a retry loop. If the tenant is created concurrently by another transaction, this
|
||||
* function may still return successfully.<br>
|
||||
* <br>
|
||||
* This is a convenience method that generates the tenant name by packing a {@code Tuple}.
|
||||
*
|
||||
* @param db The database used to create a transaction for creating the tenant.
|
||||
* @param tenantName The name of the tenant, as a Tuple.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been created.
|
||||
*/
|
||||
public static CompletableFuture<Void> createTenant(Database db, Tuple tenantName) {
|
||||
return createTenant(db, tenantName.pack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tenant from the cluster. If the tenant does not exists, this operation will complete
|
||||
* successfully without changing anything. The transaction must be committed for the deletion to take
|
||||
* effect or to observe any errors.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> A tenant cannot be deleted if it has any data in it. To delete a non-empty tenant, you must
|
||||
* first use a clear operation to delete all of its keys.
|
||||
*
|
||||
* @param tr The transaction used to delete the tenant.
|
||||
* @param tenantName The name of the tenant being deleted.
|
||||
*/
|
||||
public static void deleteTenant(Transaction tr, byte[] tenantName) {
|
||||
tr.options().setSpecialKeySpaceEnableWrites();
|
||||
tr.clear(ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tenant from the cluster. If the tenant does not exists, this operation will complete
|
||||
* successfully without changing anything. The transaction must be committed for the deletion to take
|
||||
* effect or to observe any errors.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> A tenant cannot be deleted if it has any data in it. To delete a non-empty tenant, you must
|
||||
* first use a clear operation to delete all of its keys.<br>
|
||||
* <br>
|
||||
* This is a convenience method that generates the tenant name by packing a {@code Tuple}.
|
||||
*
|
||||
* @param tr The transaction used to delete the tenant.
|
||||
* @param tenantName The name of the tenant being deleted, as a Tuple.
|
||||
*/
|
||||
public static void deleteTenant(Transaction tr, Tuple tenantName) {
|
||||
deleteTenant(tr, tenantName.pack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tenant from the cluster using a transaction created on the specified {@code Database}. This
|
||||
* operation will first check whether the tenant exists, and if it does not it will set the
|
||||
* {@code CompletableFuture} to a tenant_not_found error. Otherwise, it will attempt to delete the
|
||||
* tenant in a retry loop. If the tenant is deleted concurrently by another transaction, this function may
|
||||
* still return successfully.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> A tenant cannot be deleted if it has any data in it. To delete a non-empty tenant, you must
|
||||
* first use a clear operation to delete all of its keys.
|
||||
*
|
||||
* @param db The database used to create a transaction for deleting the tenant.
|
||||
* @param tenantName The name of the tenant being deleted.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been deleted.
|
||||
*/
|
||||
public static CompletableFuture<Void> deleteTenant(Database db, byte[] tenantName) {
|
||||
final AtomicBoolean checkedExistence = new AtomicBoolean(false);
|
||||
final byte[] key = ByteArrayUtil.join(TENANT_MAP_PREFIX, tenantName);
|
||||
return db.runAsync(tr -> {
|
||||
tr.options().setSpecialKeySpaceEnableWrites();
|
||||
if(checkedExistence.get()) {
|
||||
tr.clear(key);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
else {
|
||||
return tr.get(key).thenAcceptAsync(result -> {
|
||||
checkedExistence.set(true);
|
||||
if(result == null) {
|
||||
throw new FDBException("Tenant does not exist", 2131);
|
||||
}
|
||||
tr.clear(key);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tenant from the cluster using a transaction created on the specified {@code Database}. This
|
||||
* operation will first check whether the tenant exists, and if it does not it will set the
|
||||
* {@code CompletableFuture} to a tenant_not_found error. Otherwise, it will attempt to delete the
|
||||
* tenant in a retry loop. If the tenant is deleted concurrently by another transaction, this function may
|
||||
* still return successfully.<br>
|
||||
* <br>
|
||||
* <b>Note:</b> A tenant cannot be deleted if it has any data in it. To delete a non-empty tenant, you must
|
||||
* first use a clear operation to delete all of its keys.<br>
|
||||
* <br>
|
||||
* This is a convenience method that generates the tenant name by packing a {@code Tuple}.
|
||||
*
|
||||
* @param db The database used to create a transaction for deleting the tenant.
|
||||
* @param tenantName The name of the tenant being deleted.
|
||||
* @return a {@code CompletableFuture} that when set without error will indicate that the tenant has
|
||||
* been deleted.
|
||||
*/
|
||||
public static CompletableFuture<Void> deleteTenant(Database db, Tuple tenantName) {
|
||||
return deleteTenant(db, tenantName.pack());
|
||||
}
|
||||
|
||||
private TenantManagement() {}
|
||||
}
|
|
@ -43,6 +43,7 @@ import com.apple.foundationdb.KeyArrayResult;
|
|||
import com.apple.foundationdb.MutationType;
|
||||
import com.apple.foundationdb.Range;
|
||||
import com.apple.foundationdb.StreamingMode;
|
||||
import com.apple.foundationdb.TenantManagement;
|
||||
import com.apple.foundationdb.Transaction;
|
||||
import com.apple.foundationdb.async.AsyncUtil;
|
||||
import com.apple.foundationdb.tuple.ByteArrayUtil;
|
||||
|
@ -473,13 +474,13 @@ public class AsyncStackTester {
|
|||
else if (op == StackOperation.TENANT_CREATE) {
|
||||
return inst.popParam().thenAcceptAsync(param -> {
|
||||
byte[] tenantName = (byte[])param;
|
||||
inst.push(inst.context.db.allocateTenant(tenantName));
|
||||
inst.push(TenantManagement.createTenant(inst.context.db, tenantName));
|
||||
}, FDB.DEFAULT_EXECUTOR);
|
||||
}
|
||||
else if (op == StackOperation.TENANT_DELETE) {
|
||||
return inst.popParam().thenAcceptAsync(param -> {
|
||||
byte[] tenantName = (byte[])param;
|
||||
inst.push(inst.context.db.deleteTenant(tenantName));
|
||||
inst.push(TenantManagement.deleteTenant(inst.context.db, tenantName));
|
||||
}, FDB.DEFAULT_EXECUTOR);
|
||||
}
|
||||
else if (op == StackOperation.TENANT_SET_ACTIVE) {
|
||||
|
|
|
@ -45,6 +45,7 @@ import com.apple.foundationdb.LocalityUtil;
|
|||
import com.apple.foundationdb.MutationType;
|
||||
import com.apple.foundationdb.Range;
|
||||
import com.apple.foundationdb.StreamingMode;
|
||||
import com.apple.foundationdb.TenantManagement;
|
||||
import com.apple.foundationdb.Transaction;
|
||||
import com.apple.foundationdb.async.AsyncIterable;
|
||||
import com.apple.foundationdb.async.AsyncUtil;
|
||||
|
@ -422,11 +423,11 @@ public class StackTester {
|
|||
}
|
||||
else if (op == StackOperation.TENANT_CREATE) {
|
||||
byte[] tenantName = (byte[])inst.popParam().join();
|
||||
inst.push(inst.context.db.allocateTenant(tenantName));
|
||||
inst.push(TenantManagement.createTenant(inst.context.db, tenantName));
|
||||
}
|
||||
else if (op == StackOperation.TENANT_DELETE) {
|
||||
byte[] tenantName = (byte[])inst.popParam().join();
|
||||
inst.push(inst.context.db.deleteTenant(tenantName));
|
||||
inst.push(TenantManagement.deleteTenant(inst.context.db, tenantName));
|
||||
}
|
||||
else if (op == StackOperation.TENANT_SET_ACTIVE) {
|
||||
byte[] tenantName = (byte[])inst.popParam().join();
|
||||
|
@ -761,7 +762,7 @@ public class StackTester {
|
|||
|
||||
private static void testTenantTupleNames(Database db) {
|
||||
try {
|
||||
db.allocateTenant(Tuple.from("tenant")).join();
|
||||
TenantManagement.createTenant(db, Tuple.from("tenant")).join();
|
||||
Tenant tenant = db.openTenant(Tuple.from("tenant"));
|
||||
|
||||
tenant.run(tr -> {
|
||||
|
@ -781,7 +782,7 @@ public class StackTester {
|
|||
return null;
|
||||
});
|
||||
|
||||
db.deleteTenant(Tuple.from("tenant")).join();
|
||||
TenantManagement.deleteTenant(db, Tuple.from("tenant")).join();
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
Loading…
Reference in New Issue