Skip to content

Conversation

@nikagra
Copy link

@nikagra nikagra commented Jan 9, 2026

Addresses: #384

Summary

LWT statements previously bypassed the configured LoadBalancingPolicy and used raw replicas from the token map, which ignored DC/rack-aware policies. This PR routes LWT through LBP, introduces explicit routing hints, and adjusts replica handling to prefer local-DC replicas and avoid rack-local bias.

Motivation

In clusters using TokenAware with DC/rack-aware policies, LWT traffic was coordinated against replicas in a fixed order, sometimes in a different DC or rack than intended. This led to suboptimal coordination and unexpected cross-DC behavior.

Changes

  • Public API:
    • Add RequestRoutingType enum: REGULAR, LWT.
    • Add RequestRoutingMethod enum: REGULAR, PRESERVE_REPLICA_ORDER, TOKEN_BASED_REPLICA_SHUFFLING (hint; only PRESERVE_REPLICA_ORDER is honored in this PR).
    • Extend Request:
      • @Nullable RequestRoutingType getRequestType()
      • default RequestRoutingMethod getRoutingMethod() { return RequestRoutingMethod.REGULAR; }
  • Statements:
    • SimpleStatement, BoundStatement, BatchStatement, graph statements, and PrepareRequest now implement getRequestType() (LWT when applicable; otherwise REGULAR).
    • Refactor BatchStatementBuilder and DefaultBatchStatement: replace isLWT plumbing with RequestRoutingType. isLWT() now derives from routing type or constituent statements.
  • Request execution:
    • Remove LWT-specific replica lookup from CqlRequestHandler; routing for both LWT and non-LWT is delegated to LBP via DefaultLoadBalancingPolicy.newQueryPlan(...).
  • Load balancing (DefaultLoadBalancingPolicy):
    • Derive replicas from the request when available.
    • LWT:
      • Build the plan from replicas, preferring local-DC replicas when a local DC is configured; preserve original replica order.
      • Ignore local rack prioritization to avoid rack-local leader congestion; treat local-DC replicas uniformly.
    • Non-LWT: preserve existing behavior (local-DC live nodes; replicas bubbled to the front).
    • Honor RequestRoutingMethod.PRESERVE_REPLICA_ORDER by skipping shuffling.
    • Factor replica movement/shuffling and slow-replica avoidance into helpers; apply health-based reordering only when avoidSlowReplicas is enabled.
    • Accept @Nullable Request and @Nullable Session to align with updated call sites.
  • Nullability/test adjustments:
    • Update NodeStateIT to accept nullable params in newQueryPlan.
    • Update examples (KeyRequest) to implement getRequestType().

Tests

  • LWTLoadBalancingIT: assert coordinators are a subset of replicas (not a single fixed node).
  • LWTLoadBalancingMultiDcIT: new test verifying LWT routing prefers local-DC replicas in multi-DC setups.
  • BatchStatementIT: strengthen LWT batch coverage; validate serial consistency, RequestRoutingType, presence of routing key, and rerun behavior.
  • Minor test rule/constant cleanups.

Behavior

  • LWT requests:
    • Routed through LBP using the computed replica set.
    • Prefer local-DC replicas; do not enforce rack-local prioritization.
    • Optionally preserve replica order when getRoutingMethod() == PRESERVE_REPLICA_ORDER.
  • Non-LWT requests:
    • Unchanged; existing DC/rack-aware + token-aware behavior remains.

Backward Compatibility

  • API additions are backward-compatible (new enums and default methods).
  • Existing client code continues to work; advanced users can opt into routing hints via Request.

Notes for Reviewers

  • RequestRoutingMethod.TOKEN_BASED_REPLICA_SHUFFLING is introduced as a hint but not actively applied in DefaultLoadBalancingPolicy in this PR; scope limited to PRESERVE_REPLICA_ORDER.
  • Graph statements and PrepareRequest report REGULAR routing type by default.

@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch 4 times, most recently from c4bdac5 to 5bff3d3 Compare January 15, 2026 20:43
@nikagra nikagra changed the title Fix/lwt routing lbp filtering 384 Fix: LWT routing lbp filtering 384 Jan 19, 2026
@nikagra nikagra changed the title Fix: LWT routing lbp filtering 384 Fix: LWT routing enhancements to utilize LBP Jan 19, 2026
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch 5 times, most recently from f02d437 to 3c7f0db Compare January 20, 2026 18:26
@nikagra nikagra marked this pull request as ready for review January 20, 2026 18:26
@nikagra nikagra requested a review from dkropachev January 20, 2026 18:26
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch from 3c7f0db to 5d8ec44 Compare January 20, 2026 18:33
@nikagra nikagra requested a review from dkropachev January 20, 2026 19:00
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch 3 times, most recently from c74510a to 5f6c11d Compare January 22, 2026 08:34
@avikivity
Copy link
Member

Fix: LWT routing enhancements to utilize LBP

What's LBP?

@dkropachev
Copy link

Fix: LWT routing enhancements to utilize LBP

What's LBP?

LoadBalancingPolicy

@nikagra nikagra requested a review from dkropachev January 22, 2026 14:02
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch from 059281b to a4f26e1 Compare January 22, 2026 19:32
Copy link

@dkropachev dkropachev left a comment

Choose a reason for hiding this comment

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

Last ask

