or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

atomic-operations.mdconfiguration.mdcore-operations.mddirectory-layer.mdindex.mdkey-value-ops.mdsubspaces.mdtransactions.md

directory-layer.mddocs/

0

# Directory Layer

1

2

Hierarchical directory abstraction for organizing keys into namespaces with automatic prefix management and metadata. The Directory Layer provides a file system-like interface on top of FoundationDB's key-value store.

3

4

## Capabilities

5

6

### Directory Management

7

8

Create, open, and manage hierarchical directories with automatic prefix allocation.

9

10

```python { .api }

11

class DirectoryLayer:

12

def __init__(self, node_subspace: Subspace = None, content_subspace: Subspace = None):

13

"""

14

Create a DirectoryLayer instance

15

16

Args:

17

node_subspace: Subspace for directory metadata (default: system subspace)

18

content_subspace: Subspace for directory contents (default: empty prefix)

19

"""

20

21

def create_or_open(self, tr: Transaction, path: List[str], layer: bytes = b'') -> DirectorySubspace:

22

"""

23

Create directory if it doesn't exist, otherwise open it

24

25

Args:

26

tr: Transaction to use

27

path: Directory path as list of strings

28

layer: Optional layer identifier for the directory

29

30

Returns:

31

DirectorySubspace for the directory

32

33

Raises:

34

DirectoryLayerMismatchError: If directory exists with different layer

35

"""

36

37

def open(self, tr: Transaction, path: List[str], layer: bytes = b'') -> DirectorySubspace:

38

"""

39

Open an existing directory

40

41

Args:

42

tr: Transaction to use

43

path: Directory path as list of strings

44

layer: Expected layer identifier (must match existing)

45

46

Returns:

47

DirectorySubspace for the directory

48

49

Raises:

50

DirectoryNotFoundError: If directory doesn't exist

51

DirectoryLayerMismatchError: If layer doesn't match

52

"""

53

54

def create(self, tr: Transaction, path: List[str], layer: bytes = b'', prefix: bytes = None) -> DirectorySubspace:

55

"""

56

Create a new directory

57

58

Args:

59

tr: Transaction to use

60

path: Directory path as list of strings

61

layer: Optional layer identifier

62

prefix: Optional specific prefix (auto-allocated if None)

63

64

Returns:

65

DirectorySubspace for the new directory

66

67

Raises:

68

DirectoryAlreadyExistsError: If directory already exists

69

"""

70

71

def move(self, tr: Transaction, old_path: List[str], new_path: List[str]) -> DirectorySubspace:

72

"""

73

Move/rename a directory

74

75

Args:

76

tr: Transaction to use

77

old_path: Current directory path

78

new_path: New directory path

79

80

Returns:

81

DirectorySubspace for the moved directory

82

83

Raises:

84

DirectoryNotFoundError: If source directory doesn't exist

85

DirectoryAlreadyExistsError: If destination already exists

86

"""

87

88

def remove(self, tr: Transaction, path: List[str]) -> bool:

89

"""

90

Remove a directory and all its contents

91

92

Args:

93

tr: Transaction to use

94

path: Directory path to remove

95

96

Returns:

97

True if directory was removed, False if it didn't exist

98

"""

99

100

def list(self, tr: Transaction, path: List[str] = []) -> List[str]:

101

"""

102

List subdirectories at the given path

103

104

Args:

105

tr: Transaction to use

106

path: Directory path to list (empty for root)

107

108

Returns:

109

List of subdirectory names

110

"""

111

112

def exists(self, tr: Transaction, path: List[str]) -> bool:

113

"""

114

Check if a directory exists

115

116

Args:

117

tr: Transaction to use

118

path: Directory path to check

119

120

Returns:

121

True if directory exists, False otherwise

122

"""

123

124

# Default directory layer instance

125

directory: DirectoryLayer

126

```

127

128

