dgraph4j Java Client

repository·main·Indexed 41 days ago

https://github.com/dgraph-io/dgraph4j

A minimal Java client for Dgraph supporting synchronous (DgraphClient) and asynchronous (DgraphAsyncClient) communication via gRPC. Version 25.0.0 requires JDK 11 or higher. It provides features for managing transactions, mutations, queries in JSON and RDF formats, ACL authentication, and TLS configuration.

Tokens
7K
Snippets
28
Records
31
Agent score
49%

What's inside dgraph4j

  1. Manage Dgraph transactions

    main

    Dgraph supports two types of transactions:

    1. Read-Write Transactions: Created via newTransaction(). These allow both queries and mutations. It is best practice to call Transaction#discard() in a finally block to clean up resources.
    2. Read-Only Transactions: Created via newReadOnlyTransaction(). These can share read timestamps across multiple transactions for lower latency. They can also be set as setBestEffort(true) to relax linearizability requirements.

    Note: Calling discard() after a successful commit() is a no-op and is safe.

    // Read-write transaction pattern
    Transaction txn = dgraphClient.newTransaction();
    try {
        // ... operations
        txn.commit();
    } finally {
        txn.discard();
    }
    
    // Best-effort read-only transaction
    Transaction bestEffortTxn = dgraphClient.newReadOnlyTransaction()
        .setBestEffort(true);
  2. Avoid setting a global deadline on the DgraphStub

    main

    When using dgraph4j, be careful with the withDeadlineAfter method on the DgraphStub. If you apply a deadline to the stub itself, that deadline applies to the entire life of the stub, not to individual requests. This will cause all subsequent requests to fail once the initial deadline has passed.

    In the provided sample, the following pattern is used to demonstrate this behavior (which is generally undesirable for production):

    stub = stub.withDeadlineAfter(5, TimeUnit.SECONDS);

    When the deadline expires, you will encounter a java.util.concurrent.CompletionException caused by io.grpc.StatusRuntimeException: DEADLINE_EXCEEDED with the message: ClientCall started after deadline exceeded.

    stub = stub.withDeadlineAfter(5, TimeUnit.SECONDS);
  3. Understand Dgraph client styles: Synchronous vs Asynchronous

    main

    The library provides two primary client styles:

    1. DgraphClient (Synchronous): The standard blocking client for performing mutations and queries.
    2. DgraphAsyncClient (Asynchronous): A non-blocking client for asynchronous operations.

    Both client types are initialized by passing a list of DgraphBlockingStub clients. You can use the anyClient() API to randomly pick a stub for GRPC operations, which helps in distributing load across multiple Dgraph nodes.

  4. Use the Asynchronous Client with DgraphAsyncClient

    main

    The DgraphAsyncClient provides an asynchronous API for interacting with Dgraph. It functions similarly to the synchronous DgraphClient, but DgraphAsyncClient#newTransaction() returns an AsyncTransaction instead of a Transaction.

    Methods on AsyncTransaction do not return results directly; instead, they return a CompletableFuture<T>. This allows you to handle query and mutation results asynchronously using standard Java CompletableFuture patterns (e.g., .thenAccept(), .thenApply()).

    // Query
    String query =
    "query all($a: string){\n" +
    "  all(func: eq(name, $a)) {\n" +
    "    name\n" +
    "  }\n" +
    "}\n";
    
    Map<String, String> vars = Collections.singletonMap("$a", "Alice");
    
    AsyncTransaction txn = dgraphAsyncClient.newTransaction();
    txn.query(query).thenAccept(response -> {
        // Deserialize
        People ppl = gson.fromJson(res.getJson().toStringUtf8(), People.class);
    
        // Print results
        System.out.printf("people found: %d\n", ppl.all.size());
        ppl.all.forEach(person -> System.out.println(person.name));
    });
  5. Handle concurrent modifications with TxnConflictException

    main

    When multiple transactions attempt to update the same predicate on a node simultaneously, Dgraph allows the first transaction to succeed and throws an io.dgraph.TxnConflictException for the others.

    To handle these conflicts in a multi-threaded Java application, you should implement a retry mechanism. A common pattern is to catch the TxnConflictException, wait for a short duration (e.g., 1 second), and then retry the transaction. This allows the conflicting transaction to complete, after which the retried transaction can proceed successfully.

  6. Handle Dgraph exceptions and retries

    main

    All client exceptions extend DgraphException. Key properties include getStatus() (gRPC status) and isRetryable() (boolean indicating if the operation can be retried).

    Common Exception Types:

    • AlphaException (retryable): Server is reachable but temporarily unable to serve (e.g., AlphaNotReadyException, AlphaOverloadedException).
    • ConnectionException (retryable): Network-level issues.
    • TxnConflictException (retryable): Concurrent modification of the same data.
    • QueryException: Invalid syntax.
    • AuthException: Authentication/Authorization failure.

    Automatic Retry: Use client.withRetry(Callable) to execute operations with a managed transaction that automatically retries on retryable failures. You can use RetryPolicy.readOnly() or RetryPolicy.bestEffort() for specific transaction types.

    // Automatic retry with custom policy
    Response resp = client.withRetry(
        RetryPolicy.builder()
            .maxRetries(10)
            .baseDelay(Duration.ofMillis(200))
            .build(),
        txn -> {
            txn.mutate(mutation);
            txn.commit();
            return txn.query(query);
        }
    );
  7. Start a local Dgraph server for testing

    main

    To run the sample code, you must have Dgraph v1.1.0 or above installed and running. You can set up a clean environment by creating separate directories for dgraph zero and dgraph alpha (the server).

    Follow these steps to initialize the directories and start the services:

    1. Create data directories:

      mkdir -p dgraphdata/zero dgraphdata/data
    2. Start Dgraph Zero:

      cd dgraphdata/zero
      rm -rf zw; dgraph zero
    3. Start Dgraph Alpha:

      cd dgraphdata/data
      rm -rf p w; dgraph alpha --zero localhost:5080 -o 100

    Note on Ports: Using the -o 100 flag shifts the default ports by 100. The alpha server will bind to:

    • 7180 for internal traffic (default 7080)
    • 8180 for HTTP (default 8080)
    • 9180 for gRPC (default 9080)
    mkdir -p dgraphdata/zero dgraphdata/data
    cd dgraphdata/zero
    rm -rf zw; dgraph zero
    cd ../data
    rm -rf p w; dgraph alpha --zero localhost:5080 -o 100
  8. Publish artifacts locally for testing

    main

    If you need to test the publishing process from your local machine, you can publish to Sonatype by configuring your credentials in ~/.gradle/gradle.properties and running the Gradle publish task.

    1. Configure credentials

    Add the following properties to your ~/.gradle/gradle.properties file:

    ossrhUsername=<portal-token-username>
    ossrhPassword=<portal-token-password>
    
    signing.keyId=<gpg-key-id>
    signing.password=<gpg-passphrase>
    signing.secretKeyRingFile=</path/to/.gnupg/secring.gpg

    2. Run the publish command

    Execute the following command from the project root:

    ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
    ossrhUsername=<portal-token-username>
    ossrhPassword=<portal-token-password>
    
    signing.keyId=<gpg-key-id>
    signing.password=<gpg-passphrase>
    signing.secretKeyRingFile=</path/to/.gnupg/secring.gpg
    ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
  9. Format source code with google-java-format

    main

    The project uses google-java-format for code styling. Running ./gradlew build will issue warnings if the code is not conformant. To automatically format the source code before committing, use the goJF task.

    ./gradlew goJF
  10. Build the Dgraph4j source

    main

    To build the project from source using Gradle, run the following command.

    Warning: The gradle build runs integration tests against a locally running Dgraph server. These tests will remove all data from your Dgraph instance. Ensure you are not running this against a production or important data instance.

    If you have modified task.proto, the build process will automatically regenerate the source files using Protocol Buffer tools.

    ./gradlew build
  11. Run unit and integration tests

    main

    To run the test suite, use the ./gradlew test command.

    Warning: This command runs integration tests on a locally running Dgraph server, which will remove all data from your Dgraph instance.

    Prerequisite: You must have a Dgraph server running on localhost before executing this command.

    ./gradlew test
  12. Install dgraph4j via Maven or Gradle

    main

    To use the Dgraph Java client, add the following dependency to your build configuration. The current version is 25.0.0.

    ### Maven
    ```xml
    <dependency>
      <groupId>io.dgraph</groupId>
      <artifactId>dgraph4j</artifactId>
      <version>25.0.0</version>
    </dependency>

    Gradle

    compile 'io.dgraph:dgraph4j:25.0.0'