or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconcurrency.mdcore-effects.mddependency-injection.mderror-handling.mdindex.mdmetrics.mdresource-management.mdservices.mdstm.mdstreams.mdtesting.md

resource-management.mddocs/

0

# Resource Management

1

2

ZIO's Scope-based resource lifecycle management provides automatic cleanup and finalizer execution for leak-free applications with guaranteed resource cleanup even in the presence of failures and interruptions.

3

4

## Capabilities

5

6

### Scope - Resource Lifecycle Management

7

8

The foundational abstraction for managing resource lifecycles with automatic cleanup guarantees.

9

10

```scala { .api }

11

/**

12

* A scope represents a resource lifecycle boundary where resources can be safely allocated

13

* and automatically cleaned up when the scope closes

14

*/

15

sealed trait Scope {

16

/** Add a finalizer that runs when the scope closes */

17

def addFinalizer(finalizer: => UIO[Any]): UIO[Unit]

18

19

/** Add a finalizer that receives the exit status */

20

def addFinalizerExit(finalizer: Exit[Any, Any] => UIO[Any]): UIO[Unit]

21

22

/** Create a child scope */

23

def fork: UIO[Scope.Closeable]

24

25

/** Create a child scope with custom execution strategy */

26

def forkWith(executionStrategy: => ExecutionStrategy): UIO[Scope.Closeable]

27

28

/** Extend the scope to cover a larger region */

29

def extend[R]: Scope.ExtendPartiallyApplied[R]

30

}

31

32

/**

33

* A closeable scope that can be explicitly closed

34

*/

35

abstract class Scope.Closeable extends Scope {

36

/** Close the scope, running all finalizers */

37

def close(exit: => Exit[Any, Any]): UIO[Unit]

38

39

/** Number of finalizers registered */

40

def size: Int

41

42

/** Use this scope for an effect */

43

def use[R]: Scope.UsePartiallyApplied[R]

44

}

45

```

46

47

### Scope Factory Methods and Operations

48

49

Create and manage scopes for various resource management patterns.

50

51

```scala { .api }

52

/**

53

* Create a new closeable scope

54

*/

55

def make: UIO[Scope.Closeable]

56

57

/**

58

* Create a scope with custom execution strategy

59

*/

60

def makeWith(executionStrategy: => ExecutionStrategy): UIO[Scope.Closeable]

61

62

/**

63

* Create a scope that runs finalizers in parallel

64

*/

65

def parallel: UIO[Scope.Closeable]

66

67

/**

68

* Add a finalizer to the current scope

69

*/

70

def addFinalizer(finalizer: => UIO[Any]): ZIO[Scope, Nothing, Unit]

71

72

/**

73

* Add an exit-aware finalizer to the current scope

74

*/

75

def addFinalizerExit(finalizer: Exit[Any, Any] => UIO[Any]): ZIO[Scope, Nothing, Unit]

76

77

/**

78

* Access to global scope (never closes)

79

*/

80

val global: Scope.Closeable

81

82

/**

83

* Default scope layer for dependency injection

84

*/

85

val default: ZLayer[Any, Nothing, Scope]

86

87

/**

88

* Execute an effect with a fresh scope

89

*/

90

def scoped[R, E, A](zio: ZIO[R with Scope, E, A]): ZIO[R, E, A]

91

92

/**

93

* Execute an effect that requires a scope

94

*/

95

def scopedWith[R, E, A](f: Scope => ZIO[R, E, A]): ZIO[R, E, A]

96

```

97

98

**Usage Examples:**

99

100

```scala

101

import zio._

102

103

// Basic resource management with scope

104

val resourceProgram = ZIO.scoped {

105

for {

106

_ <- Scope.addFinalizer(Console.printLine("Cleaning up resources"))

107

resource <- ZIO.attempt(new FileInputStream("data.txt"))

108

_ <- Scope.addFinalizer(ZIO.succeed(resource.close()))

109

data <- ZIO.attempt(resource.read())

110

} yield data

111

}

112

113

// Manual scope control

114

val manualScopeProgram = for {

115

scope <- Scope.make

116

_ <- scope.addFinalizer(Console.printLine("Manual cleanup"))

117

118

result <- scope.use { implicit s =>

119

for {

120

resource <- acquireResource

121

data <- processResource(resource)

122

} yield data

123

}

124

125

_ <- scope.close(Exit.succeed(()))

126

} yield result

127

128

// Nested scopes with different lifecycles

129

val nestedScopes = ZIO.scoped {

130

for {

131

_ <- Scope.addFinalizer(Console.printLine("Outer scope cleanup"))

132

outer <- acquireOuterResource

133

134

result <- ZIO.scoped {

135

for {

136

_ <- Scope.addFinalizer(Console.printLine("Inner scope cleanup"))

137

inner <- acquireInnerResource

138

data <- processResources(outer, inner)

139

} yield data

140

}

141

} yield result

142

}

143

```