- Added RequestRoutingType and RequestRoutingMethod enums to define request routing strategies.
- Updated DefaultLoadBalancingPolicy to consider request routing type for replica selection, especially for LWT requests.
- Updated various graph statement classes (BytecodeGraphStatement, DefaultBatchGraphStatement, DefaultFluentGraphStatement, DefaultScriptGraphStatement) to implement getRequestRoutingType method.
- Modified BatchStatementBuilder to set request routing type based on LWT status.
- Enhanced DefaultBatchStatement, DefaultBoundStatement, DefaultPrepareRequest, and DefaultSimpleStatement to include routing type in constructors and methods.
- Added logic to avoid slow replicas based on health checks and in-flight requests.
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch 2 times, most recently from 6a26ea9 to 8456130 Compare January 23, 2026 14:03
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch from 8456130 to b2df37b Compare January 23, 2026 14:07
Introduce RequestRoutingType to distinguish LWT from regular queries and
add a new configuration option to control LWT routing behavior.

The new `advanced.load-balancing-policy.default-lwt-request-routing-method`
option allows choosing between:
- REGULAR: Default shuffling and slow replica avoidance
- PRESERVE_REPLICA_ORDER: Maintains replica order from partitioner

Changes:
- Add RequestRoutingType enum (REGULAR, LWT) to classify requests
- Remove unused RequestRoutingMethod enum from Request interface
- Thread RequestRoutingType through Statement builders and implementations
- Update DefaultLoadBalancingPolicy to route LWT queries according to config
- Add corresponding TypedDriverOption and OptionsMap support
- Update prepared statement creation to detect and mark LWT queries
- Remove RequestRoutingMethod.getRoutingMethod() default method

This enables optimized LWT performance by avoiding unnecessary shuffling
when replica order preservation is beneficial for linearizability.

refactor: update LWT request routing method to preserve replica order
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch 2 times, most recently from 40f7257 to d227fc4 Compare January 23, 2026 15:13
- Fix Javadoc positioning: Move @nonnull annotations after doc comments
  in DefaultLoadBalancingPolicy methods (per Java conventions)
- Add missing @nonnull annotation to StatementBuilderTest mock builder
- Add @nullable annotation to NodeStateIT query plan method signature
- Standardize test infrastructure:
  * Add @RunWith(MockitoJUnitRunner.Silent.class) to 7 test classes
  * Update LoadBalancingPolicyTestBase to stub LWT routing config option
  * Convert base class from @RunWith to abstract (subclasses now declare runner)
- Standardize integration test naming: ccmRule→CCM_RULE, sessionRule→SESSION_RULE
- Update test mocks with RequestRoutingType.REGULAR parameter for compatibility
- Improve LWT integration tests:
  * BatchStatementIT: Fix variable references, enhance LWT batch assertions
  * LWTLoadBalancingIT: Change from single-node to replica-set validation
  * Add LWTLoadBalancingMultiDcIT: New multi-DC LWT routing test coverage

No functional changes to production code—purely code quality and test improvements.

Apply suggestions from code review

Co-authored-by: Dmitry Kropachev <dmitry.kropachev@gmail.com>
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch 2 times, most recently from c3889cd to 30f3b4b Compare January 26, 2026 13:58
@nikagra nikagra requested a review from dkropachev January 26, 2026 15:20
if (request == null) {
return RequestRoutingMethod.REGULAR;
}
switch (request.getRequestRoutingType()) {

Choose a reason for hiding this comment

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

switch does not handle null, it will throw NullPointerException

private final Node node;
private final int nowInSeconds;
private final Boolean isLWT;
private final Boolean isExplicitLWTSet;

Choose a reason for hiding this comment

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

Can you please store RoutingType instead

Copy link
Author

Choose a reason for hiding this comment

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

New impl: keeping @NonNull and @Nullable for requestRoutingType, additionally moving requestRoutingType field to concrete builder classes implementing StatementBuilder abstract class since it can't fulfill both requrements.

@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch from 30f3b4b to 1baa9e8 Compare January 26, 2026 19:27
@nikagra nikagra force-pushed the fix/lwt-routing-lbp-filtering-384 branch from 1baa9e8 to a481bc2 Compare January 26, 2026 19:31
@NonNull private ImmutableList.Builder<BatchableStatement<?>> statementsBuilder;
private int statementsCount;
@Nullable private Boolean isLWT = null;
@Nullable protected RequestRoutingType requestRoutingType = null;
Copy link
Author

@nikagra nikagra Jan 26, 2026

Choose a reason for hiding this comment

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

Note @Nullable with default null value.

@NonNull private final ByteBuffer[] values;
@NonNull private final CodecRegistry codecRegistry;
@NonNull private final ProtocolVersion protocolVersion;
@NonNull protected RequestRoutingType requestRoutingType = RequestRoutingType.REGULAR;
Copy link
Author

@nikagra nikagra Jan 26, 2026

Choose a reason for hiding this comment

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

Note @NonNull with default RequestRoutingType.REGULAR value.

@Nullable protected Duration timeout;
@Nullable protected Node node;
protected int nowInSeconds = Statement.NO_NOW_IN_SECONDS;
@NonNull protected RequestRoutingType requestRoutingType = RequestRoutingType.REGULAR;
Copy link
Author

Choose a reason for hiding this comment

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

Removing from base class since @NonNull annotation can not serve every concrete implementation (nullable for batch statements)

@nikagra nikagra requested a review from dkropachev January 26, 2026 21:12
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.

3 participants