Skip to content

Commit a045b84

Browse files
committed
updating docs
Assisted-by: Claude Code
1 parent 88e4eab commit a045b84

1 file changed

Lines changed: 76 additions & 9 deletions

File tree

docs/session.md

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -455,14 +455,18 @@ happens to start with the prefix text must not address the same record as anothe
455455

456456
The available options for Redis are:
457457

458-
| Name | Description |
459-
|--------------|---------------------------------------|
460-
| `host` | the host |
461-
| `port` | the port |
462-
| `index` | the index |
463-
| `persistent` | whether to persist connections or not |
464-
| `auth` | authentication parameters |
465-
| `socket` | socket connection |
458+
| Name | Description |
459+
|------------------|------------------------------------------------------------------|
460+
| `host` | the host |
461+
| `port` | the port |
462+
| `index` | the index |
463+
| `persistent` | whether to persist connections or not |
464+
| `auth` | authentication parameters |
465+
| `socket` | socket connection |
466+
| `lockingEnabled` | enable session locking (see [Session Locking](#session-locking)) |
467+
| `lockExpiry` | lifetime of the session lock in seconds |
468+
| `lockRetries` | maximum number of lock acquisition attempts |
469+
| `lockWaitTime` | microseconds to pause between lock acquisition attempts |
466470

467471
```php
468472
<?php
@@ -616,6 +620,69 @@ without touching the storage leaves the last-modified time under the control of
616620
`Stream` adapter's `gc()` removes session files whose modification time is older than the configured lifetime; an
617621
overridden `updateTimestamp()` that does not touch the file must account for that.
618622

623+
### Session Locking
624+
625+
As of 5.14.2 the [Phalcon\Session\Adapter\Redis][session-adapter-redis] adapter can serialize concurrent requests
626+
that share a session id. Without locking, two requests arriving with the same session cookie both read the session
627+
data when they start and both write it back when they finish; the request finishing last overwrites whatever the
628+
other one wrote. PHP's `files` handler prevents this by locking the session file for the duration of the request;
629+
a storage backend such as Redis offers no equivalent protection by default.
630+
631+
- `Redis` supports locking through the constructor options listed below
632+
- `Libmemcached`, `Stream` and `Noop` do not implement locking
633+
- The session locking offered by the phpredis extension (`redis.session.locking_enabled`) applies only to its own handler (`session.save_handler = redis`) and has no effect on this adapter
634+
635+
Locking is disabled by default and is enabled with the `lockingEnabled` constructor option. When enabled, `read()`
636+
acquires a per-session lock before returning the session data, and `close()` or `destroy()` releases it, so the
637+
lock is held for the lifetime of the request. A second request for the same session id waits at `read()` until the
638+
lock is released or its retries run out. The available options are:
639+
640+
| Name | Default | Description |
641+
|------------------|---------|----------------------------------------------------|
642+
| `lockingEnabled` | `false` | enable session locking |
643+
| `lockExpiry` | `30` | lifetime of the lock in seconds |
644+
| `lockRetries` | `100` | maximum number of acquisition attempts |
645+
| `lockWaitTime` | `50000` | microseconds to pause between acquisition attempts |
646+
647+
```php
648+
<?php
649+
650+
use Phalcon\Session\Adapter\Redis;
651+
use Phalcon\Session\Manager;
652+
use Phalcon\Storage\AdapterFactory;
653+
use Phalcon\Storage\SerializerFactory;
654+
655+
$options = [
656+
'host' => '127.0.0.1',
657+
'port' => 6379,
658+
'index' => '1',
659+
'lockingEnabled' => true,
660+
'lockExpiry' => 60,
661+
];
662+
663+
$session = new Manager();
664+
$serializerFactory = new SerializerFactory();
665+
$factory = new AdapterFactory($serializerFactory);
666+
$redis = new Redis($factory, $options);
667+
668+
$session
669+
->setAdapter($redis)
670+
->start();
671+
```
672+
673+
The lock is a Redis key derived from the session id - `sess-reds-<id>-lock` with the default prefix - created with
674+
`SET NX EX`. The key carries a lifetime of `lockExpiry` seconds, so a process that dies without releasing the lock
675+
cannot block the session past that point. Set `lockExpiry` higher than the longest expected request time: when the
676+
lock expires while its request is still running, another request can acquire it and the protection lapses for that
677+
overlap.
678+
679+
Each acquisition stores a unique token in the lock key and the release deletes the key only when the token still
680+
matches. An adapter whose lock has already expired therefore cannot remove a lock acquired by another request in
681+
the meantime.
682+
683+
When the lock cannot be acquired after `lockRetries` attempts - five seconds in total with the default values -
684+
`read()` throws `Phalcon\Session\Adapter\Exceptions\AdapterRuntimeError` and the session does not start.
685+
619686
## Bag
620687

621688
[Phalcon\Session\Bag][session-bag] is a component that helps to separate session data into `namespaces`. This way you
@@ -813,7 +880,7 @@ failure mode. Existing `catch (Phalcon\Session\Exception $e)` blocks continue to
813880
| `Phalcon\Session\Exceptions\InvalidSessionName` | `Phalcon\Session\Exception` | The session name passed to `setName()` is not a valid identifier. |
814881
| `Phalcon\Session\Exceptions\SessionAlreadyStarted` | `Phalcon\Session\Exception` | `start()` is called while a session is already active. |
815882
| `Phalcon\Session\Exceptions\SessionModificationDenied` | `Phalcon\Session\Exception` | A write is attempted on a session whose state does not allow modification. |
816-
| `Phalcon\Session\Adapter\Exceptions\AdapterRuntimeError` | `Phalcon\Session\Exception` | The underlying session adapter raises an unrecoverable I/O error. |
883+
| `Phalcon\Session\Adapter\Exceptions\AdapterRuntimeError` | `Phalcon\Session\Exception` | The underlying session adapter raises an unrecoverable I/O error, or the session lock cannot be acquired (see [Session Locking](#session-locking)). |
817884
| `Phalcon\Session\Adapter\Exceptions\InvalidSavePath` | `Phalcon\Session\Exception` | The configured save path is not a writable directory. |
818885
| `Phalcon\Session\Adapter\Exceptions\SavePathUnavailable` | `Phalcon\Session\Exception` | The save path cannot be determined for the configured adapter. |
819886

0 commit comments

Comments
 (0)