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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ vendor/
composer.lock
.phpunit.cache/
fdb.cluster
.tyci/
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
## [Unreleased]

### Added
- [#90] `ReadTransaction::getTotalCost()` and
`ReadTransaction::getTagThrottledDuration()` (backed by
`fdb_transaction_get_total_cost` and
`fdb_transaction_get_tag_throttled_duration`), exposing the transaction
introspection needed to reason about cost-based and tag throttling. The
latter resolves to a double, so `fdb_future_get_double` was bound through
the new `CrazyGoat\FoundationDB\Future\FutureDouble` future type. Both
methods are available on `Transaction` and `Snapshot`. Unit tests in
`tests/Unit/FutureDoubleTest.php`; integration tests in
`tests/Integration/TransactionIntrospectionTest.php`;
`docs/transactions.md` updated.
- [#98] Bound `fdb_future_get_bool`, the result accessor for boolean-resolving
futures, through the new `CrazyGoat\FoundationDB\Future\FutureBool` future
type (a prerequisite for the blob granule API). Also added
Expand Down
6 changes: 6 additions & 0 deletions docs/transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ $size = $tr->getApproximateSize(): FutureInt64;

// Get the versionstamp (after commit) — returns the versionstamp key
$versionstamp = $tr->getVersionstamp(): FutureKey;

// Get the transaction's accumulated cost (cluster cost units)
$cost = $tr->getTotalCost(): FutureInt64; // available on Transaction and Snapshot

// Seconds this transaction has been throttled by tag throttling
$duration = $tr->getTagThrottledDuration(): FutureDouble; // available on Transaction and Snapshot
```

---
Expand Down
32 changes: 32 additions & 0 deletions src/Future/FutureDouble.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace CrazyGoat\FoundationDB\Future;

use FFI;

final class FutureDouble extends Future
{
private float $cachedResult = 0.0;

public function await(): float
{
if ($this->resolved) {
return $this->cachedResult;
}

$this->blockUntilReady();

$out = $this->client->fdb->new('double');
$this->client->checkError(
$this->client->fdb->fdb_future_get_double($this->fpointer, FFI::addr($out)),
);

$this->cachedResult = $out->cdata;
$this->releaseMemory();
$this->resolved = true;

return $this->cachedResult;
}
}
3 changes: 3 additions & 0 deletions src/NativeClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ final class NativeClient
fdb_bool_t fdb_future_is_ready(FDBFuture* f);
fdb_error_t fdb_future_get_error(FDBFuture* f);
fdb_error_t fdb_future_get_int64(FDBFuture* f, int64_t* out);
fdb_error_t fdb_future_get_double(FDBFuture* f, double* out);
fdb_error_t fdb_future_get_bool(FDBFuture* f, fdb_bool_t* out);
fdb_error_t fdb_future_get_key(FDBFuture* f, const char** out_key, int* out_key_length);
fdb_error_t fdb_future_get_value(
Expand Down Expand Up @@ -135,6 +136,8 @@ final class NativeClient
FDBFuture* fdb_transaction_commit(FDBTransaction* tr);
fdb_error_t fdb_transaction_get_committed_version(FDBTransaction* tr, int64_t* version);
FDBFuture* fdb_transaction_get_approximate_size(FDBTransaction* tr);
FDBFuture* fdb_transaction_get_total_cost(FDBTransaction* tr);
FDBFuture* fdb_transaction_get_tag_throttled_duration(FDBTransaction* tr);
FDBFuture* fdb_transaction_get_versionstamp(FDBTransaction* tr);
FDBFuture* fdb_transaction_watch(FDBTransaction* tr, const char* key_name, int key_name_length);
FDBFuture* fdb_transaction_on_error(FDBTransaction* tr, fdb_error_t error);
Expand Down
26 changes: 26 additions & 0 deletions src/ReadTransaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace CrazyGoat\FoundationDB;

use CrazyGoat\FoundationDB\Enum\StreamingMode;
use CrazyGoat\FoundationDB\Future\FutureDouble;
use CrazyGoat\FoundationDB\Future\FutureInt64;
use CrazyGoat\FoundationDB\Future\FutureKey;
use CrazyGoat\FoundationDB\Future\FutureKeyArray;
Expand Down Expand Up @@ -63,6 +64,31 @@ public function getReadVersion(): FutureInt64
);
}

/**
* Total accumulated cost of the transaction so far, in the cluster's
* cost units (as used by cost-based throttling and the 10,000,000-unit
* per-transaction cost limit).
*/
public function getTotalCost(): FutureInt64
{
return new FutureInt64(
$this->client->fdb->fdb_transaction_get_total_cost($this->tpointer),
$this->client,
);
}

/**
* Number of seconds this transaction has been throttled due to tag
* throttling so far.
*/
public function getTagThrottledDuration(): FutureDouble
{
return new FutureDouble(
$this->client->fdb->fdb_transaction_get_tag_throttled_duration($this->tpointer),
$this->client,
);
}

public function getEstimatedRangeSizeBytes(string $begin, string $end): FutureInt64
{
$beginLength = KeyValueLimits::assertValidRangeEndpoint($begin);
Expand Down
76 changes: 76 additions & 0 deletions tests/Integration/TransactionIntrospectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace CrazyGoat\FoundationDB\Tests\Integration;

use CrazyGoat\FoundationDB\Transaction;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

/**
* Integration coverage for the transaction introspection calls bound in
* issue #90: `getTotalCost()` and `getTagThrottledDuration()`.
*
* On an unthrottled single-client cluster the throttled duration must be
* 0.0; the total cost must be a non-negative integer that grows once the
* transaction has performed writes (each write carries a per-key cost in
* the cluster's cost model).
*/
final class TransactionIntrospectionTest extends TestCase
{
use DatabaseCleanupTrait;

#[Test]
public function totalCostIsNonNegativeAndGrowsWithWrites(): void
{
$db = $this->getDatabase();

$db->transact(static function (Transaction $tr): void {
$before = $tr->getTotalCost()->await();
self::assertGreaterThanOrEqual(0, $before);

for ($i = 0; $i < 10; $i++) {
$tr->set("test/introspection/key$i", str_repeat('v', 100));
}

$after = $tr->getTotalCost()->await();
self::assertGreaterThanOrEqual($before, $after);
});
}

#[Test]
public function totalCostIsAvailableOnSnapshots(): void
{
$db = $this->getDatabase();

$cost = $db->transact(
static fn (Transaction $tr): int => $tr->snapshot()->getTotalCost()->await(),
);

self::assertGreaterThanOrEqual(0, $cost);
}

#[Test]
public function tagThrottledDurationIsZeroOnAnUnthrottledCluster(): void
{
$db = $this->getDatabase();

$db->transact(static function (Transaction $tr): void {
$tr->set('test/introspection/throttle', 'value');
self::assertSame(0.0, $tr->getTagThrottledDuration()->await());
});
}

#[Test]
public function tagThrottledDurationIsAvailableOnSnapshots(): void
{
$db = $this->getDatabase();

$duration = $db->transact(
static fn (Transaction $tr): float => $tr->snapshot()->getTagThrottledDuration()->await(),
);

self::assertSame(0.0, $duration);
}
}
Loading
Loading