Fix SimpleStatement.is_lwt(): detect LWT from CQL query string#784
Fix SimpleStatement.is_lwt(): detect LWT from CQL query string#784mykaul wants to merge 1 commit intoscylladb:masterfrom
Conversation
This sounds like a core of the problem - it should use prepared statements. |
Imo it is working as intended. If someone needs performance (including routing optimizations like the ones for LWT) they should use prepared statements. Drivers don't parse query strings.
DDL is the main use case for non-prepared statements. If you use a statement multiple times, you should prepare it. Also, a big false positive is a request with a string that contains IF NOT EXISTS, no? |
It is, but it's a separate issue, or that's what I thought. |
This is something we need to agree with - we don't make efforts to improve non-prepared statements performance. I'm OK with that.
Yes, a known one. I can handle it, if needed. |
00b14ef to
e8b3ee6
Compare
SimpleStatement.is_lwt() always returned False, which meant LWT-aware routing (TokenAwarePolicy Paxos leader routing) and retry policies never activated for: - Raw CQL queries via session.execute(SimpleStatement(...)) - All cqlengine queries (which wrap everything in SimpleStatement) This adds regex-based LWT detection to SimpleStatement that matches: - INSERT ... IF NOT EXISTS - UPDATE/DELETE ... IF EXISTS - UPDATE/DELETE ... IF <column> <op> <value> (conditional) - UPDATE/DELETE ... IF "column" <op> <value> (quoted identifiers) - BEGIN BATCH ... IF NOT EXISTS ... APPLY BATCH DDL statements (CREATE/ALTER/DROP ... IF [NOT] EXISTS) are correctly excluded by checking that the first word is a DML verb. The result is cached after the first call. This is a best-effort heuristic; PreparedStatement continues to get the authoritative is_lwt flag from the server during PREPARE. Also updates the existing BatchStatement LWT test to use the new SimpleStatement.is_lwt() directly instead of a workaround subclass.
e8b3ee6 to
09ce230
Compare
|
v2 — Rebased on current master (
|
In my opinion we shouldn't. We will only introduce hacks making the code more complicated, and there will always be cases where the behavior differs from prepared statement. User wants performance, correct routing -> user needs prepared statement. |
Perhaps we should focus on improving cqlengine queries then. |
Summary
SimpleStatement.is_lwt()always returnsFalse(inherited fromStatementbase class, line 348). This means all LWT-aware optimizations are silently disabled for:session.execute(SimpleStatement("INSERT ... IF NOT EXISTS"))— LWT routing and retry policies never activateSimpleStatementatconnection.py:347-349, losing any LWT informationThis is particularly impactful because:
query.is_lwt()returnsTrueWriteType.CASfrom the server, but the routing optimization from Fix LWT routing: preserve Paxos leader order in TokenAwarePolicy #782 is lostSimpleStatementuser gets no LWT-specific behavior at allFix
Added regex-based LWT detection to
SimpleStatement.is_lwt()that matches CQL patterns:IF NOT EXISTSINSERT INTO t (a) VALUES (1) IF NOT EXISTSIF EXISTSUPDATE t SET a=1 WHERE k=1 IF EXISTSIF <column>UPDATE t SET a=1 WHERE k=1 IF a = 2The regex is case-insensitive and handles multiline queries, extra whitespace, and tabs. The result is cached after the first call to avoid repeated regex matching.
This is a best-effort heuristic —
PreparedStatementcontinues to use the authoritativeis_lwtflag from the server. The main known false positive is DDL queries likeCREATE TABLE IF NOT EXISTS, which is harmless since DDL doesn't use token-aware routing.Changes
cassandra/query.py: Added_LWT_PATTERNregex andSimpleStatement.is_lwt()overridetests/unit/test_query.py: AddedSimpleStatementIsLwtTestclass with 20 tests; updated existingBatchStatementtest to use the new detection instead of a workaround subclassTests
20 new tests covering:
All 26 tests in
tests/unit/test_query.pypass. All 82 tests intests/unit/test_policies.pypass.Related