Francisco Guerrero created CASSANDRA-21569:
----------------------------------------------

             Summary: Revert ServerConnection.requests from ThreadLocalCounter 
to Dropwizard Counter
                 Key: CASSANDRA-21569
                 URL: https://issues.apache.org/jira/browse/CASSANDRA-21569
             Project: Apache Cassandra
          Issue Type: Bug
            Reporter: Francisco Guerrero
            Assignee: Francisco Guerrero


h2. Summary

{{ServerConnection.requests}} was changed from a Dropwizard {{Counter}} 
({{LongAdder}}) to {{ThreadLocalCounter}} in CASSANDRA-21400. I would like to 
reconsider this change: it gains a negligible write speedup while making 
{{system_views.clients}} scans and metric reads under connection churn 
dramatically more expensive. I propose we revert *only this one site*.

h2. The trade-off to consider

{{ThreadLocalCounter}}'s only advantage is a cheaper {{inc()}} (a thread-local 
array bump vs a {{LongAdder}} cell). Its cost is that {{getCount()}} takes a 
process-wide shared read lock and sums the  counter's slot across *every live 
thread*, and each counter close ({{recycleMetricId()}}) takes that lock's 
*write* side and scans every live thread. That trade only pays off for a small, 
fixed set of write-hot counters. {{ServerConnection.requests}} is the opposite:

* *Benefit is negligible here.* It is incremented once per request by the 
single Netty thread that owns the connection (no contention), so {{LongAdder}} 
stays on its base cell. Measured delta is ~1-2 ns per increment (well under 
0.1% of CQL request processing).
* *Cost is large here.* It is per-connection (high cardinality, churny) and is 
read once-per-row by {{ClientsTable}} ({{system_views.clients}} / {{nodetool 
clientstats}}), so a single query becomes an O(connections x live-threads) scan 
under the shared lock, and connection open/close churn generates write-lock 
traffic that stalls reads of unrelated counters.

h2. Evidence (JMH; single fork, JDK 11 -- read the ratios, not absolute numbers)

*Write* (the benefit) -- per-increment cost is ~1-2 ns for a single 
low-contention counter; even the worst measured case (cache-polluted, 100 
counters) is ~35 ns/inc.

*Read* (the cost) -- one {{system_views.clients}} scan, us/op:

|| Connections || Live threads || LongAdder || ThreadLocalCounter ||
| 10,000 | 64  | 9.2  | 455     |
| 10,000 | 256 | 9.6  | 6,786   |
| 50,000 | 256 | 57.3 | 120,757 |

{{LongAdder}} is flat in thread count; {{ThreadLocalCounter}} scales with 
connections x threads (~121 ms per scan at 50k connections / 256 threads).

*Churn* (the cost) -- read throughput of a stable counter while N threads close 
counters, at 256 live threads: {{0 -> 10,667 ops/ms}}, {{1 -> 1,384 (-7.7x)}}, 
{{4 -> 509 (-21x)}}.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to