# Reference Counting

Sometimes the thing you want to reuse is not a *value* but a *resource*: a
database connection, an HTTP client, a worker, a websocket. Many fibers may need
it at once, it should be acquired only once, and it must be released — but only
after the *last* user is done with it. That is exactly what reference counting
provides.

- **`RcRef<A, E>`** shares a *single* scoped resource.
- **`RcMap<K, A, E>`** shares scoped resources *keyed* by `K`.
- **`ScopedRef<A>`** is the lower-level primitive: a mutable cell holding one
  scoped resource that you can swap out, closing the previous scope.

`RcRef` and `RcMap` are lazy (the resource is acquired on first `get`),
reference-counted (each borrowing scope adds a reference and a release
finalizer), and self-cleaning (the resource is finalized when the count hits
zero, optionally after an idle delay).

## RcRef: one shared resource

`RcRef.make` takes an `acquire` effect — typically an
[`Effect.acquireRelease`](https://effect.plants.sh/resource-management/) that defines how to open and
close the resource. `RcRef.get` borrows the current resource for the caller's
scope, acquiring it the first time it is needed.

```ts
import { Effect, RcRef } from "effect"

// `acquire` describes how to open and close one connection. RcRef makes
// sure it is opened at most once and closed when the last borrower leaves.
const program = Effect.gen(function*() {
  const ref = yield* RcRef.make({
    acquire: Effect.acquireRelease(
      Effect.as(Effect.log("connecting"), { id: 1 }),
      () => Effect.log("disconnecting")
    )
  })

  // Two borrows in nested scopes: the connection is acquired once for the
  // first get and reused for the second. It is released only when both
  // borrowing scopes have closed.
  yield* Effect.scoped(
    Effect.gen(function*() {
      const a = yield* RcRef.get(ref)
      const b = yield* RcRef.get(ref)
      // a === b: the same acquired resource
    })
  )
}).pipe(Effect.scoped)
```

Note the two scopes at play. `RcRef.make` captures the *surrounding* scope (here
the outer `Effect.scoped`) — that is the resource's ultimate owner. Each
`RcRef.get` requires a `Scope` of its own (the inner `Effect.scoped`); the
reference is held for as long as that inner scope is open.
**Note:** `RcRef.get` has `Scope` in its requirements. Run it under `Effect.scoped` or
inside an already-scoped workflow. The borrowed resource is returned to the pool
when that scope closes — not when `get` returns.

### Idle time-to-live and invalidation

By default, the moment the reference count drops to zero the resource is
released. Pass `idleTimeToLive` to keep it alive for a grace period, so a quick
re-acquisition reuses the still-open resource instead of paying to reopen it:

```ts
import { Effect, RcRef } from "effect"

const makeClientRef = RcRef.make({
  acquire: Effect.acquireRelease(
    Effect.succeed({ requests: 0 }),
    () => Effect.log("closing client")
  ),
  // Keep the client around for 30 seconds after the last borrower
  // releases it, in case another request comes in soon.
  idleTimeToLive: "30 seconds"
})
```

`RcRef.invalidate` forces the next `get` to acquire a *fresh* resource — useful
when the current one is stale or broken. It does **not** revoke resources already
borrowed by active scopes; those keep using the old value until their scopes
close.

## RcMap: shared resources by key

`RcMap` is the keyed version: one acquired resource per key, each independently
reference-counted. Reach for it when you pool resources by some identifier — a
connection per database, a client per region, a session per tenant.

```ts
import { Effect, RcMap } from "effect"

const program = Effect.gen(function*() {
  // One connection per database name. Idle connections are released
  // 5 minutes after their last reference; at most 10 are held at once.
  const connections = yield* RcMap.make({
    lookup: (dbName: string) =>
      Effect.acquireRelease(
        Effect.as(Effect.log(`open ${dbName}`), { dbName }),
        () => Effect.log(`close ${dbName}`)
      ),
    idleTimeToLive: "5 minutes",
    capacity: 10
  })

  // Getting the same key twice acquires the resource once and shares it.
  yield* Effect.scoped(
    Effect.gen(function*() {
      yield* RcMap.get(connections, "orders")
      yield* RcMap.get(connections, "orders") // reuses the same connection
      yield* RcMap.get(connections, "billing") // a different connection
    })
  )
}).pipe(Effect.scoped)
```

As with `RcRef`, the map itself is scoped (it releases every remaining entry when
its owning scope closes), and each `RcMap.get` requires a `Scope` that holds the
reference.

### Capacity, idle TTL, and explicit control

`RcMap.make` accepts the same `idleTimeToLive` (a duration or a function of the
key) plus an optional `capacity`:

- `capacity` bounds how many keys can be live at once. Exceeding it makes
  `RcMap.get` fail with a `Cause.ExceededCapacityError` rather than evicting an
  in-use resource — reference counting means the map cannot safely drop something
  still in use.
- `RcMap.invalidate(map, key)` removes a key; if nothing is currently using it,
  it is released immediately, and the next `get` re-acquires it.
- `RcMap.touch(map, key)` resets the idle timer for a key, keeping an otherwise
  idle resource alive longer.
- `RcMap.keys(map)` and `RcMap.has(map, key)` inspect the current contents.

A typical use is a service that hands out pooled resources:

```ts
import { Context, Effect, Layer, RcMap, Scope } from "effect"

interface Connection {
  readonly query: (sql: string) => Effect.Effect<ReadonlyArray<unknown>>
}

// A connection pool keyed by database name. Callers borrow a connection
// for the duration of their own scope; the pool reuses and releases
// connections under the hood.
class Pool extends Context.Service<Pool, {
  readonly get: (db: string) => Effect.Effect<Connection, never, Scope.Scope>
}>()("app/Pool") {
  // Layer.effect runs construction in the layer's own scope, so the
  // RcMap (and everything it holds) is torn down when the layer closes.
  static layer = Layer.effect(
    Pool,
    Effect.gen(function*() {
      const map = yield* RcMap.make({
        lookup: (db: string) =>
          Effect.acquireRelease(
            Effect.succeed<Connection>({
              query: (_sql) => Effect.succeed([])
            }),
            () => Effect.log(`closing pool connection for ${db}`)
          ),
        idleTimeToLive: "2 minutes"
      })

      return Pool.of({ get: (db) => RcMap.get(map, db) })
    })
  )
}
```
**Caution:** When `RcMap` keys are objects, give them `Equal`/`Hash` behavior (for example via
a [`Data`](https://effect.plants.sh/data-types/) class) so lookups match by value. Plain object literals
compare by reference and each `get` would acquire a new resource.

## RcRef and RcMap vs. Cache

Reference counting is about *resources and their lifecycle*, not about memoizing
computed values:

- Use [`Cache` / `ScopedCache`](https://effect.plants.sh/caching/cache/) when you want to remember the
  *result* of a lookup (including failures), with TTL-based and capacity-based
  eviction.
- Use `RcRef` / `RcMap` when you want to *share a live resource* and have it
  released precisely when the last borrower is done, with idle grace periods.

If you need a single value kept loaded in memory and refreshed periodically, see
[`Resource`](https://effect.plants.sh/caching/resource/).

## ScopedRef: a mutable cell holding one scoped resource

`ScopedRef<A>` is the lower-level primitive that `RcRef` and
[`Resource`](https://effect.plants.sh/caching/resource/) build on. Think of it as a `Ref` whose value
*owns a `Scope`*: setting a new value runs a fresh acquisition in a new scope and
closes the previous value's scope, so swapping a resource never leaks the old
one. Unlike `RcRef`/`RcMap`, there is no reference counting — exactly one value is
held at a time, and you control when it is replaced.

```ts
import { Effect, ScopedRef } from "effect"

const program = Effect.gen(function*() {
  // Start holding one acquired connection.
  const ref = yield* ScopedRef.fromAcquire(
    Effect.acquireRelease(
      Effect.as(Effect.log("open v1"), "conn-v1"),
      (c) => Effect.log(`close ${c}`)
    )
  )

  const current = yield* ScopedRef.get(ref) // => "conn-v1"

  // Swap in a new connection; the old one's finalizer runs as part of set.
  yield* ScopedRef.set(
    ref,
    Effect.acquireRelease(
      Effect.as(Effect.log("open v2"), "conn-v2"),
      (c) => Effect.log(`close ${c}`)
    )
  )
  // => logs "close conn-v1" then completes with v2 acquired

  const next = yield* ScopedRef.get(ref) // => "conn-v2"
}).pipe(Effect.scoped) // closing the outer scope finalizes whatever value is current
```

A `ScopedRef` is itself created within a `Scope`; when that scope closes, the
currently held value's scope is closed too.

### ScopedRef.make

`ScopedRef.make(evaluate)` creates a `ScopedRef` from a `LazyArg<A>` — a plain
value that acquires no resources. Returns `Effect<ScopedRef<A>, never, Scope>`.
Use this only for non-resourceful values; for acquired values use `fromAcquire`.

```ts
import { Effect, ScopedRef } from "effect"

Effect.gen(function*() {
  const ref = yield* ScopedRef.make(() => 0) // => ScopedRef holding 0
  console.log(yield* ScopedRef.get(ref)) // => 0
}).pipe(Effect.scoped)
```

### ScopedRef.fromAcquire

`ScopedRef.fromAcquire(acquire)` initializes the cell from an acquiring effect,
running `acquire` in a fresh scope owned by the ref. Returns
`Effect<ScopedRef<A>, E, Scope | R>`. If acquisition fails, the new scope is
closed with that failure.

```ts
import { Effect, ScopedRef } from "effect"

Effect.gen(function*() {
  const ref = yield* ScopedRef.fromAcquire(
    Effect.acquireRelease(Effect.succeed("client"), () => Effect.log("close client"))
  )
  console.log(yield* ScopedRef.get(ref)) // => "client"
}).pipe(Effect.scoped)
```

### ScopedRef.get

`ScopedRef.get(self)` reads the current value, returning `Effect<A>` (no error or
requirement channel — the value is already held).

```ts
import { Effect, ScopedRef } from "effect"

Effect.gen(function*() {
  const ref = yield* ScopedRef.make(() => "hello")
  const v = yield* ScopedRef.get(ref) // => "hello"
}).pipe(Effect.scoped)
```

### ScopedRef.getUnsafe

`ScopedRef.getUnsafe(self)` reads the current value synchronously, returning `A`
directly without wrapping it in an `Effect`. Use only where an unsafe read is
acceptable.

```ts
import { Effect, ScopedRef } from "effect"

Effect.gen(function*() {
  const ref = yield* ScopedRef.make(() => 42)
  const v = ScopedRef.getUnsafe(ref) // => 42 (plain number, not an Effect)
}).pipe(Effect.scoped)
```

### ScopedRef.set

`ScopedRef.set(self, acquire)` (also curryable as `ScopedRef.set(acquire)`) runs
`acquire` in a fresh scope and closes the old value's scope. The operation is
uninterruptible and guarded by a semaphore, so concurrent sets are serialized; it
returns `Effect<void, E, Exclude<R, Scope>>` and does not complete until the new
value is acquired and the old resources are released.

```ts
import { Effect, ScopedRef } from "effect"

Effect.gen(function*() {
  const ref = yield* ScopedRef.fromAcquire(
    Effect.acquireRelease(Effect.succeed("v1"), (v) => Effect.log(`close ${v}`))
  )

  yield* ScopedRef.set(
    ref,
    Effect.acquireRelease(Effect.succeed("v2"), (v) => Effect.log(`close ${v}`))
  )
  // => logs "close v1"; ref now holds "v2"

  console.log(yield* ScopedRef.get(ref)) // => "v2"
}).pipe(Effect.scoped)
```

### ScopedRef (type)

`ScopedRef<in out A>` is the cell handle. Internally it wraps a
`SynchronizedRef` holding a `[Scope.Closeable, A]` pair (the current value
together with the scope that owns its resources); you interact with it only
through the module's functions.

```ts
import type { ScopedRef } from "effect"

// A ScopedRef whose current value is a number-backed resource.
type Counter = ScopedRef.ScopedRef<number>
```

`Resource` is built on this same swap-in-a-fresh-scope, close-the-old-scope
pattern: a `Resource` is essentially a `ScopedRef` whose value is refreshed on a
schedule. See [`Resource`](https://effect.plants.sh/caching/resource/) for the higher-level, auto-refreshing
variant.

## RcRef reference

The `RcRef` module is small: a constructor, a borrowing getter, and an
invalidation operation.

### RcRef.make

`RcRef.make({ acquire, idleTimeToLive? })` stores the acquisition effect,
captures the surrounding `Scope`, and returns an
`Effect<RcRef<A, E>, never, R | Scope>`. The resource is acquired lazily on the
first `get`; `idleTimeToLive` (a `Duration.Input`) keeps a zero-reference
resource alive for that long before release.

```ts
import { Effect, RcRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* RcRef.make({
    acquire: Effect.acquireRelease(
      Effect.as(Effect.log("acquire"), "resource"), // => logs "acquire"
      () => Effect.log("release")
    ),
    idleTimeToLive: "10 seconds"
  })

  yield* Effect.scoped(RcRef.get(ref)) // => logs "release" 10s after scope closes
}).pipe(Effect.scoped)
```

### RcRef.get

`RcRef.get(self)` borrows the current resource for the caller's `Scope`,
acquiring it on demand. It returns `Effect<A, E, Scope>`; the reference is
released when the borrowing scope closes, and the resource is finalized once the
last reference is gone (subject to `idleTimeToLive`).

```ts
import { Effect, RcRef } from "effect"

Effect.gen(function*() {
  const ref = yield* RcRef.make({
    acquire: Effect.acquireRelease(
      Effect.succeed("conn"),
      () => Effect.log("close conn") // => logged once, after both borrowers leave
    )
  })

  // Two concurrent borrowers share a single acquisition.
  yield* Effect.all(
    [Effect.scoped(RcRef.get(ref)), Effect.scoped(RcRef.get(ref))],
    { concurrency: 2 }
  )
  // => acquired once, "close conn" logged once
}).pipe(Effect.scoped)
```

### RcRef.invalidate

`RcRef.invalidate(self)` drops the currently cached resource so the next `get`
re-acquires a fresh one. Returns `Effect<void>`. Active borrowers keep the old
resource until their scopes close.

```ts
import { Effect, RcRef } from "effect"

Effect.gen(function*() {
  const ref = yield* RcRef.make({
    acquire: Effect.acquireRelease(Effect.succeed(Math.random()), () => Effect.void)
  })

  yield* RcRef.invalidate(ref) // => next get re-acquires from scratch
}).pipe(Effect.scoped)
```

### RcRef (type)

`RcRef<out A, out E = never>` is the handle returned by `make`. `A` is the
resource type and `E` the error its acquisition can produce. It carries no public
fields; interact with it through `get` and `invalidate`.

```ts
import type { RcRef } from "effect"

// A reference-counted handle to a string resource that cannot fail.
type StringRef = RcRef.RcRef<string>
```

## RcMap reference

`RcMap` mirrors `RcRef` but keys resources by `K`, exposing extra operations to
inspect and manage individual keys.

### RcMap.make

`RcMap.make({ lookup, idleTimeToLive?, capacity? })` creates a scoped,
reference-counted map. `lookup: (key: K) => Effect<A, E, R>` acquires a resource
for a key on first access. `idleTimeToLive` accepts either a single
`Duration.Input` or a per-key function `(key: K) => Duration.Input`. Passing
`capacity` switches to an overload whose error channel adds
`Cause.ExceededCapacityError` — a `get` beyond the limit fails rather than
evicting an in-use resource.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({
    lookup: (region: string) =>
      Effect.acquireRelease(
        Effect.succeed(`client:${region}`),
        () => Effect.log(`close ${region}`)
      ),
    // Per-key idle TTL: keep "us-east" longer than other regions.
    idleTimeToLive: (region) => region === "us-east" ? "5 minutes" : "30 seconds",
    capacity: 100 // => get fails with ExceededCapacityError past 100 live keys
  })

  yield* Effect.scoped(RcMap.get(map, "us-east")) // => "client:us-east"
}).pipe(Effect.scoped)
```

### RcMap.get

`RcMap.get(map, key)` (also curryable as `RcMap.get(key)`) acquires the resource
for `key` via `lookup`, or retains the already-cached one, incrementing its
reference count for the current `Scope`. Returns `Effect<A, E, Scope>`.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({
    lookup: (key: string) => Effect.succeed(`value-${key}`)
  })

  const v = yield* RcMap.get(map, "foo") // => "value-foo"
}).pipe(Effect.scoped)
```

### RcMap.keys

`RcMap.keys(map)` returns `Effect<Iterable<K>>` of the keys currently held. If the
map has been closed, the effect is interrupted.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({ lookup: (k: string) => Effect.succeed(k) })

  yield* RcMap.get(map, "foo")
  yield* RcMap.get(map, "bar")

  const ks = yield* RcMap.keys(map) // => ["foo", "bar"]
}).pipe(Effect.scoped)
```

### RcMap.invalidate

`RcMap.invalidate(map, key)` removes a key from the map. If its reference count is
zero it is released immediately; the next `get` performs a fresh lookup. Returns
`Effect<void>`.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({
    lookup: (key: string) =>
      Effect.acquireRelease(Effect.succeed(key), () => Effect.log(`close ${key}`))
  })

  yield* RcMap.invalidate(map, "cache") // => releases "cache" if idle
}).pipe(Effect.scoped)
```

### RcMap.has

`RcMap.has(map, key)` returns `Effect<boolean>` indicating whether the key is
currently held, without running the lookup. A closed map always returns `false`.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({ lookup: (k: string) => Effect.succeed(k) })

  const before = yield* RcMap.has(map, "foo") // => false
  yield* RcMap.get(map, "foo")
  const after = yield* RcMap.has(map, "foo") // => true
}).pipe(Effect.scoped)
```

### RcMap.touch

`RcMap.touch(map, key)` resets the idle timer for a key, keeping an otherwise idle
resource alive for another full `idleTimeToLive` from now. Returns `Effect<void>`;
it is a no-op when the key is absent or has a zero TTL.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({
    lookup: (k: string) => Effect.acquireRelease(Effect.succeed(k), () => Effect.void),
    idleTimeToLive: "10 seconds"
  })

  yield* RcMap.touch(map, "session") // => resets the 10s expiry for "session"
}).pipe(Effect.scoped)
```

### RcMap (type)

`RcMap<in out K, in out A, in out E = never>` is the map handle. It exposes the
configured `lookup`, `capacity`, `idleTimeToLive` (always normalized to a
per-key function on the value), and a mutable `state` field.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({
    lookup: (k: string) => Effect.succeed(k),
    capacity: 10
  })

  console.log(map.capacity) // => 10
}).pipe(Effect.scoped)
```

### RcMap.State

`State<K, A, E>` is the union `State.Open<K, A, E> | State.Closed` exposed on
`map.state`. `Open` carries a `MutableHashMap` of entries while the map is active;
`Closed` (`{ _tag: "Closed" }`) means the owning scope has closed and operations
no longer mutate the map.

```ts
import { Effect, RcMap } from "effect"

Effect.gen(function*() {
  const map = yield* RcMap.make({ lookup: (k: string) => Effect.succeed(k) })

  if (map.state._tag === "Open") {
    // map.state.map is the MutableHashMap of live entries
  }
}).pipe(Effect.scoped)
```