> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.refineryteam.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Temporary Storage

> Non-persistent, in-memory key-value storage with optional per-entry TTL.

`xyz.refineryteam.refinerycore.api.storage` provides a non-persistent, in-memory key-value store for data that only needs to live for the current server session (or shorter, via TTLs) — combat tags, pending confirmations, session caches, and similar. Nothing in this package ever touches disk or survives a restart.

<Note>
  Need data to actually survive a restart? Use [Database](/refinerycore/database) and a `Repository` instead — that's the persistent-storage layer.
</Note>

## `TemporaryStorage<K, V>`

The core contract — a typed key-value store scoped to a single key/value type pair:

```java TemporaryStorage.java icon="java" theme={"system"}
public interface TemporaryStorage<K, V> {
    void put(K key, V value);
    void put(K key, V value, Duration ttl);
    Optional<V> get(K key);
    boolean contains(K key);
    void remove(K key);
    void clear();
    Set<K> keys();
    int size();
}
```

The `Duration ttl` overload of `put` stores a value that automatically expires — and is treated as absent — once the duration elapses.

## `InMemoryStorage` — the default implementation

A `ConcurrentHashMap`-backed implementation of `TemporaryStorage`, safe for concurrent access from multiple threads (e.g. async tasks alongside the main thread):

```java MainStorage.java icon="java" theme={"system"}
TemporaryStorage<UUID, Long> combatTags = new InMemoryStorage<>();

combatTags.put(player.getUniqueId(), System.currentTimeMillis(), Duration.ofSeconds(15));

if (combatTags.contains(player.getUniqueId())) {
    player.sendMessage("You are in combat!");
}
```

Expired entries are treated as absent **lazily** — `get`, `contains`, `keys()`, and `size()` all check expiry on access and evict the entry if it's expired, so correctness doesn't depend on any background task running.

### Proactive cleanup

If you'd rather not wait for the next access to evict expired entries (e.g. to keep `size()` accurate for a live display, or just for general memory hygiene), schedule periodic cleanup:

```java MainStorage.java icon="java" theme={"system"}
InMemoryStorage<UUID, Long> combatTags = new InMemoryStorage<>();
combatTags.startCleanupTask(this, 20L * 60); // purge every 60 seconds
```

This is purely a housekeeping convenience — `get()` already treats expired entries as absent regardless of whether this is called.

## `StorageRegistry` — named stores

Rather than declaring a `TemporaryStorage` field for every unrelated system in your plugin, `StorageRegistry` hands out named instances on demand, backed by a shared registry:

```java MainStorage.java icon="java" theme={"system"}
StorageRegistry storage = new StorageRegistry();

TemporaryStorage<UUID, Long> combatTags = storage.of("combat-tags");
TemporaryStorage<UUID, String> pendingConfirmations = storage.of("pending-confirmations");
```

Each name lazily creates its own independent `InMemoryStorage` the first time it's requested, and returns the same instance on subsequent calls with that name.

| Method            | Description                                                                                                                                                      |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `of(String name)` | Gets (creating if needed) the named `TemporaryStorage`                                                                                                           |
| `clearAll()`      | Clears every store tracked by this registry — useful for a full reset (e.g. a debug command), not typically needed on disable since none of this persists anyway |

<Warning>
  `StorageRegistry#of` is generically typed per call site (`TemporaryStorage<K, V> of(String name)`), but the underlying map only tracks one instance per name — reusing the same name with different `K`/`V` type parameters elsewhere in your plugin will produce `ClassCastException`s at runtime. Keep store names unique per logical key/value shape.
</Warning>
