Skip to content

Commit

Permalink
feat(client): implement non-transactional writes
Browse files Browse the repository at this point in the history
  • Loading branch information
booniepepper committed Nov 17, 2023
1 parent 30e1124 commit 3cf7942
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public class ClientWriteRequest {
private List<ClientTupleKey> writes;
private List<ClientTupleKey> deletes;

public static ClientWriteRequest ofWrites(List<ClientTupleKey> writes) {
return new ClientWriteRequest().writes(writes);
}

public ClientWriteRequest writes(List<ClientTupleKey> writes) {
this.writes = writes;
return this;
Expand All @@ -27,6 +31,10 @@ public List<ClientTupleKey> getWrites() {
return writes;
}

public static ClientWriteRequest ofDeletes(List<ClientTupleKey> deletes) {
return new ClientWriteRequest().deletes(deletes);
}

public ClientWriteRequest deletes(List<ClientTupleKey> deletes) {
this.deletes = deletes;
return this;
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class OpenFgaClient {
private final ApiClient apiClient;
Expand Down Expand Up @@ -276,6 +277,15 @@ public CompletableFuture<ClientWriteResponse> write(ClientWriteRequest request,
configuration.assertValid();
String storeId = configuration.getStoreIdChecked();

if (options != null && options.enableTransactions()) {
return writeTransactions(storeId, request, options);
} else {
return writeOneTransaction(storeId, request, options);
}
}

private CompletableFuture<ClientWriteResponse> writeOneTransaction(
String storeId, ClientWriteRequest request, ClientWriteOptions options) {
WriteRequest body = new WriteRequest();

if (request != null) {
Expand All @@ -300,6 +310,44 @@ public CompletableFuture<ClientWriteResponse> write(ClientWriteRequest request,
return call(() -> api.write(storeId, body)).thenApply(ClientWriteResponse::new);
}

private CompletableFuture<ClientWriteResponse> writeTransactions(
String storeId, ClientWriteRequest request, ClientWriteOptions options) {

int chunkSize = options.getTransactionChunkSize();

var writeTransactions =
chunksOf(chunkSize, request.getWrites()).stream().map(ClientWriteRequest::ofWrites);
var deleteTransactions =
chunksOf(chunkSize, request.getDeletes()).stream().map(ClientWriteRequest::ofDeletes);

var transactions = Stream.concat(writeTransactions, deleteTransactions).collect(Collectors.toList());
var futureResponse = this.writeOneTransaction(storeId, transactions.get(0), options);

for (int i = 1; i < transactions.size(); i++) {
final int index = i; // Must be final in this scope for closure.

// The resulting completable future of this chain will result in either:
// 1. The first exception thrown in a failed completion. Other thenApply() will not be evaluated.
// 2. The final successful ClientWriteResponse.
futureResponse.thenApply(_response -> this.writeOneTransaction(storeId, transactions.get(index), options));
}

return futureResponse;
}

private <T> List<List<T>> chunksOf(int chunkSize, List<T> list) {
int nChunks = list.size() / chunkSize;

int finalEndExclusive = list.size();
List<List<T>> chunks = new ArrayList<>();

for (int i = 0; i < nChunks; i++) {
List<T> chunk = list.subList(i * chunkSize, Math.min((i + 1) * chunkSize, finalEndExclusive));
chunks.add(chunk);
}
return chunks;
}

/**
* WriteTuples - Utility method to write tuples, wraps Write
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

public class ClientWriteOptions {
private String authorizationModelId;
private Boolean enableTransactions;
private int transactionChunkSize;

public ClientWriteOptions authorizationModelId(String authorizationModelId) {
this.authorizationModelId = authorizationModelId;
Expand All @@ -23,4 +25,22 @@ public ClientWriteOptions authorizationModelId(String authorizationModelId) {
public String getAuthorizationModelId() {
return authorizationModelId;
}

public ClientWriteOptions enableTransactions(boolean enableTransactions) {
this.enableTransactions = enableTransactions;
return this;
}

public boolean enableTransactions() {
return enableTransactions != null && enableTransactions;
}

public ClientWriteOptions transactionChunkSize(int transactionChunkSize) {
this.transactionChunkSize = transactionChunkSize;
return this;
}

public int getTransactionChunkSize() {
return transactionChunkSize >= 0 ? transactionChunkSize : 1;
}
}

0 comments on commit 3cf7942

Please sign in to comment.