Andrey Yarovoy created HDDS-16304:
-------------------------------------

             Summary: Convert RpcMetrics latency counters to lock-free 
ConcurrentMutableStat
                 Key: HDDS-16304
                 URL: https://issues.apache.org/jira/browse/HDDS-16304
             Project: Apache Ozone
          Issue Type: Improvement
            Reporter: Andrey Yarovoy
            Assignee: Andrey Yarovoy


*Problem*

Ozone forks the Hadoop IPC layer under {{org.apache.hadoop.ipc_}} 
({{{}hadoop-hdds/common{}}}). {{RpcMetrics}} records three per-call latency 
counters as Hadoop {{@Metric MutableRate}} fields:
 * {{rpcQueueTime}}
 * {{rpcLockWaitTime}}
 * {{rpcProcessingTime}}

These are updated on *every RPC* via {{addRpcQueueTime}} / 
{{addRpcLockWaitTime}} / {{{}addRpcProcessingTime{}}}, called from the IPC 
{{Server}} handler threads. {{{}MutableRate extends MutableStat{}}}, and 
{{MutableStat.add(long)}} is {{{}synchronized(this){}}}. With a large handler 
pool (e.g. OM runs ~200 handlers), all threads recording a sample serialize on 
the _same_ metric monitor — a lock convoy on the RPC serving path. This is the 
same contention class already addressed for OM metrics in HDDS-9377 
({{{}ConcurrentMutableStat{}}}) and for {{OMPerformanceMetrics}} on the current 
branch, but on the shared IPC server path that fronts *OM, SCM, and datanode* 
RPC servers.

*Approach*

Reuse the existing lock-free {{ConcurrentMutableStat}} (striped 
{{{}LongAdder{}}}/{{{}LongAccumulator{}}}, drained lazily at snapshot) 
introduced in HDDS-9377, via the {{ConcurrentMutableRate}} convenience subclass 
(public ctor, {{sampleName="Ops"}} / {{{}valueName="Time"{}}}).

Because {{@Metric}} fields are instantiated reflectively by 
{{MutableMetricsFactory}} (which always creates a real {{{}MutableRate{}}}), 
the field type cannot simply be swapped. {{RpcMetrics}} must be reworked into a 
hand-rolled {{MetricsSource}} (same template as {{OMPerformanceMetrics}} / 
{{{}OMLockMetrics{}}}): construct the three rates in the constructor as 
{{{}ConcurrentMutableRate{}}}, keep the {{{}MetricsRegistry{}}}, and implement 
{{getMetrics()}} to snapshot the three rates plus the existing 
{{MutableCounterLong}} counters, the {{@Metric}} gauge methods 
({{{}numOpenConnections{}}}, {{{}callQueueLength{}}}, 
{{{}numDroppedConnections{}}}, {{{}numOpenConnectionsPerUser{}}}), and the 
conditional {{MutableQuantiles[]}} arrays. The lock-free counters must keep the 
*same emitted metric names* 
({{{}RpcQueueTimeNumOps{}}}/{{{}RpcQueueTimeAvgTime{}}}, etc.) — guaranteed by 
construction (capitalized name + {{{}extended=false{}}}).

{{Server.java:463-468}} consumes {{getProcessingMean()}} / 
{{getProcessingStdDev()}} / {{{}getProcessingSampleCount(){}}}, which delegate 
to {{{}rpcProcessingTime.lastStat(){}}}. {{ConcurrentMutableStat}} overrides 
{{lastStat()}} to drain pending samples first, so these callers keep working; 
the getter return type changes from {{MutableRate}} to 
{{{}ConcurrentMutableStat{}}}/{{{}MutableStat{}}}.

*Scope / non-goals*
 * *In scope:* the 3 {{MutableRate}} fields in {{RpcMetrics}} only.
 * *Out of scope:* {{{}RpcDetailedMetrics{}}}. It uses 
{{{}MutableRatesWithAggregation{}}}, *not* a plain {{{}MutableRate{}}}. That 
class already accumulates per-thread and aggregates at snapshot (HADOOP-24420) 
specifically to avoid this lock, so it is not a {{ConcurrentMutableStat}} 
drop-in and needs no change here. Left as a separate follow-up only if 
profiling later shows its aggregation step is itself hot.
 * The {{MutableCounterLong}} counters ({{{}AtomicLong{}}}-backed) and 
{{MutableQuantiles}} are not part of this change.

*Blast radius / compatibility*

Shared {{hadoop-hdds/common}} {{ipc_}} fork → touches OM, SCM, and datanode RPC 
servers. No wire/RPC protocol change. All emitted metric names ({{{}rpc{}}} 
context) are preserved byte-identically, so dashboards/alerts are unaffected. 
Std-dev is slightly underestimated under concurrent batched adds (documented 
{{ConcurrentMutableStat}} caveat) — acceptable for these latency stats and for 
the {{Server}} three-sigma slow-call heuristic.

*Testing*
 * Build {{hadoop-hdds/common}} + dependents (OM/SCM/datanode) to confirm the 
{{{}getRpcProcessingTime{}}}/{{{}lastStat(){}}} callers in {{Server.java}} 
compile against the new getter type.
 * Unit test asserting the three metric names still appear with correct 
{{{}NumOps{}}}/{{{}AvgTime{}}} after {{add()}} + snapshot.
 * Sanity-check {{{}Server{}}}'s slow-call detection path still reads a sane 
mean/stddev/sample-count after conversion.



--
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