Motivation

Currently (in BookKeeper 4.5) we have several overloaded versions of the methods createLedger/createLedegerAdv/asyncCreateLeader/asyncCreateLeaderAdv. This methods are present because from version to version we added new configuration options for the Ledger.

In order to support new features in the future we need to have a more extensible API.

Proposed Change

So we will introduce a new set of APIs to construct Ledgers, in the future this pattern will be extended to other operations, like deleteLedger/closeLedger/addEntry.

This is how the client code will look like:

 BookKeeper bookeeper = ....;
 CompletableFuture<WriteHandler> future = bookkeeper.createLedger()
    .withEnsembleSize(3)
    .withWriteQuorumSize(2)
    .withAckQuorumSize(1)
    .withDigestType(DigestType.CRC32)                                       
    .withCustomMetadata(metadata)
    .withPassword(password)
    .apply();
IWriterHandler lh = future.get();

 CompletableFuture<WriteHandlerAdv> future = bookkeeper.createLedger()
    .withEnsembleSize(3)
    .withWriteQuorumSize(2)
    .withAckQuorumSize(1)
    .withDigestType(DigestType.CRC32)                                       
    .withCustomMetadata(metadata)
    .withPassword(password)
    .withLedgerId(ledgerId)
    .makeAdv()
    .apply();
IWriterHandlerAdv lh = future.get();
 

In order to achieve this goal we are going to introduce a ICreateBuilder and ICreateAdvBuilder interfaces

interface ICreateBuilder {
     ICreateBuilder withEnsembleSize(...);
     ICreateBuilder withWriteQuorumSize(...);
     ...
     ICreateAdvBuilder makeAdv();

     // old style callbacks
     void execute(CreateCallback callback, Object ctx);
     // support java8 completable future
     CompletableFuture<IWriteHandler> apply();
     // sync method
     IWriteHandler create() throws BKException, InterruptedException;
 }
interface ICreateAdvBuilder {
     // old style callbacks
     void execute(CreateCallback callback, Object ctx);
     // support java8 completable future
     CompletableFuture<IWriteHandlerAdv> apply();
     // sync method
     IWriteHandlerAdv create() throws BKException, InterruptedException;

}

And we will make CrateLedgerOp implement such interfaces.

We are going to introduce an IBookKeeper interface

interface IBookKeeper { 
      ICreateBuilder createLedger();
      IOpenBuilder   openLedger();
}

We are going to introduce IWriteHandler and IWriteHandlerAdv interfaces which contains only the API related to writing to a Ledger, following the LedgerHandler/LedgerHandlerAdv semantics.

In a similar way we are going to introduce a IReadHandler which contains only the API related to reading to a Ledger and a IOpenBuilder.

Please note that IWriteHandler and IWriteHandlerAdv will extend IReadHandler as writer need to be able to read its own writes.

A common ILedgerHandler interface will be a base interface for IReadHandler and it will contain basic operations like 'close'

 interface IOpenBuilder {
     IOpenBuilder withRecovery(boolean)
     IOpenBuilder withPassword(password) 
     IOpenBuilder withDigestType(digestType)
     CompleableFuture<IReadHandler> apply(long ledgerId)
     void open(long ledgerId, OpenCallback cb, Object ctx)
     IReadHandler open(long ledgerId)
}

interface ILedgerHandler {
     void close();
     void asyncClose();
}

interface IReadHandler extends ILedgerHandler {
     void readEntries();
     long readLastAddConfirmed();
     .....
     
}
interface IWriteHandler extends IReadHandler {
     void addEntry(byte[] data)
     void sync(...)
}
interface IWriteHandlerAdv extends IReadHandler {
     void addEntry(long entryID, byte[] data)
     void sync(...)
}

 

Migration Plan and Compatibility

No issue about migration and compatibility.

Legacy methods will be retained. No @Deprecated annotation will be added. Further removal of existing APIs will be evaluated in the future, maybe while starting a new major release, like 5.0.0

Further works

We can create similar API style for addEntry and other operations which need an extensible API.

Rejected Alternatives

This is an example of an alternative API which has been rejected, as it will not be really extensible in the future.

The rejected idea is about using the builder pattern for creating LedgerConfiguration structures and not directly ledgers

        BookKeeper bookeeper = ....;
        LedgerConfiguration ledgerConfiguration = LedgerConfiguration.builder()
                                                                              .ensembleSize(3)
                                                                              .writeQuorumSize(2)
                                                                              .ackQuorumSize(1)
                                                                              .digestType(DigestType.CRC32)                                       
																			  .customMetadata(Map<String, byte[]> metadata)
                                                                              .password(password)
                                                                              .advanced(true|false);
        LedgerHandle ledger = bookeeper.createLedger(ledgerConfiguration);