Skip to content

[grid] Add Distributor Redis-backed implementation as built-in support#17396

Merged
VietND96 merged 3 commits into
trunkfrom
distributor-redis-backed
Apr 27, 2026
Merged

[grid] Add Distributor Redis-backed implementation as built-in support#17396
VietND96 merged 3 commits into
trunkfrom
distributor-redis-backed

Conversation

@VietND96

Copy link
Copy Markdown
Member

🔗 Related Issues

💥 What does this PR do?

Redis-backed Distributor implementation for Selenium Grid, enabling stateless horizontal scaling with multiple Distributor replicas sharing state through Redis

Three new classes implement the full Distributor / GridModel / NodeRegistry contract against Redis:

  • RedisBackedGridModel: all grid state (node blobs, availability sets, slot session keys, health fail counts, last-touch timestamps) lives in Redis
  • RedisBackedNodeRegistry — per-node health check runs are leader-elected via a per-node Redis SET NX PX lock, so exactly one replica performs the HTTP /status probe per interval regardless of replica count. On startup, reconstructs RemoteNode proxies from Redis availability sets, so new replicas immediately know about all live nodes.
  • RedisBackedDistributor — extends Distributor, publishes a per-instance heartbeat key every 10 s with a 30 s TTL, and writes a draining marker on close() for graceful shutdown. isReady() checks both the Redis connection and the event bus. Includes a static create(Config) factory that reads distributor.backend-url from config for use with --distributor-implementation.

🔧 Implementation Notes

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s):
    • What was generated:
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

🔄 Types of changes

  • Cleanup (formatting, renaming)
  • Bug fix (backwards compatible)
  • New feature (non-breaking change which adds functionality and tests!)
  • Breaking change (fix or feature that would cause existing functionality to change)

Signed-off-by: Viet Nguyen Duc <nguyenducviet4496@gmail.com>
@selenium-ci selenium-ci added B-grid Everything grid and server related C-java Java Bindings B-build Includes scripting, bazel and CI integrations labels Apr 26, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add Redis-backed Distributor implementation for stateless Grid scaling

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Adds Redis-backed Distributor implementation for stateless horizontal scaling
• Implements RedisBackedGridModel, RedisBackedNodeRegistry, and RedisBackedDistributor classes
• Enables multiple Distributor replicas to share state via Redis with atomic slot reservation
• Includes distributed health-check coordination via Redis leader-election locks
• Adds comprehensive test coverage for Redis-backed components
• Updates configuration to support --distributor-backend-url parameter

Grey Divider

File Changes

1. java/src/org/openqa/selenium/grid/distributor/redis/RedisBackedDistributor.java ✨ Enhancement +727/-0

Main Redis-backed Distributor with session management

java/src/org/openqa/selenium/grid/distributor/redis/RedisBackedDistributor.java


2. java/src/org/openqa/selenium/grid/distributor/redis/RedisBackedGridModel.java ✨ Enhancement +673/-0

Redis-backed grid state storage and slot management

java/src/org/openqa/selenium/grid/distributor/redis/RedisBackedGridModel.java


3. java/src/org/openqa/selenium/grid/distributor/redis/RedisBackedNodeRegistry.java ✨ Enhancement +636/-0

Redis-backed node registry with health check coordination

java/src/org/openqa/selenium/grid/distributor/redis/RedisBackedNodeRegistry.java


View more (15)
4. java/src/org/openqa/selenium/grid/distributor/config/DistributorFlags.java ⚙️ Configuration changes +12/-0

Add distributor-backend-url configuration parameter

java/src/org/openqa/selenium/grid/distributor/config/DistributorFlags.java


5. java/src/org/openqa/selenium/grid/distributor/config/DistributorOptions.java ✨ Enhancement +13/-0

Add getBackendUri method for backend configuration

java/src/org/openqa/selenium/grid/distributor/config/DistributorOptions.java


6. java/src/org/openqa/selenium/redis/GridRedisClient.java ✨ Enhancement +52/-0

Add Redis operations for slot reservation and key scanning

java/src/org/openqa/selenium/redis/GridRedisClient.java


7. java/src/org/openqa/selenium/grid/distributor/httpd/DistributorServer.java ✨ Enhancement +7/-1

Update readiness check response to JSON format

java/src/org/openqa/selenium/grid/distributor/httpd/DistributorServer.java


8. java/src/org/openqa/selenium/grid/commands/InfoCommand.java 📝 Documentation +5/-0

Add distributor help topic to info command

java/src/org/openqa/selenium/grid/commands/InfoCommand.java


9. java/src/org/openqa/selenium/grid/commands/distributor.txt 📝 Documentation +105/-0

Documentation for Distributor backends and configuration

java/src/org/openqa/selenium/grid/commands/distributor.txt


10. java/src/org/openqa/selenium/grid/commands/info.txt 📝 Documentation +1/-0

Update info command to reference distributor topic

java/src/org/openqa/selenium/grid/commands/info.txt


11. java/test/org/openqa/selenium/grid/distributor/redis/RedisBackedDistributorTest.java 🧪 Tests +246/-0

Test suite for Redis-backed Distributor functionality

java/test/org/openqa/selenium/grid/distributor/redis/RedisBackedDistributorTest.java


12. java/test/org/openqa/selenium/grid/distributor/redis/RedisBackedGridModelTest.java 🧪 Tests +416/-0

Test suite for Redis-backed grid model operations

java/test/org/openqa/selenium/grid/distributor/redis/RedisBackedGridModelTest.java


13. java/test/org/openqa/selenium/grid/distributor/redis/RedisBackedNodeRegistryTest.java 🧪 Tests +169/-0

Test suite for Redis-backed node registry

java/test/org/openqa/selenium/grid/distributor/redis/RedisBackedNodeRegistryTest.java


14. java/src/org/openqa/selenium/grid/distributor/redis/BUILD.bazel Build +36/-0

Build configuration for Redis distributor module

java/src/org/openqa/selenium/grid/distributor/redis/BUILD.bazel


15. java/test/org/openqa/selenium/grid/distributor/redis/BUILD.bazel Build +42/-0

Build configuration for Redis distributor tests

java/test/org/openqa/selenium/grid/distributor/redis/BUILD.bazel


16. java/src/org/openqa/selenium/grid/distributor/httpd/BUILD.bazel Build +2/-0

Add Redis distributor dependency to build

java/src/org/openqa/selenium/grid/distributor/httpd/BUILD.bazel


17. java/src/org/openqa/selenium/grid/BUILD.bazel Build +1/-0

Add Redis distributor to Grid runtime dependencies

java/src/org/openqa/selenium/grid/BUILD.bazel


18. java/src/org/openqa/selenium/events/zeromq/BUILD.bazel Build +1/-0

Update visibility for Redis distributor tests

java/src/org/openqa/selenium/events/zeromq/BUILD.bazel


Grey Divider

Qodo Logo

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

Grey Divider

Looking for bugs?

Check back in a few minutes. An AI review agent is analyzing this pull request.

Grey Divider

Qodo Logo

@VietND96 VietND96 changed the title [grid] Add Distributor Redis-backed as default implementation [grid] Add Distributor Redis-backed implementation as built-in support Apr 26, 2026
Signed-off-by: Viet Nguyen Duc <nguyenducviet4496@gmail.com>
@VietND96 VietND96 force-pushed the distributor-redis-backed branch from 5d79014 to 461b29b Compare April 26, 2026 17:54
@VietND96

Copy link
Copy Markdown
Member Author

/review

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

1234 - Partially compliant

Compliant requirements:

Non-compliant requirements:

  • Fix regression where Selenium 2.48 does not trigger JavaScript in a link href when invoking click()
  • Ensure behavior is correct specifically with Firefox 42 on affected platforms
  • Provide/maintain a reproducible test case demonstrating the fix

Requires further human verification:

5678 - Partially compliant

Compliant requirements:

Non-compliant requirements:

  • Investigate and fix ChromeDriver multi-instantiation errors showing ConnectFailure (Connection refused)
  • Ensure subsequent ChromeDriver instantiations do not log connection-refused errors
  • Provide guidance or a code change that prevents/reduces the observed connection failures

Requires further human verification:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Thread Leak

A dedicated scheduled executor is created for the Redis distributor heartbeat but is not stored or shut down in close(). This can leak threads and keep Redis writes running after shutdown; consider holding a reference and shutting it down alongside the other services.

  // Publish this replica's liveness heartbeat to Redis.
  Executors.newSingleThreadScheduledExecutor(
          r -> {
            Thread t = new Thread(r);
            t.setName("Redis Distributor - Heartbeat");
            t.setDaemon(true);
            return t;
          })
      .scheduleAtFixedRate(
          GuardedRunnable.guard(this::publishHeartbeat), 0, 10, TimeUnit.SECONDS);

  new JMXHelper().register(this);
}
Content-Type

The readiness endpoint sets Content-Type to plain text while returning JSON content. This mismatch may break clients expecting correct content negotiation; consider switching to a JSON media type (or returning plain text consistently).

  return new HttpResponse()
      .setStatus(ready ? HTTP_OK : HTTP_UNAVAILABLE)
      .setHeader("Content-Type", MediaType.PLAIN_TEXT_UTF_8.toString())
      .setContent(
          Contents.asJson(
              Map.of(
                  "ready",
                  ready,
                  "message",
                  ready ? "Distributor is ready" : "Distributor is not ready")));
};
Redis Scan Cost

The new scanKeys helper iterates the entire keyspace (even if incrementally) and is used for node cleanup. In large deployments this can still be expensive; consider constraining key patterns further, adding backoff/limits, or using Redis data structures to avoid scans for operational paths.

/** Scans keys matching the pattern using SCAN cursor iteration (non-blocking). */
public List<String> scanKeys(String pattern) {
  List<String> result = new ArrayList<>();
  ScanCursor cursor = ScanCursor.INITIAL;
  ScanArgs args = ScanArgs.Builder.matches(pattern).limit(100);
  do {
    KeyScanCursor<String> scanResult = connection.sync().scan(cursor, args);
    result.addAll(scanResult.getKeys());
    cursor = scanResult;
  } while (!cursor.isFinished());
  return result;
}

Signed-off-by: Viet Nguyen Duc <nguyenducviet4496@gmail.com>
@VietND96 VietND96 merged commit 8cc26f7 into trunk Apr 27, 2026
89 of 91 checks passed
@VietND96 VietND96 deleted the distributor-redis-backed branch April 27, 2026 04:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-grid Everything grid and server related C-java Java Bindings Review effort 4/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants