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

directory-layer.mddocs/reference/

Directory Layer

Hierarchical namespace management mapping human-readable paths to efficient binary prefixes. The directory layer provides a file-system-like organization of the keyspace.

Capabilities

Directory Creation

Create directory layer and directory subspaces.

/**
 * Create default directory layer.
 * Uses default subspaces for node and content storage.
 */
class DirectoryLayer implements Directory {
    DirectoryLayer();
}

/**
 * Create directory layer with custom subspaces.
 * 
 * Parameters:
 * - nodeSubspace: Subspace - Subspace for directory metadata storage
 * - contentSubspace: Subspace - Subspace for directory prefix allocation
 */
class DirectoryLayer implements Directory {
    DirectoryLayer(Subspace nodeSubspace, Subspace contentSubspace);
}

Usage examples:

import com.apple.foundationdb.directory.*;
import com.apple.foundationdb.subspace.Subspace;
import java.util.Arrays;
import java.util.List;

// Default directory layer
DirectoryLayer rootDir = new DirectoryLayer();

// Custom directory layer with specific subspaces
Subspace nodeSpace = new Subspace(Tuple.from("dir", "nodes"));
Subspace contentSpace = new Subspace(Tuple.from("dir", "content"));
DirectoryLayer customDir = new DirectoryLayer(nodeSpace, contentSpace);

// Access global default instance
DirectoryLayer dir = DirectoryLayer.getDefault();

Creating Directories

Create directory subspaces at specified paths.

/**
 * Create or open directory at path.
 * Creates directory if it doesn't exist, opens if it does.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path segments for directory
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - Directory subspace at path
 */
CompletableFuture<DirectorySubspace> Directory.createOrOpen(
    TransactionContext tcx, List<String> subpath
);

/**
 * Create or open directory with layer check.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path segments for directory
 * - layer: byte[] - Expected layer metadata
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - Directory subspace at path
 * 
 * Throws:
 * MismatchedLayerException - If directory exists with different layer
 */
CompletableFuture<DirectorySubspace> Directory.createOrOpen(
    TransactionContext tcx, List<String> subpath, byte[] layer
);

/**
 * Create new directory at path.
 * Fails if directory already exists.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path segments for directory
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - New directory subspace
 * 
 * Throws:
 * DirectoryAlreadyExistsException - If directory exists
 */
CompletableFuture<DirectorySubspace> Directory.create(
    TransactionContext tcx, List<String> subpath
);

/**
 * Create new directory with layer metadata.
 *
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path segments for directory
 * - layer: byte[] - Layer metadata for directory
 *
 * Returns:
 * CompletableFuture<DirectorySubspace> - New directory subspace
 */
CompletableFuture<DirectorySubspace> Directory.create(
    TransactionContext tcx, List<String> subpath, byte[] layer
);

/**
 * Create new directory with custom prefix.
 * Allows specifying both layer metadata and prefix instead of auto-allocation.
 *
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path segments for directory
 * - layer: byte[] - Layer metadata for directory
 * - prefix: byte[] - Custom prefix for directory (null for auto-allocation)
 *
 * Returns:
 * CompletableFuture<DirectorySubspace> - New directory subspace
 *
 * Throws:
 * DirectoryAlreadyExistsException - If directory exists
 */
CompletableFuture<DirectorySubspace> Directory.create(
    TransactionContext tcx, List<String> subpath, byte[] layer, byte[] prefix
);

/**
 * Get the DirectoryLayer that was used to create this Directory.
 *
 * Returns:
 * DirectoryLayer - Parent directory layer instance
 */
DirectoryLayer Directory.getDirectoryLayer();

Usage examples:

DirectoryLayer dir = new DirectoryLayer();

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

// Create with layer metadata
byte[] layer = "user_data".getBytes();
DirectorySubspace userDir = dir.createOrOpen(
    db,
    Arrays.asList("myapp", "users"),
    layer
).join();

// Create new directory (fails if exists)
try {
    DirectorySubspace configDir = dir.create(
        db,
        Arrays.asList("myapp", "config")
    ).join();
} catch (DirectoryAlreadyExistsException e) {
    System.out.println("Directory already exists");
}

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

Opening Directories

Open existing directories.

/**
 * Open existing directory at path.
 * Fails if directory doesn't exist.
 * 
 * Parameters:
 * - tcx: ReadTransactionContext - Context for read transaction
 * - subpath: List<String> - Path segments for directory
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - Directory subspace at path
 * 
 * Throws:
 * NoSuchDirectoryException - If directory doesn't exist
 */
CompletableFuture<DirectorySubspace> Directory.open(
    ReadTransactionContext tcx, List<String> subpath
);

/**
 * Open existing directory with layer check.
 * 
 * Parameters:
 * - tcx: ReadTransactionContext - Context for read transaction
 * - subpath: List<String> - Path segments for directory
 * - layer: byte[] - Expected layer metadata
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - Directory subspace at path
 * 
 * Throws:
 * NoSuchDirectoryException - If directory doesn't exist
 * MismatchedLayerException - If layer doesn't match
 */
