Description
fdb_future_set_callback is not bound in NativeClient.php, so every future in this library is strictly blocking: Future::await() calls fdb_future_block_until_ready() and the calling thread stops.
This is the largest structural gap against the other bindings. Java resolves futures into CompletableFuture and exposes AsyncIterable for ranges; Go resolves them on goroutines. In PHP today:
- there is no way to fan out N reads and wait for them together — the caller can construct several
FutureValue objects before awaiting, but nothing prefetches or waits on them as a group;
RangeResult fetches page N+1 only after page N has been fully consumed, with no read-ahead (Java's AsyncIterable overlaps them);
- the library cannot be driven from a Fiber-based event loop (Revolt/AMPHP/ReactPHP) or from Swoole without blocking the loop.
What needs to be done
-
Bind the callback entry point:
fdb_error_t fdb_future_set_callback(FDBFuture* f, FDBCallback callback, void* callback_parameter);
Note that the callback fires on an FDB network thread. With setCallbacksOnExternalThreads() disabled this is the client's network thread, and PHP code cannot safely run there — so the callback must only flip a flag/write to a self-pipe, with the actual resolution happening on the PHP thread.
-
Add to Future:
onReady(callable $fn): void — register a completion hook
- a non-blocking poll path built on the already-bound
fdb_future_is_ready
-
Add a group-await helper, e.g. Future::awaitAll(array $futures): array, that polls/waits across futures instead of serializing them.
-
Add read-ahead to RangeResult::getIterator(): issue the request for the next chunk before yielding the current chunk's last item.
-
Optional follow-up (separate issue if it grows): a Fiber-aware await() that suspends the current Fiber instead of blocking, so the library composes with Revolt-based event loops.
Notes
- Step 4 alone (read-ahead) delivers most of the practical win for typical range scans and does not require the callback binding — it can land first.
- Thread-safety at the FFI boundary is the risky part; a self-pipe or an atomic flag checked from PHP is the conservative design.
Acceptance Criteria
Description
fdb_future_set_callbackis not bound inNativeClient.php, so every future in this library is strictly blocking:Future::await()callsfdb_future_block_until_ready()and the calling thread stops.This is the largest structural gap against the other bindings. Java resolves futures into
CompletableFutureand exposesAsyncIterablefor ranges; Go resolves them on goroutines. In PHP today:FutureValueobjects before awaiting, but nothing prefetches or waits on them as a group;RangeResultfetches page N+1 only after page N has been fully consumed, with no read-ahead (Java'sAsyncIterableoverlaps them);What needs to be done
Bind the callback entry point:
Note that the callback fires on an FDB network thread. With
setCallbacksOnExternalThreads()disabled this is the client's network thread, and PHP code cannot safely run there — so the callback must only flip a flag/write to a self-pipe, with the actual resolution happening on the PHP thread.Add to
Future:onReady(callable $fn): void— register a completion hookfdb_future_is_readyAdd a group-await helper, e.g.
Future::awaitAll(array $futures): array, that polls/waits across futures instead of serializing them.Add read-ahead to
RangeResult::getIterator(): issue the request for the next chunk before yielding the current chunk's last item.Optional follow-up (separate issue if it grows): a Fiber-aware
await()that suspends the current Fiber instead of blocking, so the library composes with Revolt-based event loops.Notes
Acceptance Criteria
Future::awaitAll()resolves N futures with roughly one round trip's latency, not NRangeResultprefetches the next chunk while the current one is being consumeddocs/advanced.mddocuments the async model and its limitscomposer lintandcomposer testclean