Commit 3ebd8d3
committed
[#2275] Lock-free MemoryUsage accounting via LongAdder
Replace the exclusive-lock byte counter in MemoryUsage with a striped
java.util.concurrent.atomic.LongAdder and make percentUsage volatile.
increaseUsage/decreaseUsage become lock-free adds; the existing
usageLock/setPercentUsage path (listener events, waitForSpace
signalling) is entered only when the rounded percentUsage actually
changes - at most ~100/percentUsageMinDelta locked updates per limit
traversal instead of one exclusive write lock (plus the parent chain's)
per message. isFull() becomes a volatile read. waitForSpace and all
public APIs are unchanged.
Rationale: profiling the previous commit's benchmark showed ~86% of
lock wait time in this class - Topic.send -> isFull (read lock),
Message.incrementReferenceCount -> increaseUsage (write lock) and
decrementReferenceCount -> decreaseUsage (write lock), each recursing
into the broker-global SystemUsage parent. That made usage accounting a
broker-wide serialization point that destination sharding cannot avoid.
The multi-producer degradation curve is eliminated; the shared-topic
and uncontended single-producer cases also improve (~+22% / ~+11%).
Allocation is unchanged (JMH gc profiler: ~480 vs ~527 B/op, GC count
~0 in both), so the gain is purely lock behaviour.
Semantics validation (all pass against this change):
ProducerFlowControlTest (7), ProducerFlowControlSendFailTest (8),
TopicProducerFlowControlTest (3), CompositeMessageCursorUsageTest (1),
QueueMemoryAndStoreUsageCleanupTest (1).
Known trade-off: between an add and the locked percent update there is
a sub-percentUsageMinDelta staleness window; rapid crossings may
coalesce listener events (pairs stay consistent and the locked update
recomputes from the live sum, so it self-corrects). StoreUsage and
TempUsage still use the base-class locked path and can be converted the
same way as a follow-up.
Fix setUsage() concurrent safety - delta set instead of LongAdder.reset()
LongAdder.reset() is documented as safe only when there are no
concurrent updates: a racing increaseUsage/decreaseUsage could be lost
outright (or, in narrow cell-sweep interleavings, applied out of
program order), permanently corrupting the accounting. Implement
setUsage(value) as a single delta add instead:
usage.add(value - usage.sum());
Every interleaving with a concurrent update now maps to a legal
linearization (the update is preserved, as if ordered after the set).
Also rename the parameter to avoid shadowing the field, and document
that setUsage - like the historical plain field assignment - does not
propagate an adjustment to the parent usage.
Tests: adds a sequential setUsage semantics test and a 200-round
concurrent drift-bound hammer (16 threads of balanced increase/decrease
pairs racing a mid-flight setUsage; final usage must stay within one
in-flight op per thread of the set value). Honest note: the hammer did
NOT empirically catch the reset() implementation in 200 rounds - a
same-thread increase/decrease pair usually lands in the same adder
cell, so both are wiped or both survive and the loss window is
vanishingly narrow. The fix is justified by LongAdder's documented
contract; the test remains as a regression guard on the delta
implementation's bound.
Validation: MemoryUsageConcurrencyTest (3) and MemoryUsageTest (5)
pass; ProducerFlowControlTest, ProducerFlowControlSendFailTest and
TopicProducerFlowControlTest (18 total) pass against the patched
client. setUsage has no production callers (test/config only), so this
is hardening, not a behavior change for the broker runtime.
Harden lock-free usage accounting - liveness soak, invariant docs
Untimed waitForSpace() has no polling fallback: it blocks on
waitForSpaceCondition and depends entirely on a lasting full ->
not-full transition reaching the locked setPercentUsage() path. With
lock-free accounting that liveness rests on an ordering invariant
(every usage.add() is unconditionally followed by maybeUpdatePercent(),
so the temporally last mutation of a lasting drop recomputes from the
complete sum and signals). This commit:
- documents the invariant loudly at both mutation sites in MemoryUsage
so a future edit cannot silently break it, and
- adds testUntimedWaitForSpaceLivenessSoak: 150 rounds parking untimed
waiters at exactly 100% usage, racing balanced increase/decrease
churn (usage never drops below full), then issuing the lasting
one-byte drop WHILE churn runs; after churn quiesces every waiter
must be released. Passes consistently.
GC evidence for the review record (send_08_threads, distinctTopics,
direct dispatch, JDK 25, -prof gc): per-op allocation is unchanged by
the LongAdder change - 467.5 +/- 36.4 B/op stock vs 440.0 +/- 0.2 B/op
patched. Absolute alloc rate rises only in proportion to throughput
(1.34M -> 5.69M ops/s in the profiled runs); gc.time was 17ms vs 19ms
over the measurement window. The change adds no per-operation
allocation and no meaningful GC pressure.1 parent 6f45655 commit 3ebd8d3
3 files changed
Lines changed: 226 additions & 26 deletions
File tree
- activemq-client/src
- main/java/org/apache/activemq/usage
- test/java/org/apache/activemq/usage
Lines changed: 51 additions & 25 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
31 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
32 | 37 | | |
33 | 38 | | |
34 | 39 | | |
| |||
129 | 134 | | |
130 | 135 | | |
131 | 136 | | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
| 137 | + | |
| 138 | + | |
138 | 139 | | |
139 | 140 | | |
140 | 141 | | |
| |||
159 | 160 | | |
160 | 161 | | |
161 | 162 | | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
169 | 171 | | |
170 | 172 | | |
171 | 173 | | |
| |||
182 | 184 | | |
183 | 185 | | |
184 | 186 | | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
192 | 191 | | |
193 | 192 | | |
194 | 193 | | |
195 | 194 | | |
196 | 195 | | |
197 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
198 | 215 | | |
199 | 216 | | |
200 | | - | |
| 217 | + | |
201 | 218 | | |
202 | 219 | | |
203 | 220 | | |
204 | 221 | | |
205 | | - | |
| 222 | + | |
206 | 223 | | |
207 | 224 | | |
208 | | - | |
209 | | - | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
210 | 236 | | |
211 | 237 | | |
212 | 238 | | |
| |||
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
46 | 48 | | |
47 | 49 | | |
48 | 50 | | |
| |||
Lines changed: 172 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
40 | 212 | | |
41 | 213 | | |
42 | 214 | | |
| |||
0 commit comments