Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/run/sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ With HTTP, PHP often serves each request in a different process (e.g. PHP-FPM wo
doesn't find them. Use a store that all processes share, like `FileSessionStore` on a shared
directory or `Psr16SessionStore` with a shared cache such as Redis.

`FileSessionStore` suits a single host. Once requests are spread over several hosts, or sessions number in the
tens of thousands, use `Psr16SessionStore` with Redis or a similar shared cache: it is shared across hosts,
expires sessions on its own and needs no garbage collection (see below). A network-mounted directory adds latency and
makes session availability depend on the shared filesystem.

## Garbage Collection Configuration

The SDK periodically runs garbage collection to clean up expired sessions, similar to PHP's native
Expand Down Expand Up @@ -81,6 +86,36 @@ $server = Server::builder()
->build();
```

**Running GC Out of Band:**

GC runs inside a request, before the server handles the message. `FileSessionStore::gc()` scans every file in
the session directory, so its cost grows with the number of sessions. On a busy server, a random request then
waits for that scan.

In that case, disable GC in the server and call `gc()` from a cron job or a console command. Use the same
directory and TTL as the server:

```php
// bin/session-gc.php, e.g. run every 10 minutes from cron
use Mcp\Server\Session\FileSessionStore;

require __DIR__ . '/../vendor/autoload.php';

$store = new FileSessionStore(__DIR__ . '/../sessions', ttl: 3600);
$deleted = $store->gc(); // returns the IDs of the deleted sessions

echo count($deleted) . " expired sessions deleted\n";
```

```php
$server = Server::builder()
->setSession(new FileSessionStore(__DIR__ . '/sessions', ttl: 3600), gcProbability: 0)
->build();
```

`Psr16SessionStore` doesn't scan anything: it stores each session with the TTL, and its `gc()` does nothing.
With a cache like Redis, the cache expires the keys itself.

**Parameters:**

- `$gcProbability` (int): The numerator of the GC probability fraction (default: `1`). Set to `0` to disable GC.
Expand Down
1 change: 1 addition & 0 deletions src/Server/Session/FileSessionStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/**
* File-based session store.
* Stores each session as a file named by the RFC4122 UUID, with the payload.
* gc() scans the whole directory, so its cost grows with the number of sessions.
*/
class FileSessionStore implements SessionStoreInterface
{
Expand Down
Loading