mrevanzak

effect-ts-resources

Use when managing resource lifecycles (DB connections, file handles, sockets) where cleanup must be guaranteed despite failures, interruptions, or potential resource leaks.

mrevanzak 8 1 Updated 7mo ago
GitHub

Install

npx skillscat add mrevanzak/effect-ts-skills/effect-ts-resources

Install via the SkillsCat registry.

About this skill

This skill provides resource lifecycle management for Effect-TS, ensuring guaranteed cleanup of resources like database connections and file handles. It solves resource leaks and complex cleanup logic by using Scope and acquireRelease patterns that handle failures and interruptions. Use it when managing resources that require explicit release, such as opening files or database connections.

SKILL.md

Effect-TS Resource Management

Overview

Manage resource lifecycles using Scope and acquireRelease to guarantee cleanup. Cleanup always runs, even if the program fails, throws, or is interrupted.

When to Use

  • Symptoms: Resource leaks (hanging connections), manual try/finally blocks, complex cleanup logic.
  • Triggers: Opening files, connecting to databases, starting servers, acquiring locks.
  • When NOT to use: For simple data transformations or values that don't require explicit closing/release.

Core Pattern

Manual try/finally is error-prone in Effect because it bypasses interruption handling.

Anti-Pattern (Manual) Effect Pattern (Safe)
try { conn.open() } finally { conn.close() } Effect.acquireRelease(open, close)

Quick Reference

Function Purpose
Effect.acquireRelease Define acquisition and guaranteed release logic.
Effect.scoped Create a boundary where all resources in scope are released.
Effect.addFinalizer Add a cleanup action to the current scope.

Implementation

1. acquireRelease pattern

Guarantees cleanup even on failure or interruption.

import { Effect } from 'effect';

const withDatabaseConnection = Effect.acquireRelease(
  Effect.tryPromise(() => pool.connect()), // Acquire
  (connection) => Effect.sync(() => connection.release()) // Release
);

const program = Effect.gen(function* () {
  const conn = yield* withDatabaseConnection;
  return yield* conn.query('SELECT * FROM users');
});

2. Scoped resources

Multiple resources share a lifecycle boundary. Resources are released in LIFO (Last-In, First-Out) order.

import { Effect, Scope } from 'effect';

const program = Effect.scoped(
  Effect.gen(function* () {
    const db = yield* managedConnection;
    const cache = yield* managedCache;
    // Both cleaned up when scope closes (cache first, then db)
    return yield* doWork(db, cache);
  })
);

3. File handling

const withFile = (path: string) => Effect.acquireRelease(
  Effect.tryPromise(() => fs.open(path, 'r')),
  (handle) => Effect.promise(() => handle.close())
);

Common Mistakes

  • Using try/finally: Bypasses Effect's interruption model. Use acquireRelease.
  • Forgetting Effect.scoped: Resources won't be released until the parent scope closes (often the app end).
  • Wrong Order: Resources are released in reverse order of acquisition. Ensure dependencies are acquired first.

REQUIRED BACKGROUND: Use effect-ts-fundamentals for core generator patterns.