144

145

### Resource Acquisition Patterns

146

147

Common patterns for safe resource acquisition and management.

148

149

```scala { .api }

150

/**

151

* Acquire-release pattern for resource management

152

*/

153

def acquireRelease[R, E, A](

154

acquire: => ZIO[R, E, A]

155

)(release: A => URIO[R, Any]): ZIO[R with Scope, E, A]

156

157

/**

158

* Acquire-release with exit-aware cleanup

159

*/

160

def acquireReleaseExit[R, E, A](

161

acquire: => ZIO[R, E, A]

162

)(release: (A, Exit[Any, Any]) => URIO[R, Any]): ZIO[R with Scope, E, A]

163

164

/**

165

* Acquire multiple resources atomically

166

*/

167

def acquireReleaseAttempt[R, E, A](

168

acquire: ZIO[R, E, A]

169

)(release: A => URIO[R, Any]): ZIO[R with Scope, E, A]

170

171

/**

172

* Auto-closeable resource pattern (for Java interop)

173

*/

174

def fromAutoCloseable[R, E, A <: AutoCloseable](

175

fa: ZIO[R, E, A]

176

): ZIO[R with Scope, E, A]

177

178

/**

179

* Ensuring finalizer runs regardless of how effect completes

180

*/

181

def ensuring[R1 <: R](finalizer: => URIO[R1, Any]): ZIO[R1, E, A]

182

183

/**

184

* Add finalizer that only runs on specific exit cases

185

*/

186

def onExit[R1 <: R](cleanup: Exit[E, A] => URIO[R1, Any]): ZIO[R1, E, A]

187

```

188

189

**Usage Examples:**

190

191

```scala

192

// File processing with automatic cleanup

193

val fileProcessing = ZIO.scoped {

194

for {

195

file <- ZIO.acquireRelease(

196

ZIO.attempt(new BufferedReader(new FileReader("input.txt")))

197

)(reader => ZIO.succeed(reader.close()).ignore)

198

199

output <- ZIO.acquireRelease(

200

ZIO.attempt(new PrintWriter("output.txt"))

201

)(writer => ZIO.succeed(writer.close()).ignore)

202

203

_ <- processFile(file, output)

204

} yield ()

205

}

206

207

// Database connection management

208

val databaseWork = ZIO.scoped {

209

for {

210

connection <- ZIO.acquireRelease(

211

ZIO.attempt(DriverManager.getConnection(jdbcUrl))

212

)(conn => ZIO.succeed(conn.close()).ignore)

213

214

statement <- ZIO.acquireRelease(

215

ZIO.attempt(connection.prepareStatement(sql))

216

)(stmt => ZIO.succeed(stmt.close()).ignore)

217

218

results <- ZIO.attempt(statement.executeQuery())

219

data <- processResults(results)

220

} yield data

221

}

222

223

// Network resource management

224

val networkOperation = ZIO.scoped {

225

for {

226

socket <- ZIO.acquireReleaseExit(

227

ZIO.attempt(new Socket("localhost", 8080))

228

) { (socket, exit) =>

229

for {

230

_ <- Console.printLine(s"Closing socket. Exit: $exit")

231

_ <- ZIO.succeed(socket.close()).ignore

232

} yield ()

233

}

234

235

stream <- ZIO.fromAutoCloseable(

236

ZIO.attempt(socket.getInputStream())

237

)

238

239

data <- readFromStream(stream)

240

} yield data

241

}

242

```

243

244

### Advanced Resource Patterns

245

246

Complex resource management patterns for sophisticated applications.

247

248

