Skip to content

Update the credis library to support Redis Sentinel 6 & 7 and add username+pwd support to the plugin. - #325

Open
bdeclerc wants to merge 7 commits into
matomo-org:6.x-devfrom
bdeclerc:5.x-dev-BD
Open

bdeclerc wants to merge 7 commits into
matomo-org:6.x-devfrom
bdeclerc:5.x-dev-BD

Conversation

@bdeclerc

@bdeclerc bdeclerc commented Jul 15, 2026 •

Copy link
Copy Markdown

Description

The credis library code used in the current version is 6 years old and doesn't support newer Redis releases very well, beyond that there is no support for authenticating on Redis with username and password - I added this support specifically for Sentinel as that's our use case

  • I have understood, reviewed, and tested all AI outputs before use
  • All AI instructions respect security, IP, and privacy rules

Issue No

 - Partially fixes #292 
 - Resolves #324

Steps to Replicate the Issue

Try to use the plugin with a Redis Sentinel 6 or 7 with username+pwd authentication

@AltamashShaikh

Copy link
Copy Markdown
Contributor

@bdeclerc Is this ready for review ?

@bdeclerc

Copy link
Copy Markdown
Author

It should be ready - we tested it on our environment vs Redis Sentinel 7.2 and it worked - didn't test any other configurations though.

@AltamashShaikh
AltamashShaikh requested a review from a team July 17, 2026 05:28

@AltamashShaikh AltamashShaikh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdeclerc Thanks for the PR, some review comments, please let us know, if something is unclear or you have no idea on how to resolve the issueu.

1. 🔴 Redis::auth() called with 2 args — crashes every password-auth connection

  Queue/Backend/Redis.php:267
  $success = $this->redis->auth($this->password, $this->username);
  $this->redis is native phpredis (new \Redis()), whose auth() takes exactly one argument. I confirmed this live on this machine (PHP 8.3.30 / phpredis 6.3.0):
  Redis::auth() expects exactly 1 argument, 2 given  (ArgumentCountError)
  Because the second arg is always passed (username is null when unset), this throws for every existing user who uses a Redis password, not just ACL users — a hard regression on the
  plain redis backend. It also never actually transmits the ACL username. This path was clearly never exercised (the PR was tested against Sentinel, which uses the separate
  Credis_Client path). Fix:
  if ($success && !empty($this->password)) {
      $success = !empty($this->username)
          ? $this->redis->auth([$this->username, $this->password])
          : $this->redis->auth($this->password);
  }

  2. 🟠 RedisCluster backend silently drops the username

  Queue/Factory.php:87 calls setConfig($host, $port, $timeout, $password, $username) for all Redis backends, but Queue/Backend/RedisCluster.php:318 setConfig() still takes only 4
  params, and new \RedisCluster(...) (line 311) never receives a username. PHP silently ignores the extra userland arg (no crash), so a username configured in the UI has no effect and
  no error on the cluster backend — silent misconfiguration. Either add username support there or explicitly reject username + cluster.

  3. 🟡 New setting bypasses translations (project convention)

  SystemSettings.php:246,251 hardcodes English:
  $field->title = 'Redis Username';
  $field->inlineHelp = 'Username for Redis ACL authentication. Leave empty if not used.';
  Every sibling setting uses Piwik::translate('QueuedTracking_...') with keys in lang/en.json (e.g. createRedisPasswordSetting). Add
  QueuedTracking_RedisUsernameFieldTitle/...FieldHelp keys and use Piwik::translate. Minor: siblings also append . '</br>' to inlineHelp; this one doesn't.

  4. 🟡 No test coverage for the new path

  tests/Integration/Queue/Backend/RedisTest.php — nothing added for username, and the existing setConfig(...) calls still pass 4 args. A single ACL-auth integration test would have
  caught finding #1. Please add one.

@bdeclerc

Copy link
Copy Markdown
Author

I will take the feedback and work on integrating the necessary fixes - I'll come back when it is done.

@AltamashShaikh

Copy link
Copy Markdown
Contributor

@bdeclerc alot has happened and the default branch is now 6.x-dev, can you update the base branch ?

@AltamashShaikh
AltamashShaikh requested a review from a team September 25, 2026 08:37
@bdeclerc

Copy link
Copy Markdown
Author

But we'll have no way of testing it on our end then - we're running Matomo5 and considering the scale of our installation, an upgrade to M6 will be quite some ways away.

We'll see how easy it is to align with the M6 branch, but for our needs we'd have to backport it to M5 also...

@bdeclerc
bdeclerc marked this pull request as draft September 25, 2026 09:08
@bdeclerc
bdeclerc changed the base branch from 5.x-dev to 6.x-dev September 25, 2026 09:09
@bdeclerc
bdeclerc marked this pull request as ready for review September 25, 2026 09:10
@bdeclerc

Copy link
Copy Markdown
Author

I've cleaned up the description and verified and included the necessary elements for the "ai-checklist" check failure.

But the UI failure is a harder one, as it mentions that there's no functional failure, but a big difference between the before/after screenshots without me having access to those screenshots.

@snake14 snake14 left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this @bdeclerc, and for bringing the bundled credis up to date.

The changes look good and work well. I requested a couple changes and one optional one via inline comments.

No need to worry about the failing UI screenshot tests as we can update those after this PR is merged.

$hostsPorts = array_map(fn($host, $port): string => "$host:$port", $hosts, $ports);

try {
$this->redis = new \RedisCluster(null, $hostsPorts, $this->timeout, $this->timeout, true, $this->password);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please adjust this so that the username is actually used for RedisCluster?

Suggested change
$auth = $this->username ? [$this->username, $this->password] : $this->password;
$this->redis = new \RedisCluster(null, $hostsPorts, $this->timeout, $this->timeout, true, $auth);

Comment thread Queue/Backend/Redis.php

if ($success && !empty($this->password)) {
$success = $this->redis->auth($this->password);
$success = $this->redis->auth([$this->username, $this->password]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to keep older installs working as before, can we please only pass the username when it's set?

Suggested change
$success = $this->redis->auth([$this->username, $this->password]);
$auth = $this->username ? [$this->username, $this->password] : $this->password;
$success = $this->redis->auth($auth);

$configuredClient->forceStandalone();
$configuredClient->connect();
if ($this->usePasswordForSentinelInstances && !empty($this->password)) {
$configuredClient->auth($this->password);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: Include the username along with the password for individual hosts.

Suggested change
$configuredClient->auth($this->password, $this->username);

@AltamashShaikh

Copy link
Copy Markdown
Contributor

But we'll have no way of testing it on our end then - we're running Matomo5 and considering the scale of our installation, an upgrade to M6 will be quite some ways away.

We'll see how easy it is to align with the M6 branch, but for our needs we'd have to backport it to M5 also...

cc: @snake14 @lachiebol What do you guys think on backporting this feature ?

@AltamashShaikh AltamashShaikh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdeclerc Can you check this ? UI test we can takecare of itlater.

Blocking

1. The UI tests fail. The new "Redis Username" field changes the settings page, and the saved screenshots weren't updated. All 3 specs in tests/UI/QueuedTrackingSettings_spec.js fail with about 231k differing pixels (settings_page, settings_save_error, settings_page_sentinel). In the comments the author says they can't see the screenshots, so a maintainer probably needs to pull the CI artifacts and commit the new files (in Git LFS). The AI checklist check also still shows as failing, even though the author says they fixed the description; a re-run should confirm.
2. The username is never sent when logging in to the Sentinel nodes themselves. Queue/Backend/Sentinel.php ~L43–45 still calls $configuredClient->auth($this->password). The new Credis_Client::auth($password, $username = null) accepts a username, but it isn't passed. If the Sentinel nodes use ACL users and use_password_for_sentinel_instances is enabled, that login fails and the master can't be found. The username is only used for the master connection (L55). The fix is $configuredClient->auth($this->password, $this->username ?: null).

Medium

1. Cluster users can enter a username, but it's thrown away. Queue/Backend/RedisCluster.php ~L337 stores $this->username, but ~L312 still passes only $this->password to new \RedisCluster(...). The setting has condition = 'backend=="redis"', so the field appears for Cluster too. Either pass $this->username ? [$this->username, $this->password] : $this->password, or hide the field for Cluster.
2. The standalone backend now always sends auth([$username, $password]). This is in Queue/Backend/Redis.php ~L267. With no username this becomes auth([null, $password]). CI passes with phpredis 6.x (test_checkConnectionWithCorrectPasswordShouldConnect). However, phpredis older than 5.3 doesn't accept an array here, and the bundled credis explicitly guards for that version. Safer: $this->redis->auth(!empty($this->username) ? [$this->username, $this->password] : $this->password). That keeps today's behaviour exactly when no username is set.
3. The tests don't cover the main use case. The 4 new integration tests in tests/Integration/Queue/Backend/RedisTest.php only exercise the standalone Redis backend. Nothing tests the Sentinel username path, which is the stated goal (and Blocking #2 would have been caught), or the new redisUsername setting feeding through Factory::makeBackendFromSettings. Also, createAdminConnection() hard-codes 127.0.0.1:6379, while the rest of the file takes host and port from settings.
4. The library version isn't recorded. About 1,000 lines of libs/credis/* were replaced, and nothing says which upstream credis version this is (composer.json has no version, and the commit message is just "Add updated credis library"). Reviewers can't diff it against upstream to confirm it wasn't modified. I did check that the two earlier Matomo PHP 8.2 fixes are still covered upstream: no 'self' callable strings, and the Credis_Sentinel properties are declared. The author should name the tag in the PR or a commit message.

Low / Polish

1. lang/en.json: "Redis Username" should be "Redis username", to match "Redis password" and "Redis database".
2. SystemSettings.php ~L96: the username setting is created between timeout and database, so it shows up away from the password field. Creating it just before redisPassword would group the login fields.
3. RedisCluster.php ~L325 adds a trailing comma in the parameter list, which Redis.php doesn't have. Harmless, but inconsistent.
4. The tests mix rawcommand and rawCommand.
5. There's no CHANGELOG.md entry for the new setting or the library bump.
6. The vendored README.markdown has trailing whitespace. Not worth changing, since it's upstream content.
7. Blocking

1. The UI tests fail. The new "Redis Username" field changes the settings page, and the saved screenshots weren't updated. All 3 specs in tests/UI/QueuedTrackingSettings_spec.js fail with about 231k differing pixels (settings_page, settings_save_error, settings_page_sentinel). In the comments the author says they can't see the screenshots, so a maintainer probably needs to pull the CI artifacts and commit the new files (in Git LFS). The AI checklist check also still shows as failing, even though the author says they fixed the description; a re-run should confirm.
2. The username is never sent when logging in to the Sentinel nodes themselves. Queue/Backend/Sentinel.php ~L43–45 still calls $configuredClient->auth($this->password). The new Credis_Client::auth($password, $username = null) accepts a username, but it isn't passed. If the Sentinel nodes use ACL users and use_password_for_sentinel_instances is enabled, that login fails and the master can't be found. The username is only used for the master connection (L55). The fix is $configuredClient->auth($this->password, $this->username ?: null).

Medium

1. Cluster users can enter a username, but it's thrown away. Queue/Backend/RedisCluster.php ~L337 stores $this->username, but ~L312 still passes only $this->password to new \RedisCluster(...). The setting has condition = 'backend=="redis"', so the field appears for Cluster too. Either pass $this->username ? [$this->username, $this->password] : $this->password, or hide the field for Cluster.
2. The standalone backend now always sends auth([$username, $password]). This is in Queue/Backend/Redis.php ~L267. With no username this becomes auth([null, $password]). CI passes with phpredis 6.x (test_checkConnectionWithCorrectPasswordShouldConnect). However, phpredis older than 5.3 doesn't accept an array here, and the bundled credis explicitly guards for that version. Safer: $this->redis->auth(!empty($this->username) ? [$this->username, $this->password] : $this->password). That keeps today's behaviour exactly when no username is set.
3. The tests don't cover the main use case. The 4 new integration tests in tests/Integration/Queue/Backend/RedisTest.php only exercise the standalone Redis backend. Nothing tests the Sentinel username path, which is the stated goal (and Blocking #2 would have been caught), or the new redisUsername setting feeding through Factory::makeBackendFromSettings. Also, createAdminConnection() hard-codes 127.0.0.1:6379, while the rest of the file takes host and port from settings.
4. The library version isn't recorded. About 1,000 lines of libs/credis/* were replaced, and nothing says which upstream credis version this is (composer.json has no version, and the commit message is just "Add updated credis library"). Reviewers can't diff it against upstream to confirm it wasn't modified. I did check that the two earlier Matomo PHP 8.2 fixes are still covered upstream: no 'self' callable strings, and the Credis_Sentinel properties are declared. The author should name the tag in the PR or a commit message.

Low / Polish

1. lang/en.json: "Redis Username" should be "Redis username", to match "Redis password" and "Redis database".
2. SystemSettings.php ~L96: the username setting is created between timeout and database, so it shows up away from the password field. Creating it just before redisPassword would group the login fields.
3. RedisCluster.php ~L325 adds a trailing comma in the parameter list, which Redis.php doesn't have. Harmless, but inconsistent.
4. The tests mix rawcommand and rawCommand.
5. There's no CHANGELOG.md entry for the new setting or the library bump.
6. The vendored README.markdown has trailing whitespace. Not worth changing, since it's upstream content

@Douglasdc3

Copy link
Copy Markdown

The credis library version was updated to v1.17.1 https://github.com/colinmollenhour/credis/releases/tag/v1.17.1

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants