feat(#92): grouped future waits (awaitAll/onReady) and range read-ahead - #111
Merged
Conversation
- Future::awaitAll() resolves N futures with one grouped wait: all requests are already in flight, so total latency is the slowest future instead of the sum of all of them. Results and errors are resolved in input order. - Future::onReady() registers a completion hook that always fires on the PHP thread (never on the FDB network thread), once, just before the future's payload is read. - RangeResult pagination now prefetches the next page before the consumer finishes the current one (page N+1's round trip overlaps page N), via the new KvsFuture abstraction; FutureKeyValueArray and FutureKvResult both implement it so existing fetchers keep working. - The fdb_future_set_callback binding itself is deliberately deferred: PHP code cannot safely run on the FDB network thread, and grouped waits plus read-ahead recover most of the practical parallelism without it. - Documented in docs/advanced.md; covered by FutureAwaitAllTest and new read-ahead tests in RangeResultTest.
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #92
What
Future::awaitAll(array $futures): array— awaits N futures as a group. All requests are already in flight, so total wait is driven by the slowest future (roughly one round trip) instead of the sum of N round trips. Implemented by polling the non-blockingfdb_future_is_ready()for every future at once (1 ms spin), then resolving results in input order — so errors still throw fromawait()in input order, and a failure in an early future never cancels the wait for the rest. Keys of the input array are preserved.Future::onReady(callable $fn): void— registers a completion hook that fires exactly once, just before the future's payload is read. Hooks always execute on the PHP thread that resolved the future — never on the FDB network thread. Hooks registered after resolution fire immediately; exceptions propagate to the caller.Range read-ahead —
RangeResult::paginate()now issues the request for page N+1 before yielding the current page's rows, so page N+1's network round trip overlaps with the consumer processing page N.getIterator()no longer awaits inside its fetcher closure — it hands the un-awaited future to the paginator. NewFuture\KvsFutureinterface (await(): FutureKvResult) is implemented by bothFutureKeyValueArray(real FDB future) andFutureKvResult(already-resolved value), so existing test stubs and custom fetchers keep working unchanged.Deliberately deferred
The
fdb_future_set_callbackbinding itself. The callback fires on the FDB network thread, where PHP code cannot safely run (the Zend engine is not thread-safe), and a correct design needs a C-level self-pipe/flag + PHP-side pump. Grouped waits + read-ahead recover most of the practical parallelism without it; the docs state this limit explicitly. The issue itself flagged this as the risky part and suggested landing the read-ahead first.Tests
tests/Unit/FutureAwaitAllTest.php(new): compiles a stub C library (same technique asFutureUInt64Test) where each future becomes ready only after N polls, and records the global poll count at first await — proving all futures are resolved only after the slowest one is ready (grouped wait, not serialized). Also covers input-order/keyed results, already-resolved futures, and allonReady()semantics.tests/Unit/RangeResultTest.php: newnextBatchIsPrefetchedBeforeTheCurrentBatchIsConsumed(2 fetches already made at the first yielded row; exactly 10 round trips for 10 pages) andlimitIsNotExceededByPrefetching(prefetch never requests more than the remaining limit). The fake server now honors a positive limit like the real one.Docs / changelog
docs/advanced.md: new section under "Future Objects" coveringawaitAll(),onReady(), range read-ahead, and the limits of the current async model (no callback binding, no Fiber suspension yet).CHANGELOG.md:[#92]entry under Added.