or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

examples

edge-cases.mdreal-world-scenarios.md
index.md
tile.json

real-world-scenarios.mddocs/examples/

Real-World Scenarios

Common patterns and use cases for FoundationDB Java bindings.

User Management System

Store and retrieve user data with structured keys:

import com.apple.foundationdb.*;
import com.apple.foundationdb.subspace.Subspace;
import com.apple.foundationdb.tuple.Tuple;

Subspace users = new Subspace(Tuple.from("users"));

// Create user
db.run(tr -> {
    long userId = 1001L;
    byte[] profileKey = users.pack(Tuple.from(userId, "profile"));
    byte[] emailKey = users.pack(Tuple.from(userId, "email"));
    
    tr.set(profileKey, "Alice Smith".getBytes());
    tr.set(emailKey, "alice@example.com".getBytes());
    return null;
});

// Get user profile
String profile = db.run(tr -> {
    byte[] key = users.pack(Tuple.from(1001L, "profile"));
    byte[] value = tr.get(key).join();
    return value != null ? new String(value) : null;
});

// List all users
List<Long> userIds = db.read(tr -> {
    List<Long> result = new ArrayList<>();
    Range range = users.range(Tuple.from(1001L));
    
    for (KeyValue kv : tr.getRange(range)) {
        Tuple key = users.unpack(kv.getKey());
        if (key.size() == 2 && key.getString(1).equals("profile")) {
            result.add(key.getLong(0));
        }
    }
    return result;
});

Counter with Atomic Operations

Implement atomic counters:

import java.nio.ByteBuffer;

// Increment counter atomically
db.run(tr -> {
    byte[] key = "counter".getBytes();
    byte[] increment = ByteBuffer.allocate(8).putLong(1).array();
    tr.mutate(MutationType.ADD, key, increment);
    return null;
});

// Get counter value
long count = db.run(tr -> {
    byte[] value = tr.get("counter".getBytes()).join();
    return value != null ? ByteBuffer.wrap(value).getLong() : 0L;
});

Secondary Index Pattern

Create and query secondary indexes:

Subspace users = new Subspace(Tuple.from("users"));
Subspace emailIndex = new Subspace(Tuple.from("email_index"));

// Write user with index
db.run(tr -> {
    long userId = 1001L;
    String email = "alice@example.com";
    
    // Primary data
    byte[] userKey = users.pack(Tuple.from(userId));
    tr.set(userKey, "Alice".getBytes());
    
    // Index entry
    byte[] indexKey = emailIndex.pack(Tuple.from(email, userId));
    tr.set(indexKey, new byte[0]);
    
    return null;
});

// Query by email
Long userId = db.read(tr -> {
    Range emailRange = emailIndex.range(Tuple.from("alice@example.com"));
    
    for (KeyValue kv : tr.getRange(emailRange)) {
        Tuple indexKey = emailIndex.unpack(kv.getKey());
        return indexKey.getLong(1); // Return userId
    }
    return null;
});

Batch Operations

Process multiple operations efficiently:

// Batch write
db.run(tr -> {
    for (int i = 0; i < 1000; i++) {
        byte[] key = ("item:" + i).getBytes();
        byte[] value = ("value:" + i).getBytes();
        tr.set(key, value);
    }
    return null;
});

// Batch read
Map<String, String> results = db.read(tr -> {
    Map<String, String> map = new HashMap<>();
    
    List<CompletableFuture<byte[]>> futures = new ArrayList<>();
    List<String> keys = new ArrayList<>();
    
    for (int i = 0; i < 100; i++) {
        String keyStr = "item:" + i;
        keys.add(keyStr);
        futures.add(tr.get(keyStr.getBytes()));
    }
    
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    
    for (int i = 0; i < futures.size(); i++) {
        byte[] value = futures.get(i).join();
        if (value != null) {
            map.put(keys.get(i), new String(value));
        }
    }
    
    return map;
});

Multi-Tenant Application

Isolate data per tenant:

import com.apple.foundationdb.tuple.Tuple;

// Create tenant
TenantManagement.createTenant(db, "tenant1".getBytes()).join();

// Use tenant
Tenant tenant = db.openTenant("tenant1".getBytes());

tenant.run(tr -> {
    // Operations are isolated to this tenant
    tr.set("key".getBytes(), "value".getBytes());
    return null;
});

// Read from tenant
String value = tenant.read(tr -> {
    byte[] result = tr.get("key".getBytes()).join();
    return result != null ? new String(result) : null;
});

Async Operations

Use async operations for better concurrency:

// Async transaction
CompletableFuture<Void> future = db.runAsync(tr -> {
    return tr.get("key1".getBytes())
        .thenCompose(value1 -> {
            tr.set("key2".getBytes(), value1);
            return tr.get("key3".getBytes());
        })
        .thenCompose(value3 -> {
            tr.set("key4".getBytes(), value3);
            return tr.commit();
        });
});

future.thenRun(() -> System.out.println("Transaction completed"));

Range Queries with Pagination

Implement pagination for large datasets:

// Paginated range query
List<KeyValue> getPage(Database db, byte[] startKey, int pageSize) {
    return db.read(tr -> {
        KeySelector begin = KeySelector.firstGreaterOrEqual(startKey);
        KeySelector end = KeySelector.firstGreaterOrEqual("end:".getBytes());
        
        AsyncIterable<KeyValue> range = tr.getRange(
            begin, end, pageSize, false, StreamingMode.WANT_ALL
        );
        
        return range.asList().join();
    });
}

// Usage
byte[] lastKey = "start:".getBytes();
List<KeyValue> page = getPage(db, lastKey, 50);

Watch Pattern

Monitor keys for changes:

// Set up watch
CompletableFuture<Void> watch = db.run(tr -> {
    CompletableFuture<Void> w = tr.watch("config:refresh".getBytes());
    tr.commit().join();
    return w;
});

// Watch triggers when key changes
watch.thenRun(() -> {
    System.out.println("Configuration changed, reloading...");
    // Reload configuration
});

Transaction with Retry Logic

Manual transaction management with retry:

boolean success = false;
int retries = 0;
int maxRetries = 10;

while (!success && retries < maxRetries) {
    try (Transaction tr = db.createTransaction()) {
        // Perform operations
        tr.set("key".getBytes(), "value".getBytes());
        tr.commit().join();
        success = true;
    } catch (FDBException e) {
        if (e.isRetryable()) {
            retries++;
            Thread.sleep(100 * retries); // Exponential backoff
        } else {
            throw e;
        }
    }
}

Directory Layer for Dynamic Namespaces

Use directory layer for hierarchical organization:

import com.apple.foundationdb.directory.*;

DirectoryLayer dir = new DirectoryLayer();

// Create directory
DirectorySubspace appDir = dir.createOrOpen(
    db, Arrays.asList("myapp", "users")
).join();

// Use directory as subspace
db.run(tr -> {
    byte[] key = appDir.pack(Tuple.from("user123"));
    tr.set(key, "user data".getBytes());
    return null;
});