```scala { .api }

249

// Resource pool pattern

250

class ResourcePool[R, E, A](

251

factory: ZIO[R, E, A],

252

cleanup: A => URIO[R, Any],

253

maxSize: Int

254

) {

255

private val available = Queue.bounded[A](maxSize)

256

private val inUse = Ref.make(Set.empty[A])

257

258

def acquire: ZIO[R with Scope, E, A] = ZIO.scoped {

259

for {

260

resource <- available.take.orElse(factory)

261

_ <- inUse.update(_ + resource)

262

_ <- Scope.addFinalizer(release(resource))

263

} yield resource

264

}

265

266

def release(resource: A): URIO[R, Unit] = {

267

for {

268

_ <- inUse.update(_ - resource)

269

_ <- available.offer(resource).ignore

270

} yield ()

271

}

272

273

def shutdown: URIO[R, Unit] = {

274

for {

275

resources <- available.takeAll

276

_ <- ZIO.foreachDiscard(resources)(cleanup)

277

} yield ()

278

}

279

}

280

281

// Cached resource pattern

282

class CachedResource[R, E, A](

283

factory: ZIO[R, E, A],

284

cleanup: A => URIO[R, Any],

285

ttl: Duration

286

) {

287

private val cache = Ref.make(Option.empty[(A, Long)])

288

289

def get: ZIO[R with Scope, E, A] = ZIO.scoped {

290

for {

291

now <- Clock.currentTime(TimeUnit.MILLISECONDS)

292

cached <- cache.get

293

294

resource <- cached match {

295

case Some((resource, timestamp)) if (now - timestamp) < ttl.toMillis =>

296

ZIO.succeed(resource)

297

case _ =>

298

for {

299

newResource <- factory

300

_ <- cache.set(Some((newResource, now)))

301

_ <- Scope.addFinalizer(cleanup(newResource))

302

} yield newResource

303

}

304

} yield resource

305

}

306

307

def invalidate: UIO[Unit] = cache.set(None)

308

}

309

310

// Scoped reference pattern

311

class ScopedRef[A](initialValue: A) {

312

private val ref = Ref.make(initialValue)

313

314

def scoped: ZIO[Scope, Nothing, Ref[A]] = {

315

for {

316

currentValue <- ref.get

317

scopedRef <- Ref.make(currentValue)

318

_ <- Scope.addFinalizerExit { exit =>

319

for {

320

finalValue <- scopedRef.get

321

_ <- ref.set(finalValue)

322

} yield ()

323

}

324

} yield scopedRef

325

}

326

327

def get: UIO[A] = ref.get

328

def set(a: A): UIO[Unit] = ref.set(a)

329

}

330

```

331

332

**Usage Examples:**

333

334

```scala

335

// HTTP client with connection pooling

336

val httpClientProgram = for {

337

pool <- ZIO.succeed(new ResourcePool(

338

factory = ZIO.attempt(HttpClientBuilder.create().build()),

339

cleanup = client => ZIO.succeed(client.close()).ignore,

340

maxSize = 10

341

))

342

343

results <- ZIO.foreachPar(urls) { url =>

344

ZIO.scoped {

345

for {

346

client <- pool.acquire

347

request <- ZIO.succeed(HttpGet(url))

348

response <- ZIO.attempt(client.execute(request))

349

body <- ZIO.attempt(EntityUtils.toString(response.getEntity))

350

} yield body

351

}

352

}

353

354

_ <- pool.shutdown

355

} yield results

356

357

// Cached database connection

358

val cachedDbProgram = for {

359

connectionCache <- ZIO.succeed(new CachedResource(

360

factory = ZIO.attempt(DriverManager.getConnection(url)),

361

cleanup = conn => ZIO.succeed(conn.close()).ignore,

362

ttl = 5.minutes

363

))

364

365

results <- ZIO.foreachPar(queries) { query =>

366

ZIO.scoped {

367

for {

368

conn <- connectionCache.get

369

stmt <- ZIO.attempt(conn.prepareStatement(query))

370

result <- ZIO.attempt(stmt.executeQuery())

371

data <- extractData(result)

372

} yield data

373

}

374

}

375

} yield results

376

377

// Hierarchical resource management

378

val hierarchicalResources = ZIO.scoped {

379

for {

380

// Top-level resources

381

_ <- Scope.addFinalizer(Console.printLine("Shutting down application"))

382

config <- loadConfiguration

383

384

// Mid-level resources

385

database <- ZIO.scoped {

386

for {

387

_ <- Scope.addFinalizer(Console.printLine("Closing database"))

388

db <- initializeDatabase(config.dbConfig)

389

} yield db

390

}

391

392

// Low-level resources

393

result <- ZIO.scoped {

394

for {

395

_ <- Scope.addFinalizer(Console.printLine("Cleaning up transaction"))

396

session <- database.createSession()

397

_ <- Scope.addFinalizer(ZIO.succeed(session.close()).ignore)

398

data <- session.query("SELECT * FROM users")

399

} yield data

400

}

401

} yield result

402

}

403

```