or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-operations.mdcursors.mdindex.mdmulti-database.mdtransactions.md

multi-database.mddocs/

0

# Multi-Database Support

1

2

LMDB environments can contain multiple named databases for data organization and isolation. This enables logical separation of different data types while maintaining ACID transaction guarantees across all databases within a single environment.

3

4

## Capabilities

5

6

### Database Configuration

7

8

Configure environment to support multiple databases and create named databases with specialized options.

9

10

```python { .api }

11

def open(path: str, max_dbs: int = 0, **kwargs) -> Environment:

12

"""

13

Open environment with multi-database support.

14

15

Parameters:

16

- path: Environment path

17

- max_dbs: Maximum number of named databases (0 = default database only)

18

- **kwargs: Other environment options

19

20

Returns:

21

Environment instance

22

23

Note:

24

max_dbs must be set > 0 to use named databases

25

"""

26

27

class Environment:

28

def open_db(self, key: bytes = None, txn=None, reverse_key: bool = False,

29

dupsort: bool = False, create: bool = True, integerkey: bool = False,

30

integerdup: bool = False, dupfixed: bool = False) -> _Database:

31

"""

32

Open or create named database within environment.

33

34

Parameters:

35

- key: Database name (None for default database)

36

- txn: Transaction to use (temporary read-write transaction created if None)

37

- reverse_key: Keys stored in reverse byte order

38

- dupsort: Allow and sort duplicate keys

39

- create: Create database if it doesn't exist

40

- integerkey: Keys are native byte-order integers

41

- integerdup: Duplicate values are native integers (requires dupsort)

42

- dupfixed: All duplicate values same size (requires dupsort)

43

44

Returns:

45

Database handle for use in transactions

46

47

Raises:

48

Error if max_dbs=0 and key is not None

49

"""

50

```

51

52

### Database Handle Management

53

54

Work with database handles across transactions and manage database metadata.

55

56

```python { .api }

57

class _Database:

58

"""

59

Database handle representing a named database within an LMDB environment.

60

61

Database handles are returned by Environment.open_db() and used to specify

62

which database to operate on in transactions and cursors. Database handles

63

are lightweight objects that can be safely passed between transactions.

64

65

Note:

66

Database handles remain valid until the environment is closed.

67

"""

68

69

def flags(self, *args) -> dict:

70

"""

71

Get database configuration flags.

72

73

Returns:

74

Dictionary containing database flags:

75

- reverse_key: Keys stored in reverse order

76

- dupsort: Duplicate keys allowed and sorted

77

- integerkey: Keys are native integers

78

- integerdup: Duplicate values are native integers

79

- dupfixed: All duplicate values are same size

80

"""

81

```

82

83

### Cross-Database Transactions

84

85

Perform atomic operations across multiple databases within single transactions.

86

87

```python { .api }

88

class Transaction:

89

def get(self, key: bytes, default=None, db=None) -> bytes:

90

"""

91

Get value from specific database.

92

93

Parameters:

94

- key: Key to retrieve

95

- default: Default value if key not found

96

- db: Database handle (uses transaction default if None)

97

"""

98

99

def put(self, key: bytes, value: bytes, db=None, **kwargs) -> bool:

100

"""

101

Store key-value pair in specific database.

102

103

Parameters:

104

- key: Key bytes

105

- value: Value bytes

106

- db: Database handle (uses transaction default if None)

107

- **kwargs: Additional put options

108

"""

109

110

def delete(self, key: bytes, value: bytes = b'', db=None) -> bool:

111

"""

112

Delete key from specific database.

113

114

Parameters:

115

- key: Key to delete

116

- value: Specific value for duplicate keys

117

- db: Database handle (uses transaction default if None)

118

"""

119

120

def cursor(self, db=None) -> Cursor:

121

"""

122

Create cursor for specific database.

123

124

Parameters:

125

- db: Database handle (uses transaction default if None)

126

"""

127

128

def stat(self, db) -> dict:

129

"""

130

Get statistics for specific database.

131

132

Parameters:

133

- db: Database handle

134

135

Returns:

136

Database statistics dictionary

137

"""

138

139

def drop(self, db, delete: bool = True) -> None:

140

"""

141

Empty or delete specific database.

142

143

Parameters:

144

- db: Database handle

145

- delete: If True delete database, if False just empty it

146

147

Note:

148

Cannot delete default database (key=None), only empty it

149

"""

150

```

