Skip to content

Add non-blocking future support (fdb_future_set_callback, awaitAll, range read-ahead) #92

Description

@s2x

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

  1. 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.

  2. 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
  3. Add a group-await helper, e.g. Future::awaitAll(array $futures): array, that polls/waits across futures instead of serializing them.

  4. Add read-ahead to RangeResult::getIterator(): issue the request for the next chunk before yielding the current chunk's last item.

  5. 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

  • Future::awaitAll() resolves N futures with roughly one round trip's latency, not N
  • RangeResult prefetches the next chunk while the current one is being consumed
  • No PHP code executes on the FDB network thread
  • Benchmarks/tests demonstrating the parallel-read and read-ahead improvements
  • docs/advanced.md documents the async model and its limits
  • composer lint and composer test clean

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions