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

tuple-encoding.mddocs/reference/

Tuple Encoding

Type-safe, sortable key encoding that supports multiple data types while preserving lexicographic ordering. Tuples provide a structured way to encode keys and values for FoundationDB.

Capabilities

Tuple Creation

Create tuples from various data sources.

/**
 * Create tuple from variable arguments.
 * 
 * Parameters:
 * - items: Object... - Items to include in tuple
 * 
 * Returns:
 * Tuple - New tuple containing items
 */
static Tuple Tuple.from(Object... items);

/**
 * Create tuple from iterable collection.
 *
 * Parameters:
 * - items: Iterable<?> - Items to include in tuple
 *
 * Returns:
 * Tuple - New tuple containing items
 */
static Tuple Tuple.fromItems(Iterable<?> items);

/**
 * Create tuple from list.
 *
 * Parameters:
 * - items: List<?> - Items to include in tuple
 *
 * Returns:
 * Tuple - New tuple containing items
 */
static Tuple Tuple.fromList(List<?> items);

/**
 * Create tuple from stream.
 *
 * Parameters:
 * - items: Stream<?> - Items to include in tuple
 *
 * Returns:
 * Tuple - New tuple containing items
 */
static Tuple Tuple.fromStream(Stream<?> items);

/**
 * Unpack tuple from byte array.
 *
 * Parameters:
 * - bytes: byte[] - Packed tuple data
 *
 * Returns:
 * Tuple - Unpacked tuple
 */
static Tuple Tuple.fromBytes(byte[] bytes);

/**
 * Unpack tuple from byte array with offset.
 * 
 * Parameters:
 * - bytes: byte[] - Packed tuple data
 * - offset: int - Starting offset in array
 * - length: int - Number of bytes to read
 * 
 * Returns:
 * Tuple - Unpacked tuple
 */
static Tuple Tuple.fromBytes(byte[] bytes, int offset, int length);

Usage examples:

import com.apple.foundationdb.tuple.Tuple;
import java.util.UUID;

// Create from multiple items
Tuple tuple1 = Tuple.from("user", 1001, "Alice");

// Create from collection
List<Object> items = Arrays.asList("key", 42, true);
Tuple tuple2 = Tuple.fromItems(items);

// Unpack from bytes
byte[] packed = tuple1.pack();
Tuple unpacked = Tuple.fromBytes(packed);

// Empty tuple
Tuple empty = Tuple.from();

Adding Elements

Build tuples by adding typed elements (returns new tuple).

/**
 * Add untyped object to tuple.
 * Returns new tuple with object appended.
 * 
 * Parameters:
 * - o: Object - Object to add
 * 
 * Returns:
 * Tuple - New tuple with object appended
 */
Tuple Tuple.addObject(Object o);

/**
 * Add string to tuple.
 * 
 * Parameters:
 * - s: String - String to add
 * 
 * Returns:
 * Tuple - New tuple with string appended
 */
Tuple Tuple.add(String s);

/**
 * Add long integer to tuple.
 * 
 * Parameters:
 * - l: long - Long to add
 * 
 * Returns:
 * Tuple - New tuple with long appended
 */
Tuple Tuple.add(long l);

/**
 * Add byte array to tuple.
 * 
 * Parameters:
 * - b: byte[] - Bytes to add
 * 
 * Returns:
 * Tuple - New tuple with bytes appended
 */
Tuple Tuple.add(byte[] b);

/**
 * Add boolean to tuple.
 * 
 * Parameters:
 * - b: boolean - Boolean to add
 * 
 * Returns:
 * Tuple - New tuple with boolean appended
 */
Tuple Tuple.add(boolean b);

/**
 * Add UUID to tuple.
 * 
 * Parameters:
 * - uuid: UUID - UUID to add
 * 
 * Returns:
 * Tuple - New tuple with UUID appended
 */
Tuple Tuple.add(UUID uuid);

/**
 * Add nested tuple to tuple.
 * 
 * Parameters:
 * - tuple: Tuple - Nested tuple to add
 * 
 * Returns:
 * Tuple - New tuple with nested tuple appended
 */
Tuple Tuple.add(Tuple tuple);

/**
 * Add list to tuple (stored as nested structure).
 * 
 * Parameters:
 * - list: List<?> - List to add
 * 
 * Returns:
 * Tuple - New tuple with list appended
 */
Tuple Tuple.add(List<?> list);

/**
 * Add big integer to tuple.
 * 
 * Parameters:
 * - bigInt: BigInteger - Big integer to add
 * 
 * Returns:
 * Tuple - New tuple with big integer appended
 */
Tuple Tuple.add(BigInteger bigInt);

/**
 * Add double to tuple.
 * 
 * Parameters:
 * - d: double - Double to add
 * 
 * Returns:
 * Tuple - New tuple with double appended
 */
Tuple Tuple.add(double d);

/**
 * Add float to tuple.
 * 
 * Parameters:
 * - f: float - Float to add
 * 
 * Returns:
 * Tuple - New tuple with float appended
 */
Tuple Tuple.add(float f);

/**
 * Add versionstamp to tuple.
 *
 * Parameters:
 * - v: Versionstamp - Versionstamp to add
 *
 * Returns:
 * Tuple - New tuple with versionstamp appended
 */
Tuple Tuple.add(Versionstamp v);

/**
 * Add byte array slice to tuple.
 *
 * Parameters:
 * - b: byte[] - Source byte array
 * - offset: int - Starting offset in source array
 * - length: int - Number of bytes to copy
 *
 * Returns:
 * Tuple - New tuple with byte slice appended
 */
Tuple Tuple.add(byte[] b, int offset, int length);

/**
 * Add all elements from list to tuple.
 * Elements are added individually (not as nested list).
 *
 * Parameters:
 * - o: List<?> - List of elements to add
 *
 * Returns:
 * Tuple - New tuple with all list elements appended
 */
Tuple Tuple.addAll(List<?> o);

/**
 * Add all elements from another tuple.
 * Elements are added individually (not as nested tuple).
 *
 * Parameters:
 * - other: Tuple - Tuple whose elements to add
 *
 * Returns:
 * Tuple - New tuple with all other tuple's elements appended
 */
Tuple Tuple.addAll(Tuple other);

Usage examples:

// Build tuple incrementally
Tuple base = Tuple.from("users");
Tuple withId = base.add(1001L);
Tuple complete = withId.add("profile").add("email");

// Chain multiple additions
Tuple tuple = Tuple.from()
    .add("order")
    .add(12345L)
    .add(new UUID(1, 2))
    .add(99.99)
    .add(true);

// Add various types
Tuple mixed = Tuple.from("data")
    .add(42L)
    .add("text")
    .add(new byte[]{1, 2, 3})
    .add(3.14)
    .add(BigInteger.valueOf(1000000000000L));

// Nested tuples
Tuple inner = Tuple.from("inner", 1);
Tuple outer = Tuple.from("outer").add(inner);

// Add null
Tuple withNull = Tuple.from("key", null, "value");

Retrieving Elements

Extract typed elements from tuple by index.

/**
 * Get untyped object at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * Object - Object at index
 */
Object Tuple.get(int index);

/**
 * Get string at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * String - String at index, or null
 */
String Tuple.getString(int index);

/**
 * Get long at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * long - Long value at index
 * 
 * Throws:
 * IllegalArgumentException - If value is null or not a number
 */
long Tuple.getLong(int index);

/**
 * Get byte array at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * byte[] - Byte array at index, or null
 */
byte[] Tuple.getBytes(int index);

/**
 * Get boolean at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * boolean - Boolean value at index
 * 
 * Throws:
 * IllegalArgumentException - If value is null or not a boolean
 */
boolean Tuple.getBoolean(int index);

/**
 * Get UUID at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * UUID - UUID at index, or null
 */
UUID Tuple.getUUID(int index);

/**
 * Get nested tuple at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * Tuple - Nested tuple at index, or null
 */
Tuple Tuple.getNestedTuple(int index);

/**
 * Get nested list at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * List<Object> - Nested list at index, or null
 */
List<Object> Tuple.getNestedList(int index);

/**
 * Get big integer at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * BigInteger - Big integer at index
 * 
 * Throws:
 * IllegalArgumentException - If value is null or not a number
 */
BigInteger Tuple.getBigInteger(int index);

/**
 * Get double at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * double - Double value at index
 * 
 * Throws:
 * IllegalArgumentException - If value is null or not a number
 */
double Tuple.getDouble(int index);

/**
 * Get float at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * float - Float value at index
 * 
 * Throws:
 * IllegalArgumentException - If value is null or not a number
 */
float Tuple.getFloat(int index);

/**
 * Get number at index (any numeric type).
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * Number - Number at index
 * 
 * Throws:
 * IllegalArgumentException - If value is null or not a number
 */
Number Tuple.getNumber(int index);

/**
 * Get versionstamp at index.
 * 
 * Parameters:
 * - index: int - Zero-based index
 * 
 * Returns:
 * Versionstamp - Versionstamp at index, or null
 */
Versionstamp Tuple.getVersionstamp(int index);

Usage examples:

// Extract elements
Tuple tuple = Tuple.from("user", 1001, "Alice", true, 3.14);

String type = tuple.getString(0);      // "user"
long id = tuple.getLong(1);            // 1001
String name = tuple.getString(2);      // "Alice"
boolean active = tuple.getBoolean(3);  // true
double score = tuple.getDouble(4);     // 3.14

// Handle nulls
Tuple withNull = Tuple.from("key", null);
String value = withNull.getString(1);  // null
// long num = withNull.getLong(1);     // Throws exception

// Nested structures
Tuple nested = Tuple.from("outer", Tuple.from("inner", 42));
Tuple inner = nested.getNestedTuple(1);
long value = inner.getLong(1);  // 42

// Get as generic object
Object obj = tuple.get(0);  // Returns String "user"

Packing and Unpacking

Convert tuples to and from byte arrays for storage.

/**
 * Pack tuple to byte array.
 * Result can be used as FDB key or value.
 * 
 * Returns:
 * byte[] - Packed tuple bytes
 */
byte[] Tuple.pack();

/**
 * Pack tuple with prefix prepended.
 *
 * Parameters:
 * - prefix: byte[] - Prefix to prepend to packed tuple
 *
 * Returns:
 * byte[] - Prefix + packed tuple bytes
 */
byte[] Tuple.pack(byte[] prefix);

/**
 * Pack tuple directly into ByteBuffer.
 * Does not store packed representation internally.
 *
 * Parameters:
 * - dest: ByteBuffer - Destination buffer for packed tuple
 *
 * Throws:
 * IllegalArgumentException - If tuple contains incomplete versionstamps
 */
void Tuple.packInto(ByteBuffer dest);

/**
 * Pack tuple containing incomplete versionstamp.
 * Used with SET_VERSIONSTAMPED_KEY or SET_VERSIONSTAMPED_VALUE.
 *
 * Returns:
 * byte[] - Packed tuple with versionstamp placeholder
 *
 * Throws:
 * IllegalArgumentException - If tuple has no incomplete versionstamp
 */
byte[] Tuple.packWithVersionstamp();

/**
 * Pack tuple with versionstamp and prefix.
 * 
 * Parameters:
 * - prefix: byte[] - Prefix to prepend
 * 
 * Returns:
 * byte[] - Prefix + packed tuple with versionstamp placeholder
 */
byte[] Tuple.packWithVersionstamp(byte[] prefix);

Usage examples:

// Pack for storage
Tuple tuple = Tuple.from("user", 1001);
byte[] key = tuple.pack();

db.run(tr -> {
    tr.set(key, "user data".getBytes());
    return null;
});

// Unpack from storage
byte[] storedKey = db.run(tr -> {
    return tr.get(key).join();
});
Tuple unpacked = Tuple.fromBytes(key);

// Pack with prefix
byte[] prefix = "app:".getBytes();
byte[] keyWithPrefix = tuple.pack(prefix);

// Pack with versionstamp
Tuple timestamped = Tuple.from("log", Versionstamp.incomplete());
byte[] versionstampedKey = timestamped.packWithVersionstamp();

db.run(tr -> {
    tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, 
              versionstampedKey, "log entry".getBytes());
    return null;
});

Tuple Properties

Query tuple properties and metadata.

/**
 * Get number of elements in tuple.
 * 
 * Returns:
 * int - Number of elements
 */
int Tuple.size();

/**
 * Check if tuple is empty.
 * 
 * Returns:
 * boolean - True if tuple has no elements
 */
boolean Tuple.isEmpty();

/**
 * Get all elements as list.
 * 
 * Returns:
 * List<Object> - List containing all tuple elements
 */
List<Object> Tuple.getItems();

/**
 * Check if tuple contains incomplete versionstamp.
 * 
 * Returns:
 * boolean - True if tuple has incomplete versionstamp
 */
boolean Tuple.hasIncompleteVersionstamp();

/**
 * Get size of packed tuple in bytes.
 * 
 * Returns:
 * int - Size in bytes when packed
 */
int Tuple.getPackedSize();

/**
 * Get range covering all keys with this tuple as prefix.
 * 
 * Returns:
 * Range - Range from tuple to next possible tuple
 */
Range Tuple.range();

/**
 * Iterate over tuple elements.
 *
 * Returns:
 * Iterator<Object> - Iterator over elements
 */
Iterator<Object> Tuple.iterator();

/**
 * Create new tuple without first element.
 *
 * Returns:
 * Tuple - New tuple with first element removed
 *
 * Throws:
 * IllegalStateException - If tuple is empty
 */
Tuple Tuple.popFront();

/**
 * Create new tuple without last element.
 *
 * Returns:
 * Tuple - New tuple with last element removed
 *
 * Throws:
 * IllegalStateException - If tuple is empty
 */
Tuple Tuple.popBack();

Usage examples:

Tuple tuple = Tuple.from("user", 1001, "Alice");

// Query properties
int size = tuple.size();           // 3
boolean empty = tuple.isEmpty();   // false
List<Object> items = tuple.getItems();

// Check versionstamp
Tuple vstamped = Tuple.from("log", Versionstamp.incomplete());
boolean hasVS = vstamped.hasIncompleteVersionstamp();  // true

// Get packed size
int packedSize = tuple.getPackedSize();
System.out.println("Packed size: " + packedSize + " bytes");

// Get range for prefix
Tuple prefix = Tuple.from("users");
Range range = prefix.range();

db.read(tr -> {
    for (KeyValue kv : tr.getRange(range)) {
        Tuple key = Tuple.fromBytes(kv.getKey());
        // Process keys with "users" prefix
    }
    return null;
});

// Iterate over elements
for (Object item : tuple) {
    System.out.println(item);
}

Tuple Comparison

Compare tuples lexicographically.

/**
 * Compare tuples lexicographically.
 * Matches sort order of packed tuples.
 * 
 * Parameters:
 * - other: Tuple - Tuple to compare to
 * 
 * Returns:
 * int - Negative if less, 0 if equal, positive if greater
 */
int Tuple.compareTo(Tuple other);

/**
 * Check equality with another object.
 * 
 * Parameters:
 * - o: Object - Object to compare
 * 
 * Returns:
 * boolean - True if equal
 */
boolean Tuple.equals(Object o);

/**
 * Get hash code.
 * 
 * Returns:
 * int - Hash code
 */
int Tuple.hashCode();

/**
 * Get human-readable string representation.
 * 
 * Returns:
 * String - String representation
 */
String Tuple.toString();

Usage examples:

Tuple t1 = Tuple.from("a", 1);
Tuple t2 = Tuple.from("a", 2);
Tuple t3 = Tuple.from("b", 1);

// Compare
int cmp1 = t1.compareTo(t2);  // Negative (t1 < t2)
int cmp2 = t2.compareTo(t1);  // Positive (t2 > t1)
int cmp3 = t1.compareTo(t1);  // 0 (equal)

// Equality
boolean eq1 = t1.equals(t2);  // false
boolean eq2 = t1.equals(Tuple.from("a", 1));  // true

// Sort tuples
List<Tuple> tuples = Arrays.asList(t3, t1, t2);
Collections.sort(tuples);  // [t1, t2, t3]

// Use in maps/sets
Map<Tuple, String> map = new HashMap<>();
map.put(t1, "value1");
map.put(t2, "value2");

Versionstamp Support

Versionstamps provide transaction commit version timestamps.

Creating Versionstamps

/**
 * Create complete versionstamp from 12 bytes.
 * 
 * Parameters:
 * - data: byte[] - 12-byte versionstamp
 * 
 * Returns:
 * Versionstamp - Complete versionstamp
 */
static Versionstamp Versionstamp.complete(byte[] data);

/**
 * Create complete versionstamp from data at offset.
 * 
 * Parameters:
 * - data: byte[] - Byte array containing versionstamp
 * - offset: int - Offset of 12-byte versionstamp
 * 
 * Returns:
 * Versionstamp - Complete versionstamp
 */
static Versionstamp Versionstamp.complete(byte[] data, int offset);

/**
 * Create incomplete versionstamp with default user version (0).
 * Used as placeholder for transaction commit version.
 * 
 * Returns:
 * Versionstamp - Incomplete versionstamp
 */
static Versionstamp Versionstamp.incomplete();

/**
 * Create incomplete versionstamp with user version.
 * 
 * Parameters:
 * - userVersion: int - 16-bit user version (0-65535)
 * 
 * Returns:
 * Versionstamp - Incomplete versionstamp with user version
 */
static Versionstamp Versionstamp.incomplete(int userVersion);

/**
 * Create versionstamp from bytes (auto-detect complete/incomplete).
 * 
 * Parameters:
 * - data: byte[] - 12-byte versionstamp
 * 
 * Returns:
 * Versionstamp - Versionstamp
 */
static Versionstamp Versionstamp.fromBytes(byte[] data);

Versionstamp Operations

/**
 * Get 12-byte versionstamp representation.
 * 
 * Returns:
 * byte[] - 12-byte array (10 bytes version + 2 bytes user version)
 */
byte[] Versionstamp.getBytes();

/**
 * Get 10-byte transaction version portion.
 * 
 * Returns:
 * byte[] - 10-byte transaction version
 */
byte[] Versionstamp.getTransactionVersion();

/**
 * Get 16-bit user version.
 * 
 * Returns:
 * int - User version (0-65535)
 */
int Versionstamp.getUserVersion();

/**
 * Check if versionstamp is complete.
 * 
 * Returns:
 * boolean - True if complete, false if placeholder
 */
boolean Versionstamp.isComplete();

/**
 * Compare versionstamps.
 * 
 * Parameters:
 * - other: Versionstamp - Versionstamp to compare
 * 
 * Returns:
 * int - Negative if less, 0 if equal, positive if greater
 */
int Versionstamp.compareTo(Versionstamp other);

/**
 * Check equality.
 * 
 * Parameters:
 * - o: Object - Object to compare
 * 
 * Returns:
 * boolean - True if equal
 */
boolean Versionstamp.equals(Object o);

/**
 * Get hash code.
 * 
 * Returns:
 * int - Hash code
 */
int Versionstamp.hashCode();

/**
 * Get string representation.
 * 
 * Returns:
 * String - Human-readable representation
 */
String Versionstamp.toString();

Usage examples:

import com.apple.foundationdb.tuple.Versionstamp;

// Create incomplete versionstamp for commit-time assignment
Versionstamp incomplete = Versionstamp.incomplete();
Tuple logKey = Tuple.from("log", incomplete);

db.run(tr -> {
    byte[] key = logKey.packWithVersionstamp();
    tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, key, "entry".getBytes());
    return null;
});

// Create with user version for ordering within same transaction
for (int i = 0; i < 10; i++) {
    Versionstamp vs = Versionstamp.incomplete(i);
    Tuple key = Tuple.from("events", vs);
    // Each key gets same transaction version but different user version
}

// Read complete versionstamp after commit
db.run(tr -> {
    tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY,
              logKey.packWithVersionstamp(), "data".getBytes());
    tr.commit().join();
    
    byte[] vstampBytes = tr.getVersionstamp().join();
    Versionstamp committed = Versionstamp.complete(vstampBytes);
    
    System.out.println("Committed at: " + committed);
    return null;
});

Types

class Tuple implements Comparable<Tuple>, Iterable<Object> {
    // All methods documented above
}

class Versionstamp implements Comparable<Versionstamp> {
    Versionstamp(byte[] bytes);
    Versionstamp(byte[] transactionVersion, int userVersion);
    // All methods documented above
}

Supported Types

Tuples support the following types with proper ordering:

  • null: Sorts before all other types
  • byte[]: Byte arrays (arbitrary binary data)
  • String: UTF-8 encoded strings
  • Long, Integer, Short, Byte: Integer types (little-endian ordering)
  • BigInteger: Arbitrary precision integers
  • Float, Double: Floating-point numbers (IEEE 754 ordering)
  • Boolean: false < true
  • UUID: Standard UUID ordering
  • Versionstamp: 96-bit versionstamps
  • Tuple: Nested tuples
  • List: Nested lists (stored as tuples)

Important Notes

Ordering Guarantees

  • Packed tuples sort lexicographically in same order as tuple comparison
  • Type ordering: null < bytes < string < nested < int < float < boolean < UUID < versionstamp
  • Use tuples for compound keys that need predictable ordering

Immutability

  • Tuple objects are immutable
  • add() methods return new tuple instances
  • Safe to share tuples across threads

Performance

  • Packing is efficient for keys up to a few KB
  • Consider pre-packing frequently used tuples
  • Unpacking validates format and can throw exceptions

Versionstamps

  • Incomplete versionstamps are placeholders filled at commit time
  • packWithVersionstamp() required for incomplete versionstamps
  • Only one incomplete versionstamp allowed per key or value
  • User version enables ordering within same transaction

Null Handling

  • null is a valid tuple element
  • getString/getBytes return null for null elements
  • getLong/getBoolean/etc throw exception for null elements
  • Use get() to check for null before typed access