Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ jobs:
fdbcli -C fdb.cluster --exec "status" || true
exit 1

- name: Configure tenant mode
run: |
fdbcli -C fdb.cluster --exec "configure tenant_mode=optional_experimental"

- name: Run E2E tests
env:
FDB_CLUSTER_FILE: fdb.cluster
Expand Down
15 changes: 15 additions & 0 deletions src/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ public function createTransaction(): Transaction
return new Transaction($trPointer, $this, $this->client);
}

public function openTenant(string $name): Tenant
{
$tpointer = $this->client->fdb->new('FDBTenant*');
$this->client->checkError(
$this->client->fdb->fdb_database_open_tenant(
$this->dpointer,
$name,
strlen($name),
FFI::addr($tpointer),
),
);

return new Tenant($tpointer, $this, $this->client);
}

public function transact(callable $fn): mixed
{
$tr = $this->createTransaction();
Expand Down
1 change: 1 addition & 0 deletions src/NativeClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ final class NativeClient

void fdb_tenant_destroy(FDBTenant* t);
fdb_error_t fdb_tenant_create_transaction(FDBTenant* t, FDBTransaction** out_transaction);
FDBFuture* fdb_tenant_get_id(FDBTenant* tenant);

void fdb_transaction_destroy(FDBTransaction* tr);
void fdb_transaction_cancel(FDBTransaction* tr);
Expand Down
11 changes: 11 additions & 0 deletions src/Tenant.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace CrazyGoat\FoundationDB;

use CrazyGoat\FoundationDB\Future\FutureInt64;
use FFI;
use FFI\CData;

Expand All @@ -26,6 +27,16 @@ public function createTransaction(): Transaction
return new Transaction($trPointer, $this->db, $this->client);
}

public function getId(): int
{
$future = new FutureInt64(
$this->client->fdb->fdb_tenant_get_id($this->tpointer),
$this->client,
);

return $future->await();
}

public function __destruct()
{
$this->client->fdb->fdb_tenant_destroy($this->tpointer);
Expand Down
138 changes: 138 additions & 0 deletions tests/Integration/TenantTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php

declare(strict_types=1);

namespace CrazyGoat\FoundationDB\Tests\Integration;

use CrazyGoat\FoundationDB\Database;
use CrazyGoat\FoundationDB\FoundationDB;
use CrazyGoat\FoundationDB\Tenant;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class TenantTest extends TestCase
{
private static bool $initialized = false;

private static Database $db;

protected function setUp(): void
{
if (!self::$initialized) {
FoundationDB::reset();
FoundationDB::apiVersion(730);
self::$db = FoundationDB::open();
self::$initialized = true;
}

$this->configureTenantMode();
}

#[Test]
public function openTenantReturnsTenantInstance(): void
{
$this->createTenantViaFdbcli('test_tenant_open');

try {
$tenant = self::$db->openTenant('test_tenant_open');
self::assertInstanceOf(Tenant::class, $tenant);
} finally {
$this->deleteTenantViaFdbcli('test_tenant_open');
}
}

#[Test]
public function tenantGetIdReturnsPositiveInteger(): void
{
$this->createTenantViaFdbcli('test_tenant_id');

try {
$tenant = self::$db->openTenant('test_tenant_id');
$id = $tenant->getId();
self::assertGreaterThan(0, $id);
} finally {
$this->deleteTenantViaFdbcli('test_tenant_id');
}
}

#[Test]
public function tenantGetIdReturnsSameIdForSameTenant(): void
{
$this->createTenantViaFdbcli('test_tenant_same_id');

try {
$tenant1 = self::$db->openTenant('test_tenant_same_id');
$tenant2 = self::$db->openTenant('test_tenant_same_id');
self::assertSame($tenant1->getId(), $tenant2->getId());
} finally {
$this->deleteTenantViaFdbcli('test_tenant_same_id');
}
}

#[Test]
public function tenantGetIdReturnsDifferentIdsForDifferentTenants(): void
{
$this->createTenantViaFdbcli('test_tenant_diff_a');
$this->createTenantViaFdbcli('test_tenant_diff_b');

try {
$tenantA = self::$db->openTenant('test_tenant_diff_a');
$tenantB = self::$db->openTenant('test_tenant_diff_b');
self::assertNotSame($tenantA->getId(), $tenantB->getId());
} finally {
$this->deleteTenantViaFdbcli('test_tenant_diff_a');
$this->deleteTenantViaFdbcli('test_tenant_diff_b');
}
}

#[Test]
public function tenantCanCreateTransactionAndPerformCrud(): void
{
$this->createTenantViaFdbcli('test_tenant_crud');

try {
$tenant = self::$db->openTenant('test_tenant_crud');
$tr = $tenant->createTransaction();
$tr->set('tenant_key', 'tenant_value');
$tr->commit()->await();

$tr2 = $tenant->createTransaction();
$value = $tr2->get('tenant_key')->await();
self::assertSame('tenant_value', $value);

$tr3 = $tenant->createTransaction();
$tr3->clear('tenant_key');
$tr3->commit()->await();
} finally {
$this->deleteTenantViaFdbcli('test_tenant_crud');
}
}

private function configureTenantMode(): void
{
$clusterFile = getenv('FDB_CLUSTER_FILE') ?: '/etc/foundationdb/fdb.cluster';
$output = (string) shell_exec(
"fdbcli -C {$clusterFile} --exec 'configure tenant_mode=optional_experimental' 2>&1",
);
if (!str_contains($output, 'committed') && !str_contains($output, 'already')) {
self::markTestSkipped('Could not configure tenant mode: ' . $output);
}
}

private function createTenantViaFdbcli(string $name): void
{
$clusterFile = getenv('FDB_CLUSTER_FILE') ?: '/etc/foundationdb/fdb.cluster';
$output = (string) shell_exec(
"fdbcli -C {$clusterFile} --exec 'createtenant {$name}' 2>&1",
);
if (!str_contains($output, 'created') && !str_contains($output, 'already exists')) {
self::markTestSkipped('Could not create tenant: ' . $output);
}
}

private function deleteTenantViaFdbcli(string $name): void
{
$clusterFile = getenv('FDB_CLUSTER_FILE') ?: '/etc/foundationdb/fdb.cluster';
shell_exec("fdbcli -C {$clusterFile} --exec 'deletetenant {$name}' 2>&1");
}
}
Loading