Package com.tuladb

Class BufferedLedgerClient

java.lang.Object
com.tuladb.BufferedLedgerClient
All Implemented Interfaces:
Closeable, AutoCloseable

public class BufferedLedgerClient extends Object implements Closeable
Auto-batching wrapper: many concurrent callers, few round trips.

Callers keep making one-at-a-time calls (transfer, reserve, commit, rollback, their by-key and tiered forms, and rejects -- every kind of transfer is batched). Whatever is outstanding when a connection frees up is sent as a single batch frame, so the batch size is set by the offered load rather than by a guess -- the classic self-clocking arrangement, which needs no tuning to be right at both ends of the load range.

Two properties matter for throughput:

  • I/O happens outside the lock. The lock is held only to append to, or swap out, a queue, so producers keep filling the next batch while the current one is on the wire.
  • There is a persistent flusher, not a timer per batch.

Each operation type has its own queue, so commits batch with commits and do not queue behind transfers -- a 2PC workload batches on both legs.

Errors. One rule, shared by every TulaDB SDK: a queued item's failure (its own status, a refusal of its whole frame, a transport error) is thrown only to the caller who submitted that item. flush() and close() wait for those frames but never rethrow their errors.

Set pipelineDepth to keep several batch frames in flight per connection, and pass a LedgerClientPool as the target to get flushWorkers connections going at once. In-flight transfers are then bounded by flushWorkers × pipelineDepth × maxBatch. maxBatch (default and ceiling LedgerClient.TXN_BATCH_MAX) caps the items in one frame.

Everything else. Built over a single LedgerClient (the BufferedLedgerClient(LedgerClient) constructors or of(LedgerClient)), this is a complete client: reads, registry and admin calls, explicit batches and rejects first wait for what is queued, then run directly on that connection, so a read never observes less than what a caller has already been told was committed. Over a pool or a SmartLedgerClient those calls throw UnsupportedOperationException; make them on that client.


 try (BufferedLedgerClient buffered = new BufferedLedgerClient(new LedgerClient("127.0.0.1", 7700, 30_000))) {
     buffered.connect();
     TransferResult r = buffered.transfer(transfer);   // called from any thread
     Balance b = buffered.getBalance(key);             // flushes first, then reads
 }

 LedgerClientPool pool = new LedgerClientPool("ledger://n1:7700,n2:7700", 8, 30_000);
 try (BufferedLedgerClient buffered =
          new BufferedLedgerClient(BufferedLedgerClient.of(pool), 2.0, 4, 8)) {
     TransferResult r = buffered.transfer(transfer);   // called from any thread
 }