```java { .api }

129

// Java API

130

public class DirectoryLayer {

131

public DirectoryLayer():

132

/**

133

* Create DirectoryLayer with default subspaces

134

*/

135

136

public DirectoryLayer(Subspace nodeSubspace, Subspace contentSubspace):

137

/**

138

* Create DirectoryLayer with custom subspaces

139

* @param nodeSubspace Subspace for directory metadata

140

* @param contentSubspace Subspace for directory contents

141

*/

142

143

public CompletableFuture<DirectorySubspace> createOrOpen(TransactionContext tcx, List<String> path):

144

/**

145

* Create directory if it doesn't exist, otherwise open it

146

* @param tcx Transaction context

147

* @param path Directory path

148

* @return CompletableFuture with DirectorySubspace

149

*/

150

151

public CompletableFuture<DirectorySubspace> open(TransactionContext tcx, List<String> path):

152

/**

153

* Open an existing directory

154

* @param tcx Transaction context

155

* @param path Directory path

156

* @return CompletableFuture with DirectorySubspace

157

*/

158

159

public CompletableFuture<DirectorySubspace> create(TransactionContext tcx, List<String> path):

160

/**

161

* Create a new directory

162

* @param tcx Transaction context

163

* @param path Directory path

164

* @return CompletableFuture with DirectorySubspace

165

*/

166

167

public CompletableFuture<DirectorySubspace> move(TransactionContext tcx, List<String> oldPath, List<String> newPath):

168

/**

169

* Move/rename a directory

170

* @param tcx Transaction context

171

* @param oldPath Current directory path

172

* @param newPath New directory path

173

* @return CompletableFuture with DirectorySubspace

174

*/

175

176

public CompletableFuture<Boolean> remove(TransactionContext tcx, List<String> path):

177

/**

178

* Remove a directory and all its contents

179

* @param tcx Transaction context

180

* @param path Directory path to remove

181

* @return CompletableFuture with success boolean

182

*/

183

184

public CompletableFuture<List<String>> list(TransactionContext tcx, List<String> path):

185

/**

186

* List subdirectories at the given path

187

* @param tcx Transaction context

188

* @param path Directory path to list

189

* @return CompletableFuture with list of subdirectory names

190

*/

191

}

192

193

// Default directory layer instance

194

public static final DirectoryLayer DEFAULT = new DirectoryLayer();

195

```

196

197

```go { .api }

198

// Go API

199

type DirectoryLayer struct{}

200

201

func NewDirectoryLayer() DirectoryLayer: ...

202

203

func (dl DirectoryLayer) CreateOrOpen(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error): ...

204

func (dl DirectoryLayer) Open(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error): ...

205

func (dl DirectoryLayer) Create(t fdb.Transactor, path []string, layer []byte) (DirectorySubspace, error): ...

206

func (dl DirectoryLayer) Move(t fdb.Transactor, oldPath, newPath []string) (DirectorySubspace, error): ...

207

func (dl DirectoryLayer) Remove(t fdb.Transactor, path []string) (bool, error): ...

208

func (dl DirectoryLayer) List(t fdb.Transactor, path []string) ([]string, error): ...

209

func (dl DirectoryLayer) Exists(t fdb.Transactor, path []string) (bool, error): ...

210

211

// Default directory layer instance

212

var DefaultDirectoryLayer DirectoryLayer

213

```

214

215

### Directory Subspaces

216

217

DirectorySubspace combines the functionality of a Subspace with directory metadata.

218

219

```python { .api }

220

class DirectorySubspace(Subspace):

221

def get_path(self) -> List[str]:

222

"""

223

Get the path of this directory

224

225

Returns:

226

Directory path as list of strings

227

"""

228

229

def get_layer(self) -> bytes:

230

"""

231

Get the layer identifier for this directory

232

233

Returns:

234

Layer identifier bytes

235

"""

236

237

def create_or_open(self, tr: Transaction, name: str, layer: bytes = b'') -> 'DirectorySubspace':

238

"""

239

Create or open a subdirectory

240

241

Args:

242

tr: Transaction to use

243

name: Subdirectory name

244

layer: Optional layer identifier

245

246

Returns:

247

DirectorySubspace for the subdirectory

248

"""

249

250

def open(self, tr: Transaction, name: str, layer: bytes = b'') -> 'DirectorySubspace':

251

"""

252

Open a subdirectory

253

254

Args:

255

tr: Transaction to use

256

name: Subdirectory name

257

layer: Expected layer identifier

258

259

Returns:

260

DirectorySubspace for the subdirectory

261

"""

262

263

def create(self, tr: Transaction, name: str, layer: bytes = b'', prefix: bytes = None) -> 'DirectorySubspace':

264

"""

265

Create a subdirectory

266

267

Args:

268

tr: Transaction to use

269

name: Subdirectory name

270

layer: Optional layer identifier

271

prefix: Optional specific prefix

272

273

Returns:

274

DirectorySubspace for the new subdirectory

275

"""

276

277

def move(self, tr: Transaction, new_path: List[str]) -> 'DirectorySubspace':

278

"""

279

Move this directory to a new path

280

281

Args:

282

tr: Transaction to use

283

new_path: New directory path

284

285

Returns:

286

DirectorySubspace for the moved directory

287

"""

288

289

def remove(self, tr: Transaction) -> bool:

290

"""

291

Remove this directory and all its contents

292

293

Args:

294

tr: Transaction to use

295

296

Returns:

297

True if directory was removed

298

"""

299

300

def list(self, tr: Transaction) -> List[str]:

301

"""

302

List subdirectories of this directory

303

304

Args:

305

tr: Transaction to use

306

307

Returns:

308

List of subdirectory names

309

"""

310

```