151

152

### Usage Examples

153

154

#### Basic Multi-Database Setup

155

156

```python

157

import lmdb

158

159

# Open environment with support for multiple databases

160

env = lmdb.open('/path/to/database', max_dbs=10)

161

162

# Create different databases for different data types

163

users_db = env.open_db(b'users')

164

products_db = env.open_db(b'products')

165

orders_db = env.open_db(b'orders')

166

settings_db = env.open_db(b'settings')

167

168

# Use default database (no name)

169

default_db = env.open_db() # or env.open_db(None)

170

171

print("Databases created successfully")

172

env.close()

173

```

174

175

#### Atomic Cross-Database Operations

176

177

```python

178

import lmdb

179

import json

180

181

env = lmdb.open('/path/to/database', max_dbs=5)

182

183

users_db = env.open_db(b'users')

184

accounts_db = env.open_db(b'accounts')

185

transactions_db = env.open_db(b'transactions')

186

187

# Atomic money transfer across multiple databases

188

def transfer_money(from_user: str, to_user: str, amount: float, tx_id: str):

189

with env.begin(write=True) as txn:

190

# Get current balances

191

from_account = json.loads(txn.get(from_user.encode(), db=accounts_db) or b'{"balance": 0}')

192

to_account = json.loads(txn.get(to_user.encode(), db=accounts_db) or b'{"balance": 0}')

193

194

# Check sufficient funds

195

if from_account['balance'] < amount:

196

raise ValueError("Insufficient funds")

197

198

# Update balances

199

from_account['balance'] -= amount

200

to_account['balance'] += amount

201

202

# Store updated balances

203

txn.put(from_user.encode(), json.dumps(from_account).encode(), db=accounts_db)

204

txn.put(to_user.encode(), json.dumps(to_account).encode(), db=accounts_db)

205

206

# Log transaction

207

tx_record = {

208

'id': tx_id,

209

'from': from_user,

210

'to': to_user,

211

'amount': amount,

212

'timestamp': time.time()

213

}

214

txn.put(tx_id.encode(), json.dumps(tx_record).encode(), db=transactions_db)

215

216

print(f"Transfer completed: {from_user} -> {to_user}, ${amount}")

217

218

# Create users and initial accounts

219

with env.begin(write=True) as txn:

220

txn.put(b'alice', b'{"name": "Alice", "email": "alice@example.com"}', db=users_db)

221

txn.put(b'bob', b'{"name": "Bob", "email": "bob@example.com"}', db=users_db)

222

223

txn.put(b'alice', b'{"balance": 1000.0}', db=accounts_db)

224

txn.put(b'bob', b'{"balance": 500.0}', db=accounts_db)

225

226

# Perform transfer

227

transfer_money('alice', 'bob', 100.0, 'tx001')

228

229

env.close()

230

```

231

232

#### Database-Specific Configuration

233

234

```python

235

import lmdb

236

237

env = lmdb.open('/path/to/database', max_dbs=5)

238

239

# Create databases with different configurations

240

users_db = env.open_db(b'users') # Standard key-value

241

tags_db = env.open_db(b'tags', dupsort=True) # Allow duplicate keys

242

counters_db = env.open_db(b'counters', integerkey=True) # Integer keys

243

reverse_db = env.open_db(b'reverse', reverse_key=True) # Reverse key order

244

245

# Store data using different database configurations

246

with env.begin(write=True) as txn:

247

# Standard database

248

txn.put(b'user1', b'Alice', db=users_db)

249

txn.put(b'user2', b'Bob', db=users_db)

250

251

# Database with duplicate keys (tags for posts)

252

txn.put(b'post1', b'python', db=tags_db)

253

txn.put(b'post1', b'tutorial', db=tags_db) # Same key, different value

254

txn.put(b'post1', b'beginner', db=tags_db)

255

256

# Integer key database

257

import struct

258

key1 = struct.pack('i', 1)

259

key2 = struct.pack('i', 100)

260

key3 = struct.pack('i', 50)

261

txn.put(key1, b'counter_one', db=counters_db)

262

txn.put(key2, b'counter_hundred', db=counters_db)

263

txn.put(key3, b'counter_fifty', db=counters_db)

264

265

# Reverse key order database

266

txn.put(b'zebra', b'last_animal', db=reverse_db)

267

txn.put(b'apple', b'first_fruit', db=reverse_db)

268

txn.put(b'banana', b'second_fruit', db=reverse_db)

269

270

# Read and verify data

271

with env.begin() as txn:

272

print("Standard database:")

273

for key, value in txn.cursor(db=users_db):

274

print(f" {key} = {value}")

275

276

print("\\nDuplicate keys database:")

277

for key, value in txn.cursor(db=tags_db):

278

print(f" {key} = {value}")

279

280

print("\\nInteger keys database (sorted by integer value):")

281

for key, value in txn.cursor(db=counters_db):

282

int_key = struct.unpack('i', key)[0]

283

print(f" {int_key} = {value}")

284

285

print("\\nReverse order database:")

286

for key, value in txn.cursor(db=reverse_db):

287

print(f" {key} = {value}")

288

289

env.close()

290

```

291

292

#### Database Statistics and Management

293

294

```python

295

import lmdb

296

297

env = lmdb.open('/path/to/database', max_dbs=3)

298

299

users_db = env.open_db(b'users')

300

posts_db = env.open_db(b'posts')

301

comments_db = env.open_db(b'comments')

302

303

# Add sample data

304

with env.begin(write=True) as txn:

305

# Users

306

for i in range(100):

307

txn.put(f'user{i}'.encode(), f'User {i}'.encode(), db=users_db)

308

309

# Posts

310

for i in range(50):

311

txn.put(f'post{i}'.encode(), f'Post {i} content'.encode(), db=posts_db)

312

313

# Comments

314

for i in range(200):

315

post_id = i % 50

316

txn.put(f'comment{i}'.encode(), f'Comment on post{post_id}'.encode(), db=comments_db)

317

318

# Get statistics for each database

319

with env.begin() as txn:

320

databases = [

321

('users', users_db),

322

('posts', posts_db),

323

('comments', comments_db)

324

]

325

326

for name, db in databases:

327

stats = txn.stat(db)

328

print(f"\\n{name.capitalize()} database statistics:")

329

print(f" Entries: {stats['entries']}")

330

print(f" Leaf pages: {stats['leaf_pages']}")

331

print(f" Branch pages: {stats['branch_pages']}")

332

print(f" Overflow pages: {stats['overflow_pages']}")

333

print(f" Tree depth: {stats['depth']}")

334

335

# Empty a database

336

with env.begin(write=True) as txn:

337

print(f"\\nComments before drop: {txn.stat(comments_db)['entries']}")

338

txn.drop(comments_db, delete=False) # Empty but don't delete

339

print(f"Comments after drop: {txn.stat(comments_db)['entries']}")

340

341

env.close()

342

```

343

344

#### Database Isolation Example

345

346

```python

347

import lmdb

348

import threading

349

import time

350

351

env = lmdb.open('/path/to/database', max_dbs=2)

352

353

db1 = env.open_db(b'database1')

354

db2 = env.open_db(b'database2')

355

356

def worker(worker_id: int, database):

357

"""Worker function that operates on specific database"""

358

with env.begin(write=True) as txn:

359

for i in range(10):

360

key = f'worker{worker_id}_item{i}'.encode()

361

value = f'data from worker {worker_id}'.encode()

362

txn.put(key, value, db=database)

363

time.sleep(0.1) # Simulate work

364

365

print(f"Worker {worker_id} completed")

366

367

# Create threads working on different databases

368

thread1 = threading.Thread(target=worker, args=(1, db1))

369

thread2 = threading.Thread(target=worker, args=(2, db2))

370

371

# Start concurrent operations on different databases

372

thread1.start()

373

thread2.start()

374

375

thread1.join()

376

thread2.join()

377

378

# Verify data isolation

379

with env.begin() as txn:

380

print("Database 1 contents:")

381

for key, value in txn.cursor(db=db1):

382

print(f" {key} = {value}")

383

384

print("\\nDatabase 2 contents:")

385

for key, value in txn.cursor(db=db2):

386

print(f" {key} = {value}")

387

388

env.close()

389

```