CompletableFuture<DirectorySubspace> Directory.open(
    ReadTransactionContext tcx, List<String> subpath, byte[] layer
);

Usage examples:

// Open existing directory
try {
    DirectorySubspace dir = rootDir.open(
        db,
        Arrays.asList("myapp", "users")
    ).join();
} catch (NoSuchDirectoryException e) {
    System.out.println("Directory not found");
}

// Open with layer verification
byte[] expectedLayer = "user_data".getBytes();
try {
    DirectorySubspace dir = rootDir.open(
        db,
        Arrays.asList("myapp", "users"),
        expectedLayer
    ).join();
} catch (MismatchedLayerException e) {
    System.out.println("Layer mismatch: " + e.getMessage());
}

Directory Queries

Check existence and list subdirectories.

/**
 * Check if directory exists at path.
 * 
 * Parameters:
 * - tcx: ReadTransactionContext - Context for read transaction
 * - subpath: List<String> - Path segments for directory
 * 
 * Returns:
 * CompletableFuture<Boolean> - True if directory exists
 */
CompletableFuture<Boolean> Directory.exists(
    ReadTransactionContext tcx, List<String> subpath
);

/**
 * List immediate subdirectories at path.
 * 
 * Parameters:
 * - tcx: ReadTransactionContext - Context for read transaction
 * - subpath: List<String> - Path segments for directory (empty for root)
 * 
 * Returns:
 * CompletableFuture<List<String>> - List of subdirectory names
 */
CompletableFuture<List<String>> Directory.list(
    ReadTransactionContext tcx, List<String> subpath
);

Usage examples:

// Check if directory exists
boolean exists = rootDir.exists(
    db,
    Arrays.asList("myapp", "users")
).join();

if (exists) {
    System.out.println("Directory exists");
}

// List subdirectories
List<String> subdirs = rootDir.list(
    db,
    Arrays.asList("myapp")
).join();

for (String name : subdirs) {
    System.out.println("Subdirectory: " + name);
}

// List root-level directories
List<String> rootDirs = rootDir.list(
    db,
    Arrays.asList()  // Empty path for root
).join();

Directory Operations

Move and remove directories.

/**
 * Move directory from old path to new path.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - oldPath: List<String> - Current path of directory
 * - newPath: List<String> - New path for directory
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - Directory at new location
 * 
 * Throws:
 * DirectoryMoveException - If move is invalid or fails
 */
CompletableFuture<DirectorySubspace> Directory.move(
    TransactionContext tcx, List<String> oldPath, List<String> newPath
);

/**
 * Move this directory to new absolute path.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - newAbsolutePath: List<String> - New absolute path
 * 
 * Returns:
 * CompletableFuture<DirectorySubspace> - Directory at new location
 */
CompletableFuture<DirectorySubspace> Directory.moveTo(
    TransactionContext tcx, List<String> newAbsolutePath
);

/**
 * Remove directory at path.
 * Directory must be empty or removeIfExists must handle non-empty.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path of directory to remove
 * 
 * Returns:
 * CompletableFuture<Void> - Completes when directory is removed
 * 
 * Throws:
 * NoSuchDirectoryException - If directory doesn't exist
 */
CompletableFuture<Void> Directory.remove(
    TransactionContext tcx, List<String> subpath
);

/**
 * Remove directory if it exists.
 * 
 * Parameters:
 * - tcx: TransactionContext - Context for transaction execution
 * - subpath: List<String> - Path of directory to remove
 * 
 * Returns:
 * CompletableFuture<Boolean> - True if directory was removed, false if didn't exist
 */
CompletableFuture<Boolean> Directory.removeIfExists(
    TransactionContext tcx, List<String> subpath
);

Usage examples:

// Move directory
rootDir.move(
    db,
    Arrays.asList("myapp", "old_users"),
    Arrays.asList("myapp", "users")
).join();

// Move using moveTo
DirectorySubspace dir = rootDir.open(
    db,
    Arrays.asList("temp", "data")
).join();

dir.moveTo(db, Arrays.asList("archive", "data")).join();

// Remove directory
try {
    rootDir.remove(db, Arrays.asList("myapp", "temp")).join();
    System.out.println("Directory removed");
} catch (NoSuchDirectoryException e) {
    System.out.println("Directory not found");
}

// Remove if exists (no exception)
boolean removed = rootDir.removeIfExists(
    db,
    Arrays.asList("myapp", "cache")
).join();

System.out.println(removed ? "Removed" : "Didn't exist");

Directory Information

Query directory metadata.

/**
 * Get directory path as list of names.
 * 
 * Returns:
 * List<String> - Path segments for this directory
 */
List<String> Directory.getPath();

/**
 * Get layer metadata for directory.
 * 
 * Returns:
 * byte[] - Layer byte string
 */
byte[] Directory.getLayer();

/**
 * Get parent directory layer.
 * 
 * Returns:
 * DirectoryLayer - Parent directory layer instance
 */
DirectoryLayer Directory.getDirectoryLayer();

Usage examples:

DirectorySubspace dir = rootDir.open(
    db,
    Arrays.asList("myapp", "users", "profiles")
).join();

// Get path
List<String> path = dir.getPath();
// ["myapp", "users", "profiles"]

// Get layer
byte[] layer = dir.getLayer();
String layerStr = new String(layer);

// Get directory layer
DirectoryLayer parentLayer = dir.getDirectoryLayer();

DirectorySubspace Operations

DirectorySubspace combines Directory and Subspace functionality, inheriting all Subspace methods for key packing, unpacking, and range operations.

Usage examples:

DirectorySubspace dir = rootDir.createOrOpen(
    db,
    Arrays.asList("myapp", "users")
).join();

// Use as Subspace for key packing
byte[] key1 = dir.pack(Tuple.from("user123"));
byte[] key2 = dir.pack(Tuple.from("user456", "profile"));

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

// Use directory range
Range dirRange = dir.range();
db.read(tr -> {
    for (KeyValue kv : tr.getRange(dirRange)) {
        Tuple key = dir.unpack(kv.getKey());
        // Process keys in this directory
    }
    return null;
});

Types

interface Directory {
    // All methods documented above
}

class DirectoryLayer implements Directory {
    DirectoryLayer();
    DirectoryLayer(Subspace nodeSubspace, Subspace contentSubspace);
    
    static DirectoryLayer getDefault();
}

class DirectorySubspace extends Subspace implements Directory {
    // Inherits all Subspace methods (pack, unpack, range, etc.)
    // Implements all Directory methods
}

class DirectoryPartition extends DirectorySubspace {
    // Partition implementation for isolated directory trees
}

Directory Exceptions

/**
 * Base exception for directory layer operations.
 * Contains path that caused the error.
 */
class DirectoryException extends RuntimeException {
    public final List<String> path;

    DirectoryException(String message, List<String> path);
    String getMessage();
    List<String> getPath();
}

/**
 * Thrown when attempting to create directory that already exists.
 * Only thrown by create() method, not createOrOpen().
 */
class DirectoryAlreadyExistsException extends DirectoryException {
    DirectoryAlreadyExistsException(List<String> path);
}

/**
 * Thrown when directory doesn't exist.
 * Thrown by open(), move(), and remove() when directory not found.
 */
class NoSuchDirectoryException extends DirectoryException {
    NoSuchDirectoryException(List<String> path);
}

/**
 * Thrown when directory's layer doesn't match expected value.
 * Occurs when opening directory with different layer than stored.
 */
class MismatchedLayerException extends DirectoryException {
    public final byte[] stored;
    public final byte[] expected;

    MismatchedLayerException(List<String> path, byte[] stored, byte[] expected);
}

/**
 * Thrown when directory move operation fails.
 * Occurs when moving to invalid location or when destination exists.
 */
class DirectoryMoveException extends RuntimeException {
    public final List<String> sourcePath;
    public final List<String> destinationPath;

    DirectoryMoveException(String message, List<String> sourcePath, List<String> destinationPath);
}

/**
 * Thrown for directory layer version incompatibilities.
 * Occurs when directory metadata format is incompatible.
 */
class DirectoryVersionException extends RuntimeException {
    DirectoryVersionException(String message);
    String getMessage();
}

Important Notes

Directory Benefits

  • Human-readable paths map to short binary prefixes
  • Automatic prefix allocation avoids conflicts
  • Efficient range scans within directory
  • Hierarchical organization of keyspace

Layer Metadata

  • Layer is optional metadata string
  • Used to identify directory purpose/schema
  • Checked on open() if specified
  • Stored as byte array for flexibility

Directory Prefixes

  • Each directory gets unique binary prefix
  • Prefixes are allocated automatically
  • Prefixes are short and efficient
  • Never reused even if directory deleted

Path Semantics

  • Paths are lists of string segments
  • Empty path refers to root
  • Paths are case-sensitive
  • No limit on path depth

Directory Moves

  • Move renames directory atomically
  • Cannot move to existing directory
  • Cannot move directory to be its own descendant
  • Move preserves directory ID and prefix

Directory Removal

  • Remove is recursive (removes subdirectories)
  • Fails if directory doesn't exist (unless removeIfExists)
  • Data in directory must be cleared first
  • Only removes directory metadata, not data

Thread Safety

  • Directory operations are transactional
  • Safe to use from multiple threads
  • Each operation is atomic
  • Use TransactionContext for safety

Performance

  • createOrOpen is efficient (single transaction)
  • list() fetches all subdirectories at once
  • Directory metadata is cached
  • Prefix allocation is fast