Sitelet https://github.com/phpredis/phpredis/pull/2458
Skip to content

Update SCAN to handle very large cursor values. - #2458

Merged
michael-grunder merged 1 commit into
developfrom
large-scan-cursors
Mar 17, 2024
Merged

michael-grunder merged 1 commit into
developfrom
large-scan-cursors

Conversation

@michael-grunder

Copy link
Copy Markdown
Member

Technically Redis may return any unsigned 64 bit integer as a scan cursor. This presents a problem for PHP in that PHP's integers are signed. Because of that if a scan cursor is > 2^63 it will overflow and fail to work properly.

This commit updates our SCAN family of commands to deliver cursors in their string form.

public function scan(null|int|string $iterator, ...);

On initial entry into our SCAN family we convert either a NULL or empty string cursor to zero, and send the initial scan command.

As Redis replies with cursors we either represent them as a long (if they are <= ZEND_ULONG_MAX) and as a string if greater. This should mean the fix is minimally breaking as the following code will still work:

$it = NULL;
do {
    print_r($redis->scan($it));
} while ($it !== 0);

The $it !== 0 still works because the zero cursor will be represented as an integer. Only absurdly large (> 2^63) values are represented as a string.

Fixes #2454

@michael-grunder

Copy link
Copy Markdown
Member Author

Tested and works on both 32-bit and 64-bit systems.

Technically Redis may return any unsigned 64 bit integer as a scan
cursor.  This presents a problem for PHP in that PHP's integers are
signed.  Because of that if a scan cursor is > 2^63 it will overflow and
fail to work properly.

This commit updates our SCAN family of commands to deliver cursors in
their string form.

```php
public function scan(null|int|string $iterator, ...);
```

On initial entry into our SCAN family we convert either a NULL or empty
string cursor to zero, and send the initial scan command.

As Redis replies with cursors we either represent them as a long (if
they are <= ZEND_ULONG_MAX) and as a string if greater.  This should
mean the fix is minimally breaking as the following code will still
work:

```php
$it = NULL;
do {
    print_r($redis->scan($it));
} while ($it !== 0);
```

The `$it !== 0` still works because the zero cursor will be represented
as an integer.  Only absurdly large (> 2^63) values are represented as a
string.

Fixes #2454
Comment thread library.c
Comment thread redis.c
@michael-grunder
michael-grunder merged commit e52f0af into develop Mar 17, 2024
@michael-grunder
michael-grunder deleted the large-scan-cursors branch March 17, 2024 17:59
michael-grunder added a commit that referenced this pull request Mar 18, 2024
We also need to update the `RedisCluster` logic to handle very large
curosr values, in addition to handling them for the `Redis` and
`RedisArray` classes.

See #2454, #2458
michael-grunder added a commit that referenced this pull request Mar 18, 2024
We also need to update the `RedisCluster` logic to handle very large
curosr values, in addition to handling them for the `Redis` and
`RedisArray` classes.

See #2454, #2458
michael-grunder added a commit that referenced this pull request Mar 18, 2024
We also need to update the `RedisCluster` logic to handle very large
curosr values, in addition to handling them for the `Redis` and
`RedisArray` classes.

See #2454, #2458
@scottasmith

Copy link
Copy Markdown

A fix has been merged into Laravel for this into laravel:11.x with laravel/framework#53095

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redis scan cursor returns unsigned int, conversion to signed int causes incorrect cursor on following iteration.

3 participants