311

312

```java { .api }

313

// Java API

314

public class DirectorySubspace extends Subspace {

315

public List<String> getPath():

316

/**

317

* Get the directory path

318

* @return Directory path as list of strings

319

*/

320

321

public byte[] getLayer():

322

/**

323

* Get the layer identifier

324

* @return Layer identifier bytes

325

*/

326

327

public CompletableFuture<DirectorySubspace> createOrOpen(TransactionContext tcx, String name):

328

/**

329

* Create or open a subdirectory

330

* @param tcx Transaction context

331

* @param name Subdirectory name

332

* @return CompletableFuture with DirectorySubspace

333

*/

334

335

public CompletableFuture<DirectorySubspace> open(TransactionContext tcx, String name):

336

/**

337

* Open a subdirectory

338

* @param tcx Transaction context

339

* @param name Subdirectory name

340

* @return CompletableFuture with DirectorySubspace

341

*/

342

343

public CompletableFuture<DirectorySubspace> create(TransactionContext tcx, String name):

344

/**

345

* Create a subdirectory

346

* @param tcx Transaction context

347

* @param name Subdirectory name

348

* @return CompletableFuture with DirectorySubspace

349

*/

350

351

public CompletableFuture<Boolean> remove(TransactionContext tcx):

352

/**

353

* Remove this directory

354

* @param tcx Transaction context

355

* @return CompletableFuture with success boolean

356

*/

357

358

public CompletableFuture<List<String>> list(TransactionContext tcx):

359

/**

360

* List subdirectories

361

* @param tcx Transaction context

362

* @return CompletableFuture with list of subdirectory names

363

*/

364

}

365

```

366

367

```go { .api }

368

// Go API

369

type DirectorySubspace interface {

370

fdb.Subspace

371

372

GetPath() []string

373

GetLayer() []byte

374

CreateOrOpen(t fdb.Transactor, name string, layer []byte) (DirectorySubspace, error)

375

Open(t fdb.Transactor, name string, layer []byte) (DirectorySubspace, error)

376

Create(t fdb.Transactor, name string, layer []byte) (DirectorySubspace, error)

377

Remove(t fdb.Transactor) (bool, error)

378

List(t fdb.Transactor) ([]string, error)

379

}

380

```

381

382

### Directory Partitions

383

384

Special directories that act as independent directory layers, providing complete isolation.

385

386

```python { .api }

387

class DirectoryPartition(DirectorySubspace):

388

"""

389

A directory partition acts as an independent directory layer

390

"""

391

392

def __init__(self, path: List[str], prefix: bytes, parent_dir_layer: DirectoryLayer):

393

"""

394

Create a DirectoryPartition

395

396

Args:

397

path: Directory path

398

prefix: Prefix for this partition

399

parent_dir_layer: Parent directory layer

400

"""

401

402

# Inherits all DirectorySubspace methods but operations are isolated to this partition

403

```

404

405

### Exception Types

406

407

Exceptions raised by directory layer operations.

408

409

```python { .api }

410

class DirectoryError(Exception):

411

"""Base class for directory layer errors"""

412

413

class DirectoryNotFoundError(DirectoryError):

414

"""Directory does not exist"""

415

416

class DirectoryAlreadyExistsError(DirectoryError):

417

"""Directory already exists"""

418

419

class DirectoryLayerMismatchError(DirectoryError):

420

"""Directory layer does not match expected"""

421

422

class DirectoryVersionError(DirectoryError):

423

"""Directory version is not supported"""

424

```

425

426

**Usage Examples:**

427

428

**Basic Directory Operations (Python):**

429

```python

430

import fdb

431

432

fdb.api_version(630)

433

db = fdb.open()

434

435

@fdb.transactional

436

def directory_example(tr):

437

# Create application directory structure

438

app_dir = fdb.directory.create_or_open(tr, ['myapp'])

439

users_dir = app_dir.create_or_open(tr, 'users')

440

config_dir = app_dir.create_or_open(tr, 'config')

441

442

# Create user-specific directories

443

alice_dir = users_dir.create_or_open(tr, 'alice')

444

bob_dir = users_dir.create_or_open(tr, 'bob')

445

446

# Store data in user directories

447

alice_dir[tr][b'profile'] = b'{"name": "Alice", "email": "alice@example.com"}'

448

bob_dir[tr][b'profile'] = b'{"name": "Bob", "email": "bob@example.com"}'

449

450

# Store configuration

451

config_dir[tr][b'database_url'] = b'fdb://cluster'

452

config_dir[tr][b'api_key'] = b'secret123'

453

454

# List users

455

user_list = users_dir.list(tr)

456

print(f"Users: {user_list}") # Users: ['alice', 'bob']

457

458

return app_dir.get_path()

459

460

app_path = directory_example(db)

461

print(f"App directory path: {app_path}")

462

```

463

464

**Directory Management (Java):**

465

```java

466

import com.apple.foundationdb.*;

467

import com.apple.foundationdb.directory.*;

468

469

FDB fdb = FDB.selectAPIVersion(630);

470

471

try (Database db = fdb.open()) {

472

DirectoryLayer directory = DirectoryLayer.getDefault();

473

474

db.run(tr -> {

475

// Create organizational structure

476

DirectorySubspace company = directory.createOrOpen(tr, Arrays.asList("company")).join();

477

DirectorySubspace departments = company.createOrOpen(tr, "departments").join();

478

479

// Create department directories

480

DirectorySubspace engineering = departments.createOrOpen(tr, "engineering").join();

481

DirectorySubspace marketing = departments.createOrOpen(tr, "marketing").join();

482

DirectorySubspace sales = departments.createOrOpen(tr, "sales").join();

483

484

// Store department data

485

engineering.pack(Tuple.from("head_count")).set(tr, Tuple.from(25).pack());

486

marketing.pack(Tuple.from("head_count")).set(tr, Tuple.from(12).pack());

487

sales.pack(Tuple.from("head_count")).set(tr, Tuple.from(18).pack());

488

489

// List all departments

490

List<String> deptList = departments.list(tr).join();

491

System.out.println("Departments: " + deptList);

492

493

return null;

494

});

495

}

496

```

497

498

**Hierarchical Data Organization (Go):**

499

```go

500

package main

501

502

import (

503

"fmt"

504

"github.com/apple/foundationdb/bindings/go/src/fdb"

505

"github.com/apple/foundationdb/bindings/go/src/fdb/directory"

506

"github.com/apple/foundationdb/bindings/go/src/fdb/tuple"

507

)

508

509

func main() {

510

fdb.MustAPIVersion(630)

511

db := fdb.MustOpenDefault()

512

513

db.Transact(func(tr fdb.Transaction) (interface{}, error) {

514

// Create e-commerce directory structure

515

ecommerce, err := directory.CreateOrOpen(tr, []string{"ecommerce"}, nil)

516

if err != nil {

517

return nil, err

518

}

519

520

// Create product catalog

521

products, err := ecommerce.CreateOrOpen(tr, "products", nil)

522

if err != nil {

523

return nil, err

524

}

525

526

// Create categories

527

electronics, err := products.CreateOrOpen(tr, "electronics", nil)

528

if err != nil {

529

return nil, err

530

}

531

532

clothing, err := products.CreateOrOpen(tr, "clothing", nil)

533

if err != nil {

534

return nil, err

535

}

536

537

// Store product data

538

laptop_key := electronics.Pack(tuple.Tuple{"laptop", "12345"})

539

tr.Set(laptop_key, []byte(`{"name": "Gaming Laptop", "price": 1299}`))

540

541

shirt_key := clothing.Pack(tuple.Tuple{"shirt", "67890"})

542

tr.Set(shirt_key, []byte(`{"name": "Cotton T-Shirt", "price": 19}`))

543

544

// List categories

545

categories, err := products.List(tr)

546

if err != nil {

547

return nil, err

548

}

549

550

fmt.Printf("Product categories: %v\n", categories)

551

552

return nil, nil

553

})

554

}

555

```

556

557

**Directory Layers and Partitions (Python):**

558

```python

559

import fdb

560

561

fdb.api_version(630)

562

db = fdb.open()

563

564

@fdb.transactional

565

def multi_tenant_example(tr):

566

# Create tenant directories with layers

567

tenant1_dir = fdb.directory.create_or_open(tr, ['tenant1'], layer=b'application')

568

tenant2_dir = fdb.directory.create_or_open(tr, ['tenant2'], layer=b'application')

569

570

# Each tenant has isolated data

571

tenant1_users = tenant1_dir.create_or_open(tr, 'users')

572

tenant1_orders = tenant1_dir.create_or_open(tr, 'orders')

573

574

tenant2_users = tenant2_dir.create_or_open(tr, 'users')

575

tenant2_orders = tenant2_dir.create_or_open(tr, 'orders')

576

577

# Store tenant-specific data

578

tenant1_users[tr][b'user:123'] = b'{"name": "Alice", "tenant": "1"}'

579

tenant1_orders[tr][b'order:456'] = b'{"user": "123", "total": 99.99}'

580

581

tenant2_users[tr][b'user:123'] = b'{"name": "Bob", "tenant": "2"}' # Same ID, different tenant

582

tenant2_orders[tr][b'order:456'] = b'{"user": "123", "total": 149.99}'

583

584

# Verify isolation - each tenant sees only their data

585

t1_users = list(tenant1_users.get_range(tr, b'', b'\xFF'))

586

t2_users = list(tenant2_users.get_range(tr, b'', b'\xFF'))

587

588

print(f"Tenant 1 users: {len(t1_users)}") # 1

589

print(f"Tenant 2 users: {len(t2_users)}") # 1

590

591

# Directory information

592

print(f"Tenant 1 path: {tenant1_dir.get_path()}")

593

print(f"Tenant 1 layer: {tenant1_dir.get_layer()}")

594

595

multi_tenant_example(db)

596

```

597

598

**Directory Cleanup and Management (Python):**

599

```python

600

import fdb

601

602

fdb.api_version(630)

603

db = fdb.open()

604

605

@fdb.transactional

606

def cleanup_example(tr):

607

# Create temporary directories

608

temp_dir = fdb.directory.create_or_open(tr, ['temp'])

609

job1_dir = temp_dir.create_or_open(tr, 'job1')

610

job2_dir = temp_dir.create_or_open(tr, 'job2')

611

612

# Add some data

613

job1_dir[tr][b'status'] = b'completed'

614

job2_dir[tr][b'status'] = b'failed'

615

616

# List temporary jobs

617

jobs = temp_dir.list(tr)

618

print(f"Temporary jobs: {jobs}")

619

620

# Remove completed job

621

job1_dir.remove(tr)

622

623

# Move failed job to retry queue

624

retry_dir = fdb.directory.create_or_open(tr, ['retry'])

625

job2_dir.move(tr, ['retry', 'job2'])

626

627

# Clean up empty temp directory if needed

628

remaining_jobs = temp_dir.list(tr)

629

if not remaining_jobs:

630

temp_dir.remove(tr)

631

print("Cleaned up empty temp directory")

632

633

# Verify final state

634

try:

635

retry_job = fdb.directory.open(tr, ['retry', 'job2'])

636

print(f"Retry job exists at: {retry_job.get_path()}")

637

except fdb.DirectoryNotFoundError:

638

print("Retry job not found")

639

640

cleanup_example(db)

641

```

642

643

**Error Handling (Python):**

644

```python

645

import fdb

646

647

fdb.api_version(630)

648

db = fdb.open()

649

650

@fdb.transactional

651

def error_handling_example(tr):

652

try:

653

# Try to open non-existent directory

654

missing_dir = fdb.directory.open(tr, ['does', 'not', 'exist'])

655

except fdb.DirectoryNotFoundError as e:

656

print(f"Directory not found: {e}")

657

658

try:

659

# Create directory that already exists

660

existing_dir = fdb.directory.create_or_open(tr, ['existing'])

661

fdb.directory.create(tr, ['existing']) # This will fail

662

except fdb.DirectoryAlreadyExistsError as e:

663

print(f"Directory already exists: {e}")

664

665

try:

666

# Layer mismatch

667

layer_dir = fdb.directory.create_or_open(tr, ['layered'], layer=b'app')

668

fdb.directory.open(tr, ['layered'], layer=b'wrong_layer')

669

except fdb.DirectoryLayerMismatchError as e:

670

print(f"Layer mismatch: {e}")

671

672

error_handling_example(db)

673

```